• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IMIO / Products.PloneMeeting / #1457

13 Dec 2023 03:58PM UTC coverage: 91.317% (-0.3%) from 91.586%
#1457

push

coveralls-python

gbastien
Begin implementation
See #MPMBRWP-19

76 of 133 new or added lines in 3 files covered. (57.14%)

4 existing lines in 2 files now uncovered.

17080 of 18704 relevant lines covered (91.32%)

0.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.05
/src/Products/PloneMeeting/vocabularies.py
1
# -*- coding: utf-8 -*-
2
#
3
# File: vocabularies.py
4
#
5
# GNU General Public License (GPL)
6
#
7

8
from collections import OrderedDict
1✔
9
from collective.behavior.internalnumber.browser.settings import DxPortalTypesVocabulary
1✔
10
from collective.contact.plonegroup.browser.settings import EveryOrganizationsVocabulary
1✔
11
from collective.contact.plonegroup.browser.settings import SortedSelectedOrganizationsElephantVocabulary
1✔
12
from collective.contact.plonegroup.config import get_registry_organizations
1✔
13
from collective.contact.plonegroup.utils import get_organization
1✔
14
from collective.contact.plonegroup.utils import get_organizations
1✔
15
from collective.contact.plonegroup.utils import get_own_organization
1✔
16
from collective.contact.plonegroup.utils import get_plone_group
1✔
17
from collective.contact.plonegroup.vocabularies import PositionTypesVocabulary
1✔
18
from collective.documentgenerator.content.vocabulary import ExistingPODTemplateFactory
1✔
19
from collective.documentgenerator.content.vocabulary import MergeTemplatesVocabularyFactory
1✔
20
from collective.documentgenerator.content.vocabulary import PortalTypesVocabularyFactory
1✔
21
from collective.documentgenerator.content.vocabulary import StyleTemplatesVocabularyFactory
1✔
22
from collective.eeafaceted.collectionwidget.content.dashboardcollection import IDashboardCollection
1✔
23
from collective.eeafaceted.collectionwidget.vocabulary import CachedCollectionVocabulary
1✔
24
from collective.eeafaceted.dashboard.vocabulary import DashboardCollectionsVocabulary
1✔
25
from collective.eeafaceted.z3ctable.columns import EMPTY_STRING
1✔
26
from collective.iconifiedcategory.utils import get_categorized_elements
1✔
27
from collective.iconifiedcategory.utils import get_category_object
1✔
28
from collective.iconifiedcategory.utils import get_config_root
1✔
29
from collective.iconifiedcategory.utils import get_group
1✔
30
from collective.iconifiedcategory.utils import render_filesize
1✔
31
from collective.iconifiedcategory.vocabularies import CategoryTitleVocabulary
1✔
32
from collective.iconifiedcategory.vocabularies import CategoryVocabulary
1✔
33
from collective.iconifiedcategory.vocabularies import EveryCategoryTitleVocabulary
1✔
34
from collective.iconifiedcategory.vocabularies import EveryCategoryVocabulary
1✔
35
from DateTime import DateTime
1✔
36
from eea.facetednavigation.interfaces import IFacetedNavigable
1✔
37
from imio.annex.content.annex import IAnnex
1✔
38
from imio.helpers.cache import get_cachekey_volatile
1✔
39
from imio.helpers.cache import get_plone_groups_for_user
1✔
40
from imio.helpers.content import find
1✔
41
from imio.helpers.content import get_user_fullname
1✔
42
from imio.helpers.content import get_vocab
1✔
43
from imio.helpers.content import uuidsToObjects
1✔
44
from imio.helpers.content import uuidToObject
1✔
45
from natsort import humansorted
1✔
46
from operator import attrgetter
1✔
47
from plone import api
1✔
48
from plone.app.vocabularies.security import GroupsVocabulary
1✔
49
from plone.app.vocabularies.users import UsersFactory
1✔
50
from plone.memoize import ram
1✔
51
from plone.memoize.ram import store_in_cache
1✔
52
from Products.CMFPlone.utils import base_hasattr
1✔
53
from Products.CMFPlone.utils import safe_unicode
1✔
54
from Products.PloneMeeting.browser.itemvotes import next_vote_is_linked
1✔
55
from Products.PloneMeeting.config import CONSIDERED_NOT_GIVEN_ADVICE_VALUE
1✔
56
from Products.PloneMeeting.config import HIDDEN_DURING_REDACTION_ADVICE_VALUE
1✔
57
from Products.PloneMeeting.config import ITEM_NO_PREFERRED_MEETING_VALUE
1✔
58
from Products.PloneMeeting.config import NO_COMMITTEE
1✔
59
from Products.PloneMeeting.config import NOT_GIVEN_ADVICE_VALUE
1✔
60
from Products.PloneMeeting.config import PMMessageFactory as _
1✔
61
from Products.PloneMeeting.indexes import DELAYAWARE_ROW_ID_PATTERN
1✔
62
from Products.PloneMeeting.indexes import REAL_ORG_UID_PATTERN
1✔
63
from Products.PloneMeeting.interfaces import IMeetingConfig
1✔
64
from Products.PloneMeeting.interfaces import IMeetingItem
1✔
65
from Products.PloneMeeting.utils import decodeDelayAwareId
1✔
66
from Products.PloneMeeting.utils import get_context_with_request
1✔
67
from Products.PloneMeeting.utils import get_datagridfield_column_value
1✔
68
from Products.PloneMeeting.utils import getAdvicePortalTypes
1✔
69
from Products.PloneMeeting.utils import number_word
1✔
70
from Products.PloneMeeting.utils import split_gender_and_number
1✔
71
from z3c.form.interfaces import NO_VALUE
1✔
72
from zope.annotation import IAnnotations
1✔
73
from zope.globalrequest import getRequest
1✔
74
from zope.i18n import translate
1✔
75
from zope.interface import implements
1✔
76
from zope.schema.interfaces import IVocabularyFactory
1✔
77
from zope.schema.vocabulary import SimpleTerm
1✔
78
from zope.schema.vocabulary import SimpleVocabulary
1✔
79
from collective.documentgenerator.interfaces import IGenerablePODTemplates
1✔
80
from zope.component import getAdapter
1✔
81

82
import html
1✔
83
import itertools
1✔
84

85

86
class PMConditionAwareCollectionVocabulary(CachedCollectionVocabulary):
1✔
87
    implements(IVocabularyFactory)
1✔
88

89
    def _cache_invalidation_key(self, context, real_context):
1✔
90
        """Take also into account current user groups,
91
           this will make cache invalidated when user groups changed.
92
           We keep original check on user_id because the vocabulary contains links
93
           to the user personal folder."""
94
        original_checks = super(PMConditionAwareCollectionVocabulary, self)._cache_invalidation_key(
1✔
95
            context, real_context)
96
        return original_checks + (get_plone_groups_for_user(), )
1✔
97

98
    def _brains(self, context):
1✔
99
        """We override the method because Meetings also provides the ICollection interface..."""
100
        root = context
1✔
101
        while IFacetedNavigable.providedBy(root.aq_inner.aq_parent):
1✔
102
            root = root.aq_inner.aq_parent
1✔
103
        brains = find(
1✔
104
            context=root,
105
            unrestricted=True,
106
            portal_type='DashboardCollection',
107
            enabled=True,
108
            sort_on='getObjPositionInParent'
109
        )
110
        return brains
1✔
111

112
    def _compute_redirect_to(self, collection, criterion):
1✔
113
        """ """
114
        redirect_to = super(PMConditionAwareCollectionVocabulary, self)._compute_redirect_to(collection,
1✔
115
                                                                                             criterion)
116
        # XXX begin change by PloneMeeting, do redirect to the folder in the user pmFolder
117
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
118
        cfg = tool.getMeetingConfig(collection)
1✔
119
        redirect_to = redirect_to.replace(cfg.searches.absolute_url(),
1✔
120
                                          tool.getPloneMeetingFolder(cfg.getId()).absolute_url())
121
        return redirect_to
1✔
122
        # XXX end change
123

124
    def _extra_expr_ctx(self):
1✔
125
        """Manage 'fromPortletTodo', other useful values will be added
126
           by TALCondition.complete_extra_expr_ctx."""
127
        return {'fromPortletTodo': False, }
1✔
128

129

130
PMConditionAwareCollectionVocabularyFactory = PMConditionAwareCollectionVocabulary()
1✔
131

132

133
class CategoriesVocabulary(object):
1✔
134
    implements(IVocabularyFactory)
1✔
135

136
    def __call___cachekey(method, self, context, cat_type='categories'):
1✔
137
        '''cachekey method for self.__call__.'''
138
        date = get_cachekey_volatile('Products.PloneMeeting.MeetingConfig.getCategoriesIds')
1✔
139
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
140
        cfg = tool.getMeetingConfig(context)
1✔
141
        return date, repr(cfg), cat_type
1✔
142

143
    @ram.cache(__call___cachekey)
1✔
144
    def __call__(self, context, cat_type='categories'):
1✔
145
        """ """
146
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
147
        cfg = tool.getMeetingConfig(context)
1✔
148
        categories = cfg.getCategories(catType=cat_type, onlySelectable=False)
1✔
149
        activeCategories = [cat for cat in categories if cat.enabled]
1✔
150
        notActiveCategories = [cat for cat in categories if not cat.enabled]
1✔
151
        res_active = []
1✔
152
        for category in activeCategories:
1✔
153
            term_id = category.getId()
1✔
154
            res_active.append(
1✔
155
                SimpleTerm(term_id,
156
                           term_id,
157
                           safe_unicode(category.Title())
158
                           )
159
            )
160
        res = humansorted(res_active, key=attrgetter('title'))
1✔
161

162
        res_not_active = []
1✔
163
        for category in notActiveCategories:
1✔
164
            term_id = category.getId()
1✔
165
            res_not_active.append(
1✔
166
                SimpleTerm(term_id,
167
                           term_id,
168
                           translate('${element_title} (Inactive)',
169
                                     domain='PloneMeeting',
170
                                     mapping={'element_title': safe_unicode(category.Title())},
171
                                     context=context.REQUEST)
172
                           )
173
            )
174
        res = res + sorted(res_not_active, key=attrgetter('title'))
1✔
175
        return SimpleVocabulary(res)
1✔
176

177

178
class ItemCategoriesVocabulary(CategoriesVocabulary):
1✔
179
    implements(IVocabularyFactory)
1✔
180

181
    def ItemCategoriesVocabulary__call__(self, context, cat_type='categories'):
1✔
182
        """ """
183
        return super(ItemCategoriesVocabulary, self).__call__(
1✔
184
            context, cat_type=cat_type)
185

186
    # do ram.cache have a different key name
187
    __call__ = ItemCategoriesVocabulary__call__
1✔
188

189

190
ItemCategoriesVocabularyFactory = ItemCategoriesVocabulary()
1✔
191

192

193
class ItemClassifiersVocabulary(ItemCategoriesVocabulary):
1✔
194
    implements(IVocabularyFactory)
1✔
195

196
    def ItemClassifiersVocabulary__call__(self, context, cat_type='categories'):
1✔
197
        """ """
198
        return super(ItemClassifiersVocabulary, self).__call__(
1✔
199
            context, cat_type='classifiers')
200

201
    # do ram.cache have a different key name
202
    __call__ = ItemClassifiersVocabulary__call__
1✔
203

204

205
ItemClassifiersVocabularyFactory = ItemClassifiersVocabulary()
1✔
206

207

208
class MeetingCategoriesVocabulary(CategoriesVocabulary):
1✔
209
    implements(IVocabularyFactory)
1✔
210

211
    def MeetingCategoriesVocabulary__call__(self, context, cat_type='categories'):
1✔
212
        """ """
213
        return super(MeetingCategoriesVocabulary, self).__call__(
1✔
214
            context, cat_type='meetingcategories')
215

216
    # do ram.cache have a different key name
217
    __call__ = MeetingCategoriesVocabulary__call__
1✔
218

219

220
MeetingCategoriesVocabularyFactory = MeetingCategoriesVocabulary()
1✔
221

222

223
class ItemProposingGroupsVocabulary(object):
1✔
224
    implements(IVocabularyFactory)
1✔
225

226
    def __call___cachekey(method, self, context):
1✔
227
        '''cachekey method for self.__call__.'''
228
        # this volatile is invalidated when plonegroup config changed
229
        date = get_cachekey_volatile(
1✔
230
            '_users_groups_value')
231
        return date
1✔
232

233
    @ram.cache(__call___cachekey)
1✔
234
    def ItemProposingGroupsVocabulary__call__(self, context):
×
235
        """ """
236
        active_orgs = get_organizations(only_selected=True)
1✔
237
        not_active_orgs = [org for org in get_organizations(only_selected=False)
1✔
238
                           if org not in active_orgs]
239
        res_active = []
1✔
240
        for active_org in active_orgs:
1✔
241
            res_active.append(
1✔
242
                SimpleTerm(active_org.UID(),
243
                           active_org.UID(),
244
                           safe_unicode(active_org.get_full_title(first_index=1))
245
                           )
246
            )
247
        res = humansorted(res_active, key=attrgetter('title'))
1✔
248

249
        res_not_active = []
1✔
250
        request = getattr(context, 'REQUEST', getRequest())
1✔
251
        for not_active_org in not_active_orgs:
1✔
252
            res_not_active.append(
1✔
253
                SimpleTerm(not_active_org.UID(),
254
                           not_active_org.UID(),
255
                           translate('${element_title} (Inactive)',
256
                                     domain='PloneMeeting',
257
                                     mapping={'element_title': safe_unicode(
258
                                            not_active_org.get_full_title(first_index=1))},
259
                                     context=request)
260
                           )
261
            )
262
        res = res + humansorted(res_not_active, key=attrgetter('title'))
1✔
263
        return SimpleVocabulary(res)
1✔
264

265
    # do ram.cache have a different key name
266
    __call__ = ItemProposingGroupsVocabulary__call__
1✔
267

268

269
ItemProposingGroupsVocabularyFactory = ItemProposingGroupsVocabulary()
1✔
270

271

272
class ItemProposingGroupsForFacetedFilterVocabulary(object):
1✔
273
    implements(IVocabularyFactory)
1✔
274

275
    def __call___cachekey(method, self, context):
1✔
276
        '''cachekey method for self.__call__.'''
277
        # this volatile is invalidated when plonegroup config changed
278
        date = get_cachekey_volatile(
1✔
279
            '_users_groups_value')
280
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
281
        cfg = tool.getMeetingConfig(context)
1✔
282
        return date, repr(cfg)
1✔
283

284
    @ram.cache(__call___cachekey)
1✔
285
    def ItemProposingGroupsForFacetedFilterVocabulary__call__(self, context):
×
286
        """ """
287
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
288
        cfg = tool.getMeetingConfig(context)
1✔
289
        active_orgs = get_organizations(only_selected=True)
1✔
290
        not_active_orgs = [org for org in get_organizations(only_selected=False)
1✔
291
                           if org not in active_orgs]
292
        res_active = []
1✔
293
        groupsToHide = cfg.getGroupsHiddenInDashboardFilter()
1✔
294
        res_active = []
1✔
295
        for active_org in active_orgs:
1✔
296
            org_uid = active_org.UID()
1✔
297
            if not groupsToHide or org_uid not in groupsToHide:
1✔
298
                res_active.append(
1✔
299
                    SimpleTerm(org_uid,
300
                               org_uid,
301
                               safe_unicode(active_org.get_full_title(first_index=1))
302
                               )
303
                )
304
        res = humansorted(res_active, key=attrgetter('title'))
1✔
305

306
        res_not_active = []
1✔
307
        for not_active_org in not_active_orgs:
1✔
308
            org_uid = not_active_org.UID()
1✔
309
            if not groupsToHide or org_uid not in groupsToHide:
1✔
310
                res_not_active.append(
1✔
311
                    SimpleTerm(org_uid,
312
                               org_uid,
313
                               translate('${element_title} (Inactive)',
314
                                         domain='PloneMeeting',
315
                                         mapping={'element_title': safe_unicode(
316
                                                not_active_org.get_full_title(first_index=1))},
317
                                         context=context.REQUEST)
318
                               )
319
                )
320
        res = res + humansorted(res_not_active, key=attrgetter('title'))
1✔
321
        return SimpleVocabulary(res)
1✔
322

323
    # do ram.cache have a different key name
324
    __call__ = ItemProposingGroupsForFacetedFilterVocabulary__call__
1✔
325

326

327
ItemProposingGroupsForFacetedFilterVocabularyFactory = ItemProposingGroupsForFacetedFilterVocabulary()
1✔
328

329

330
class UserProposingGroupsVocabulary(object):
1✔
331
    """ """
332
    implements(IVocabularyFactory)
1✔
333

334
    def _user_proposing_group_terms_cachekey(method, self, context, tool, cfg):
1✔
335
        '''cachekey method for self._user_proposing_group_terms.'''
336
        date = get_cachekey_volatile('_users_groups_value')
1✔
337
        selectable_org_uids = self._get_selectable_orgs(context, tool, cfg, the_objects=False)
1✔
338
        # use self.__class__.__name__ to get different ram.cache keys
339
        return date, context.portal_type, selectable_org_uids, self.__class__.__name__
1✔
340

341
    def _get_selectable_orgs(self, context, tool, cfg, the_objects=True):
1✔
342
        """ """
343
        isDefinedInTool = context.isDefinedInTool()
1✔
344
        # bypass for Managers, pass isDefinedInTool to True so Managers
345
        # can select any available organizations
346
        isManager = tool.isManager(realManagers=True)
1✔
347
        # show every groups for Managers or when isDefinedInTool
348
        only_selectable = not bool(isDefinedInTool or isManager)
1✔
349
        orgs = tool.get_selectable_orgs(
1✔
350
            cfg, only_selectable=only_selectable, the_objects=the_objects)
351
        return orgs
1✔
352

353
    @ram.cache(_user_proposing_group_terms_cachekey)
1✔
354
    def _user_proposing_group_terms(self, context, tool, cfg):
×
355
        """ """
356
        orgs = self._get_selectable_orgs(context, tool, cfg)
1✔
357
        terms = []
1✔
358
        term_values = []
1✔
359
        for org in orgs:
1✔
360
            term_value = org.UID()
1✔
361
            terms.append(
1✔
362
                SimpleTerm(term_value,
363
                           term_value,
364
                           safe_unicode(org.get_full_title(first_index=1))))
365
            term_values.append(term_value)
1✔
366
        return term_values, terms
1✔
367

368
    def _handle_include_stored(self, context, term_values, terms):
1✔
369
        """ """
370
        proposingGroup = context.getProposingGroup()
1✔
371
        if proposingGroup and proposingGroup not in term_values:
1✔
372
            org = context.getProposingGroup(theObject=True)
1✔
373
            term_value = org.UID()
1✔
374
            terms.append(
1✔
375
                SimpleTerm(term_value,
376
                           term_value,
377
                           safe_unicode(org.get_full_title(first_index=1))))
378
        return terms
1✔
379

380
    def __call__(self, context, include_stored=True):
1✔
381
        '''This is used as vocabulary for field 'MeetingItem.proposingGroup'.
382
           Return the organization(s) the user is creator for.
383
           If this item is being created or edited in portal_plonemeeting (as a
384
           recurring item), the list of active groups is returned.'''
385
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
386
        cfg = tool.getMeetingConfig(context)
1✔
387
        term_values, terms = self._user_proposing_group_terms(context, tool, cfg)
1✔
388
        # avoid modifying original list
389
        terms = list(terms)
1✔
390
        # include_stored
391
        if include_stored:
1✔
392
            terms = self._handle_include_stored(context, term_values, terms)
1✔
393
        # sort correctly
394
        if 'proposingGroup' not in cfg.getItemFieldsToKeepConfigSortingFor():
1✔
395
            terms = humansorted(terms, key=attrgetter('title'))
1✔
396
        # add a 'make_a_choice' value when used on an itemtemplate
397
        if context.isDefinedInTool(item_type='itemtemplate'):
1✔
398
            terms.insert(
1✔
399
                0,
400
                SimpleTerm("",
401
                           "",
402
                           translate('make_a_choice',
403
                                     domain='PloneMeeting',
404
                                     context=context.REQUEST).encode('utf-8')))
405
        return SimpleVocabulary(terms)
1✔
406

407

408
UserProposingGroupsVocabularyFactory = UserProposingGroupsVocabulary()
1✔
409

410

411
class UserProposingGroupsWithGroupsInChargeVocabulary(UserProposingGroupsVocabulary):
1✔
412
    """ """
413

414
    def _user_proposing_group_terms(self, context, tool, cfg):
1✔
415
        """ """
416
        orgs = self._get_selectable_orgs(context, tool, cfg)
1✔
417
        terms = []
1✔
418
        term_values = []
1✔
419
        active_org_uids = get_registry_organizations()
1✔
420
        for org in orgs:
1✔
421
            org_uid = org.UID()
1✔
422
            groupsInCharge = org.groups_in_charge
1✔
423
            if not groupsInCharge:
1✔
424
                # append a value that will let use a simple
425
                # proposingGroup without groupInCharge
426
                term_value = u'{0}__groupincharge__{1}'.format(org_uid, '')
×
427
                terms.append(
×
428
                    SimpleTerm(term_value,
429
                               term_value,
430
                               u'{0} ()'.format(org.get_full_title())))
431
                term_values.append(term_value)
×
432
            for gic_org in org.get_groups_in_charge(the_objects=True):
1✔
433
                gic_org_uid = gic_org.UID()
1✔
434
                term_value = u'{0}__groupincharge__{1}'.format(
1✔
435
                    org_uid, gic_org_uid)
436
                # only take active groups in charge
437
                if gic_org_uid in active_org_uids:
1✔
438
                    terms.append(
1✔
439
                        SimpleTerm(
440
                            term_value,
441
                            term_value,
442
                            u'{0} ({1})'.format(
443
                                org.get_full_title(), gic_org.get_full_title())))
444
                    term_values.append(term_value)
1✔
445
        return term_values, terms
1✔
446

447
    def _handle_include_stored(self, context, term_values, terms):
1✔
448
        """ """
449
        current_value = context.getProposingGroupWithGroupInCharge()
1✔
450
        if current_value and current_value not in term_values:
1✔
451
            current_proposingGroupUid, current_groupInChargeUid = \
1✔
452
                current_value.split('__groupincharge__')
453
            # current_groupInChargeUid may be empty in case configuration was
454
            # wrong (no selected groups in charge) and it was updated
455
            gic_title = u''
1✔
456
            if current_groupInChargeUid:
1✔
457
                gic_title = get_organization(current_groupInChargeUid).get_full_title()
1✔
458
            terms.append(
1✔
459
                SimpleTerm(
460
                    current_value,
461
                    current_value,
462
                    u'{0} ({1})'.format(
463
                        get_organization(current_proposingGroupUid).get_full_title(), gic_title)))
464
        return terms
1✔
465

466

467
UserProposingGroupsWithGroupsInChargeVocabularyFactory = UserProposingGroupsWithGroupsInChargeVocabulary()
1✔
468

469

470
class GroupsInChargeVocabulary(object):
1✔
471
    implements(IVocabularyFactory)
1✔
472

473
    def __call___cachekey(method, self, context, only_selected=True, sort=True):
1✔
474
        '''cachekey method for self.__call__.'''
475
        date = get_cachekey_volatile('Products.PloneMeeting.vocabularies.groupsinchargevocabulary')
1✔
476
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
477
        cfg = tool.getMeetingConfig(context)
1✔
478
        return date, repr(cfg), only_selected, sort
1✔
479

480
    def _get_organizations(self, context, only_selected=True):
1✔
481
        """This centralize logic of gettting groups in charge."""
482
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
483
        cfg = tool.getMeetingConfig(context)
1✔
484
        res = []
1✔
485
        is_using_cfg_order = False
1✔
486
        used_item_attrs = cfg.getUsedItemAttributes()
1✔
487
        if 'groupsInCharge' not in used_item_attrs:
1✔
488
            # groups in charge are defined on organizations or categories
489
            # organizations
490
            orgs = get_organizations(only_selected=only_selected)
1✔
491
            for org in orgs:
1✔
492
                for group_in_charge_uid in (org.groups_in_charge or []):
1✔
493
                    group_in_charge = get_organization(group_in_charge_uid)
1✔
494
                    # manage duplicates
495
                    if group_in_charge and group_in_charge not in res:
1✔
496
                        res.append(group_in_charge)
1✔
497
            # categories
498
            if 'category' in used_item_attrs:
1✔
499
                categories = cfg.getCategories(onlySelectable=False)
1✔
500
                # add classifiers when using it
501
                if 'classifier' in used_item_attrs:
1✔
502
                    categories += cfg.getCategories(
1✔
503
                        catType='classifiers', onlySelectable=False)
504
                for cat in categories:
1✔
505
                    for group_in_charge in cat.get_groups_in_charge(the_objects=True):
1✔
506
                        # manage duplicates
507
                        if group_in_charge not in res:
1✔
508
                            res.append(group_in_charge)
1✔
509
        else:
510
            # groups in charge are selected on the items
511
            is_using_cfg_order = True
1✔
512
            kept_org_uids = cfg.getOrderedGroupsInCharge()
1✔
513
            res = get_organizations(only_selected=only_selected, kept_org_uids=kept_org_uids)
1✔
514
        return is_using_cfg_order, res
1✔
515

516
    @ram.cache(__call___cachekey)
1✔
517
    def GroupsInChargeVocabulary__call__(self, context, only_selected=True, sort=True):
1✔
518
        """List groups in charge :
519
           - if groupsInCharge in MeetingConfig.usedItemAttributes,
520
             list MeetingConfig.orderedGroupsInCharge;
521
           - else, list groups_in_charge selected on organizations."""
522
        is_using_cfg_order, orgs = self._get_organizations(context, only_selected=only_selected)
1✔
523
        terms = []
1✔
524
        for org in orgs:
1✔
525
            term_value = org.UID()
1✔
526
            terms.append(
1✔
527
                SimpleTerm(term_value,
528
                           term_value,
529
                           safe_unicode(org.get_full_title(first_index=1))))
530

531
        if sort or not is_using_cfg_order:
1✔
532
            terms = humansorted(terms, key=attrgetter('title'))
1✔
533

534
        return SimpleVocabulary(terms)
1✔
535

536
    # do ram.cache have a different key name
537
    __call__ = GroupsInChargeVocabulary__call__
1✔
538

539

540
GroupsInChargeVocabularyFactory = GroupsInChargeVocabulary()
1✔
541

542

543
class ItemGroupsInChargeVocabulary(GroupsInChargeVocabulary):
1✔
544
    """Manage missing terms when context is a MeetingItem."""
545

546
    def __call__(self, context):
1✔
547
        """ """
548
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
549
        cfg = tool.getMeetingConfig(context)
1✔
550
        sort = True
1✔
551
        if 'groupsInCharge' in cfg.getItemFieldsToKeepConfigSortingFor():
1✔
552
            sort = False
1✔
553
        terms = list(super(ItemGroupsInChargeVocabulary, self).__call__(
1✔
554
            context, sort=sort)._terms)
555

556
        # when used on an item, manage missing terms, selected on item
557
        # but removed from orderedGroupsInCharge or from plonegroup
558
        # check if it is an item as vocabulary is used in the batch action
559
        if context.__class__.__name__ == "MeetingItem":
1✔
560
            stored_terms = context.getGroupsInCharge()
1✔
561
            term_uids = [term.token for term in terms]
1✔
562
            missing_term_uids = [uid for uid in stored_terms
1✔
563
                                 if uid not in term_uids]
564
            if missing_term_uids:
1✔
565
                # make sure we only have organizations stored in own org
566
                # this may be the case when creating item thru a restapi call
567
                missing_term_uids = [uid for uid in missing_term_uids
1✔
568
                                     if uid in get_organizations(only_selected=False, the_objects=False)]
569
                if missing_term_uids:
1✔
570
                    missing_terms = uuidsToObjects(missing_term_uids, ordered=False, unrestricted=True)
1✔
571
                    for org in missing_terms:
1✔
572
                        org_uid = org.UID()
1✔
573
                        terms.append(SimpleTerm(org_uid, org_uid, org.get_full_title()))
1✔
574

575
        return SimpleVocabulary(terms)
1✔
576

577

578
ItemGroupsInChargeVocabularyFactory = ItemGroupsInChargeVocabulary()
1✔
579

580

581
class PMEveryOrganizationsVocabulary(EveryOrganizationsVocabulary):
1✔
582
    """ """
583

584
    def __call___cachekey(method, self, context):
1✔
585
        '''cachekey method for self.__call__.'''
586
        date = get_cachekey_volatile(
1✔
587
            'Products.PloneMeeting.vocabularies.everyorganizationsvocabulary')
588
        return date
1✔
589

590
    @ram.cache(__call___cachekey)
1✔
591
    def PMEveryOrganizationsVocabulary__call__(self, context):
×
592
        return super(PMEveryOrganizationsVocabulary, self).__call__(context)
1✔
593

594
    def _term_title(self, orga, parent_label):
1✔
595
        # ignore parent_label
596
        return orga.title
1✔
597

598
    # do ram.cache have a different key name
599
    __call__ = PMEveryOrganizationsVocabulary__call__
1✔
600

601

602
PMEveryOrganizationsVocabularyFactory = PMEveryOrganizationsVocabulary()
1✔
603

604

605
class EveryOrganizationsAcronymsVocabulary(EveryOrganizationsVocabulary):
1✔
606
    implements(IVocabularyFactory)
1✔
607

608
    def __call___cachekey(method, self, context):
1✔
609
        '''cachekey method for self.__call__.'''
610
        date = get_cachekey_volatile(
1✔
611
            'Products.PloneMeeting.vocabularies.everyorganizationsacronymsvocabulary')
612
        return date
1✔
613

614
    @ram.cache(__call___cachekey)
1✔
615
    def EveryOrganizationsAcronymsVocabulary__call__(self, context):
×
616
        return super(EveryOrganizationsAcronymsVocabulary, self).__call__(context)
1✔
617

618
    def _term_title(self, orga, parent_label):
1✔
619
        # org acronym instead title
620
        return orga.acronym or translate("None", domain="PloneMeeting", context=orga.REQUEST)
1✔
621

622
    # do ram.cache have a different key name
623
    __call__ = EveryOrganizationsAcronymsVocabulary__call__
1✔
624

625

626
EveryOrganizationsAcronymsVocabularyFactory = EveryOrganizationsAcronymsVocabulary()
1✔
627

628

629
class PMSortedSelectedOrganizationsElephantVocabulary(SortedSelectedOrganizationsElephantVocabulary):
1✔
630
    """Vocabulary returning org objects, to be used with RelationList fields."""
631

632
    # def _term_value(self, orga):
633
    #     """RelationList vocabulary must be objects."""
634
    #     return orga
635

636
    def PMSortedSelectedOrganizationsElephantVocabulary__call__(self, context):
1✔
637
        """Does not work with ElephantVocabulary when used as vocabulary
638
           for a RelationList field, so unwrap it."""
639

640
        # caching
641
        key = "PloneMeeting-vocabularies-PMSortedSelectedOrganizationsElephantVocabulary"
1✔
642
        cache = IAnnotations(context.REQUEST)
1✔
643
        vocab = cache.get(key, None)
1✔
644
        if vocab is None:
1✔
645
            wrapped_vocab = super(PMSortedSelectedOrganizationsElephantVocabulary, self).__call__(
1✔
646
                context)
647
            vocab = wrapped_vocab.vocab
1✔
648
            # term values need to be an object but can not be ram.cached...
649
            uids = [term.value for term in vocab._terms]
1✔
650
            objs = uuidsToObjects(uids, ordered=True, unrestricted=True)
1✔
651
            # build a new vocab to avoid changing value of original terms
652
            terms = []
1✔
653
            for term, obj in itertools.izip(vocab._terms, objs):
1✔
654
                terms.append(SimpleTerm(obj, term.token, term.title))
1✔
655
            vocab = SimpleVocabulary(terms)
1✔
656
            cache[key] = vocab
1✔
657
        return vocab
1✔
658

659
    # do ram.cache have a different key name
660
    __call__ = PMSortedSelectedOrganizationsElephantVocabulary__call__
1✔
661

662

663
PMSortedSelectedOrganizationsElephantVocabularyFactory = PMSortedSelectedOrganizationsElephantVocabulary()
1✔
664

665

666
class MeetingReviewStatesVocabulary(object):
1✔
667
    implements(IVocabularyFactory)
1✔
668

669
    def __call___cachekey(method, self, context):
1✔
670
        '''cachekey method for self.__call__.'''
671
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
672
        cfg = tool.getMeetingConfig(context)
1✔
673
        return repr(cfg)
1✔
674

675
    @ram.cache(__call___cachekey)
1✔
676
    def MeetingReviewStatesVocabulary__call__(self, context):
×
677
        """ """
678
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
679
        cfg = tool.getMeetingConfig(context)
1✔
680
        res = []
1✔
681
        states = cfg.listStates('Meeting', with_state_id=False)
1✔
682
        for state_id, state_title in states:
1✔
683
            res.append(SimpleTerm(state_id,
1✔
684
                                  state_id,
685
                                  safe_unicode(state_title))
686
                       )
687
        res = humansorted(res, key=attrgetter('title'))
1✔
688
        return SimpleVocabulary(res)
1✔
689

690
    # do ram.cache have a different key name
691
    __call__ = MeetingReviewStatesVocabulary__call__
1✔
692

693

694
MeetingReviewStatesVocabularyFactory = MeetingReviewStatesVocabulary()
1✔
695

696

697
class ItemReviewStatesVocabulary(object):
1✔
698
    implements(IVocabularyFactory)
1✔
699

700
    def __call___cachekey(method, self, context):
1✔
701
        '''cachekey method for self.__call__.'''
702
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
703
        cfg = tool.getMeetingConfig(context)
1✔
704
        return repr(cfg)
1✔
705

706
    @ram.cache(__call___cachekey)
1✔
707
    def ItemReviewStatesVocabulary__call__(self, context):
×
708
        """ """
709
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
710
        cfg = tool.getMeetingConfig(context)
1✔
711
        res = []
1✔
712
        states = cfg.listStates('Item', with_state_id=False)
1✔
713
        for state_id, state_title in states:
1✔
714
            res.append(SimpleTerm(state_id,
1✔
715
                                  state_id,
716
                                  safe_unicode(state_title))
717
                       )
718
        res = humansorted(res, key=attrgetter('title'))
1✔
719
        return SimpleVocabulary(res)
1✔
720

721
    # do ram.cache have a different key name
722
    __call__ = ItemReviewStatesVocabulary__call__
1✔
723

724

725
ItemReviewStatesVocabularyFactory = ItemReviewStatesVocabulary()
1✔
726

727

728
class CreatorsVocabulary(object):
1✔
729
    implements(IVocabularyFactory)
1✔
730

731
    def __call___cachekey(method, self, context):
1✔
732
        '''cachekey method for self.__call__.'''
733
        date = get_cachekey_volatile('Products.PloneMeeting.vocabularies.creatorsvocabulary')
1✔
734
        return date
1✔
735

736
    @ram.cache(__call___cachekey)
1✔
737
    def CreatorsVocabulary__call__(self, context):
×
738
        """ """
739
        catalog = api.portal.get_tool('portal_catalog')
1✔
740
        res = []
1✔
741
        for creator in catalog.uniqueValuesFor('Creator'):
1✔
742
            value = get_user_fullname(creator)
1✔
743
            res.append(SimpleTerm(creator,
1✔
744
                                  creator,
745
                                  safe_unicode(value))
746
                       )
747
        res = humansorted(res, key=attrgetter('title'))
1✔
748
        return SimpleVocabulary(res)
1✔
749

750
    # do ram.cache have a different key name
751
    __call__ = CreatorsVocabulary__call__
1✔
752

753

754
CreatorsVocabularyFactory = CreatorsVocabulary()
1✔
755

756

757
class CreatorsForFacetedFilterVocabulary(object):
1✔
758
    implements(IVocabularyFactory)
1✔
759

760
    def __call___cachekey(method, self, context):
1✔
761
        '''cachekey method for self.__call__.'''
762
        date = get_cachekey_volatile(
1✔
763
            'Products.PloneMeeting.vocabularies.creatorsforfacetedfiltervocabulary')
764
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
765
        cfg = tool.getMeetingConfig(context)
1✔
766
        return date, repr(cfg)
1✔
767

768
    @ram.cache(__call___cachekey)
1✔
769
    def CreatorsForFacetedFilterVocabulary__call__(self, context):
×
770
        """ """
771
        catalog = api.portal.get_tool('portal_catalog')
1✔
772
        res = []
1✔
773

774
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
775
        cfg = tool.getMeetingConfig(context)
1✔
776
        creatorsToHide = cfg.getUsersHiddenInDashboardFilter()
1✔
777
        creators = catalog.uniqueValuesFor('Creator')
1✔
778
        filteredCreators = [creator for creator in creators
1✔
779
                            if creator not in creatorsToHide]
780

781
        for creator in filteredCreators:
1✔
782
            value = get_user_fullname(creator)
1✔
783
            res.append(SimpleTerm(creator,
1✔
784
                                  creator,
785
                                  safe_unicode(value))
786
                       )
787
        res = humansorted(res, key=attrgetter('title'))
1✔
788
        return SimpleVocabulary(res)
1✔
789

790
    # do ram.cache have a different key name
791
    __call__ = CreatorsForFacetedFilterVocabulary__call__
1✔
792

793

794
CreatorsForFacetedFilterVocabularyFactory = CreatorsForFacetedFilterVocabulary()
1✔
795

796

797
class CreatorsWithNobodyForFacetedFilterVocabulary(CreatorsForFacetedFilterVocabulary):
1✔
798
    """Add the 'Nobody' option.
799
       Used by the 'Taken over by' faceted filter."""
800

801
    def __call__(self, context):
1✔
802
        """ """
803
        res = super(CreatorsWithNobodyForFacetedFilterVocabulary, self).__call__(context)
1✔
804
        # avoid to change original list of _terms
805
        res = list(res._terms)
1✔
806
        res.insert(0,
1✔
807
                   SimpleTerm(EMPTY_STRING,
808
                              EMPTY_STRING,
809
                              translate('(Nobody)',
810
                                        domain='PloneMeeting',
811
                                        context=context.REQUEST)))
812
        return SimpleVocabulary(res)
1✔
813

814

815
CreatorsWithNobodyForFacetedFilterVocabularyFactory = CreatorsWithNobodyForFacetedFilterVocabulary()
1✔
816

817

818
class MeetingDatesVocabulary(object):
1✔
819
    implements(IVocabularyFactory)
1✔
820

821
    def __call___cachekey(method, self, context):
1✔
822
        '''cachekey method for self.__call__.'''
823
        date = get_cachekey_volatile('Products.PloneMeeting.Meeting.date')
1✔
824
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
825
        cfg = tool.getMeetingConfig(context)
1✔
826
        return date, repr(cfg)
1✔
827

828
    @ram.cache(__call___cachekey)
1✔
829
    def MeetingDatesVocabulary__call__(self, context):
×
830
        """ """
831
        catalog = api.portal.get_tool('portal_catalog')
1✔
832
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
833
        cfg = tool.getMeetingConfig(context)
1✔
834
        brains = catalog.unrestrictedSearchResults(
1✔
835
            portal_type=cfg.getMeetingTypeName(),
836
            sort_on='meeting_date',
837
            sort_order='reverse')
838
        res = [
1✔
839
            SimpleTerm(ITEM_NO_PREFERRED_MEETING_VALUE,
840
                       ITEM_NO_PREFERRED_MEETING_VALUE,
841
                       translate('(None)',
842
                                 domain='PloneMeeting',
843
                                 context=context.REQUEST))]
844
        for brain in brains:
1✔
845
            res.append(SimpleTerm(brain.UID,
1✔
846
                                  brain.UID,
847
                                  tool.format_date(brain.meeting_date,
848
                                                   with_hour=True,
849
                                                   short=True))
850
                       )
851
        return SimpleVocabulary(res)
1✔
852

853
    # do ram.cache have a different key name
854
    __call__ = MeetingDatesVocabulary__call__
1✔
855

856

857
MeetingDatesVocabularyFactory = MeetingDatesVocabulary()
1✔
858

859

860
class AskedAdvicesVocabulary(object):
1✔
861
    implements(IVocabularyFactory)
1✔
862

863
    def _getAdvisers(self, active=True):
1✔
864
        """ """
865
        res = []
1✔
866
        # customAdvisers
867
        customAdvisers = self.cfg and self.cfg.getCustomAdvisers() or []
1✔
868
        for customAdviser in customAdvisers:
1✔
869
            if (active and customAdviser['for_item_created_until']) or \
1✔
870
               (not active and not customAdviser['for_item_created_until']):
871
                continue
1✔
872
            if customAdviser['delay']:
1✔
873
                # build using DELAYAWARE_ROW_ID_PATTERN
874
                res.append(DELAYAWARE_ROW_ID_PATTERN.format(customAdviser['row_id']))
1✔
875
            else:
876
                # build using REAL_ORG_UID_PATTERN
877
                res.append(REAL_ORG_UID_PATTERN.format(customAdviser['org']))
×
878

879
        # classic advisers
880
        org_uids = [org_uid for org_uid in get_organizations(only_selected=True, the_objects=False)
1✔
881
                    if org_uid in self.cfg.getSelectableAdvisers()]
882
        if not active:
1✔
883
            org_uids = [org_uid for org_uid in get_organizations(only_selected=False, the_objects=False)
1✔
884
                        if org_uid not in org_uids and org_uid in self.cfg.getSelectableAdvisers()]
885
        for org_uid in org_uids:
1✔
886
            formatted = REAL_ORG_UID_PATTERN.format(org_uid)
1✔
887
            res.append(formatted)
1✔
888

889
        # power advisers
890
        power_adviser_uids = self.cfg.getPowerAdvisersGroups()
1✔
891
        for power_adviser_uid in power_adviser_uids:
1✔
892
            formatted = REAL_ORG_UID_PATTERN.format(power_adviser_uid)
1✔
893
            res.append(formatted)
1✔
894

895
        # remove duplicates
896
        res = list(set(res))
1✔
897
        return res
1✔
898

899
    def adviser_term_title(self, adviser):
1✔
900
        """ """
901
        termTitle = None
1✔
902
        if adviser.startswith(REAL_ORG_UID_PATTERN.format('')):
1✔
903
            org_uid = adviser.split(REAL_ORG_UID_PATTERN.format(''))[-1]
1✔
904
            org = get_organization(org_uid)
1✔
905
            termTitle = org.get_full_title()
1✔
906
        elif adviser.startswith(DELAYAWARE_ROW_ID_PATTERN.format('')):
1✔
907
            row_id = adviser.split(DELAYAWARE_ROW_ID_PATTERN.format(''))[-1]
1✔
908
            delayAwareAdviser = self.cfg._dataForCustomAdviserRowId(row_id)
1✔
909
            delay = safe_unicode(delayAwareAdviser['delay'])
1✔
910
            delay_label = safe_unicode(delayAwareAdviser['delay_label'])
1✔
911
            org_uid = delayAwareAdviser['org']
1✔
912
            org = get_organization(org_uid)
1✔
913
            org_title = org.get_full_title()
1✔
914
            if delay_label:
1✔
915
                termTitle = translate('advice_delay_with_label',
1✔
916
                                      domain='PloneMeeting',
917
                                      mapping={'org_title': org_title,
918
                                               'delay': delay,
919
                                               'delay_label': delay_label},
920
                                      default='${group_name} - ${delay} day(s) (${delay_label})',
921
                                      context=self.request)
922
            else:
923
                termTitle = translate('advice_delay_without_label',
1✔
924
                                      domain='PloneMeeting',
925
                                      mapping={'org_title': org_title,
926
                                               'delay': delay},
927
                                      default='${group_name} - ${delay} day(s)',
928
                                      context=self.request)
929
        return termTitle
1✔
930

931
    def __call___cachekey(method, self, context):
1✔
932
        '''cachekey method for self.__call__.'''
933
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
934
        cfg = tool.getMeetingConfig(context)
1✔
935
        # invalidate if an org title is changed
936
        date = get_cachekey_volatile('Products.PloneMeeting.vocabularies.everyorganizationsvocabulary')
1✔
937
        return date, repr(cfg)
1✔
938

939
    @ram.cache(__call___cachekey)
1✔
940
    def AskedAdvicesVocabulary__call__(self, context):
×
941
        """ """
942
        res = []
1✔
943
        context = get_context_with_request(context) or context
1✔
944

945
        self.tool = api.portal.get_tool('portal_plonemeeting')
1✔
946
        try:
1✔
947
            # in some case, like Plone Site creation, context is the Zope app...
948
            self.cfg = self.tool.getMeetingConfig(context)
1✔
949
        except Exception:
×
950
            return SimpleVocabulary(res)
×
951
        if self.cfg is None:
1✔
952
            return SimpleVocabulary(res)
1✔
953

954
        self.context = context
1✔
955
        self.request = context.REQUEST
1✔
956
        active_advisers = self._getAdvisers(active=True)
1✔
957
        not_active_advisers = [adv for adv in self._getAdvisers(active=False)
1✔
958
                               if adv not in active_advisers]
959
        for adviser in active_advisers:
1✔
960
            termTitle = self.adviser_term_title(adviser)
1✔
961
            res.append(SimpleTerm(adviser,
1✔
962
                                  adviser,
963
                                  safe_unicode(termTitle)))
964
        res = humansorted(res, key=attrgetter('title'))
1✔
965

966
        res_not_active = []
1✔
967
        for adviser in not_active_advisers:
1✔
968
            termTitle = self.adviser_term_title(adviser)
1✔
969
            termTitle = translate(
1✔
970
                u'${element_title} (Inactive)',
971
                domain='PloneMeeting',
972
                mapping={'element_title': termTitle},
973
                context=context.REQUEST)
974
            res_not_active.append(
1✔
975
                SimpleTerm(adviser,
976
                           adviser,
977
                           safe_unicode(termTitle)))
978

979
        res = res + humansorted(res_not_active, key=attrgetter('title'))
1✔
980
        return SimpleVocabulary(res)
1✔
981

982
    # do ram.cache have a different key name
983
    __call__ = AskedAdvicesVocabulary__call__
1✔
984

985

986
AskedAdvicesVocabularyFactory = AskedAdvicesVocabulary()
1✔
987

988

989
class ItemOptionalAdvicesVocabulary(object):
1✔
990
    implements(IVocabularyFactory)
1✔
991

992
    def __call___cachekey(method, self, context, include_selected=True, include_not_selectable_values=True):
1✔
993
        '''cachekey method for self.__call__.'''
994
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
995
        cfg = tool.getMeetingConfig(context)
1✔
996
        daa = self._getDelayAwareAdvisers(context, cfg)
1✔
997
        # first time, we init a cached value with include_not_selectable_values=True
998
        # and include_selected=False so it will be the base default vocabulary
999
        if not include_selected and include_not_selectable_values:
1✔
1000
            return repr(cfg), False, True, (), daa
1✔
1001
        # try to get common vocab, stored with active values
1002
        elif include_selected and include_not_selectable_values:
1✔
1003
            key = '%s.%s:%s' % (method.__module__, method.__name__, (repr(cfg), False, True))
1✔
1004
            vocab = store_in_cache(method).get(key)
1✔
1005
            if not vocab:
1✔
1006
                vocab = self(context, False, True)
1✔
1007
            # there are missing values
1008
            if set(context.getOptionalAdvisers()).difference([t.value for t in vocab._terms]):
1✔
1009
                return repr(cfg), True, True, context.getOptionalAdvisers(), daa
1✔
1010
            else:
1011
                # no missing values so we can use the default vocabulary
1012
                return repr(cfg), False, True, (), daa
1✔
1013
        else:
1014
            return repr(cfg), False, False, daa
1✔
1015

1016
    def _getDelayAwareAdvisers(self, context, cfg):
1✔
1017
        """Separated so it can be called in cachekey."""
1018
        # add delay-aware optionalAdvisers
1019
        # validity_date is used for customAdviser validaty (date from, date to)
1020
        validity_date = None
1✔
1021
        item = None
1✔
1022
        if context.meta_type == 'MeetingItem':
1✔
1023
            validity_date = context.created()
1✔
1024
            item = context
1✔
1025
        else:
1026
            validity_date = DateTime()
×
1027
        return cfg._optionalDelayAwareAdvisers(validity_date, item)
1✔
1028

1029
    @ram.cache(__call___cachekey)
1✔
1030
    def ItemOptionalAdvicesVocabulary__call__(self, context, include_selected=True, include_not_selectable_values=True):
1✔
1031
        """p_include_selected will make sure values selected on current context are
1032
           in the vocabulary.  Only relevant when context is a MeetingItem.
1033
           p_include_not_selectable_values will include the 'not_selectable_value_...' values,
1034
           useful for display only most of times."""
1035

1036
        request = context.REQUEST
1✔
1037

1038
        def _displayDelayAwareValue(delay_label, org_title, delay):
1✔
1039
            org_title = safe_unicode(org_title)
1✔
1040
            delay_label = safe_unicode(delay_label)
1✔
1041
            if delay_label:
1✔
1042
                value_to_display = translate('advice_delay_with_label',
1✔
1043
                                             domain='PloneMeeting',
1044
                                             mapping={'org_title': org_title,
1045
                                                      'delay': delay,
1046
                                                      'delay_label': delay_label},
1047
                                             default='${org_title} - ${delay} day(s) (${delay_label})',
1048
                                             context=request)
1049
            else:
1050
                value_to_display = translate('advice_delay_without_label',
1✔
1051
                                             domain='PloneMeeting',
1052
                                             mapping={'org_title': group_name,
1053
                                                      'delay': delay},
1054
                                             default='${org_title} - ${delay} day(s)',
1055
                                             context=request)
1056
            return value_to_display
1✔
1057

1058
        def _insert_term_and_users(res, term_value, term_title, add_users=True):
1✔
1059
            """ """
1060
            term = SimpleTerm(term_value, term_value, term_title)
1✔
1061
            term.sortable_title = term_title
1✔
1062
            res.append(term)
1✔
1063
            org_uid = term_value.split('__rowid__')[0]
1✔
1064
            if add_users:
1✔
1065
                user_ids = []
1✔
1066
                if org_uid in selectableAdviserUsers:
1✔
1067
                    user_ids += get_plone_group(org_uid, "advisers").getGroupMemberIds()
1✔
1068
                if include_selected:
1✔
1069
                    # manage missing user ids here so term is grouped with the org term
1070
                    prefix = term_value + '__userid__'
1✔
1071
                    missing_user_ids = [oa.replace(prefix, '') for oa in context.getOptionalAdvisers()
1✔
1072
                                        if oa.startswith(prefix)]
1073
                    user_ids += missing_user_ids
1✔
1074
                # manage users in a separate list so we sort it before appending to global res
1075
                res_users = []
1✔
1076
                for user_id in user_ids:
1✔
1077
                    user_term_value = "{0}__userid__{1}".format(term_value, user_id)
1✔
1078
                    user_title = get_user_fullname(user_id)
1✔
1079
                    user_term = SimpleTerm(user_term_value, user_term_value, user_title)
1✔
1080
                    user_term.sortable_title = u"{0} ({1})".format(term_title, user_title)
1✔
1081
                    res_users.append(user_term)
1✔
1082
                res_users = humansorted(res_users, key=attrgetter('title'))
1✔
1083
                res += res_users
1✔
1084
            return
1✔
1085

1086
        def _getNonDelayAwareAdvisers(cfg):
1✔
1087
            """Separated so it can be cached."""
1088
            resNonDelayAwareAdvisers = []
1✔
1089
            selectableAdviserOrgs = uuidsToObjects(
1✔
1090
                cfg.getSelectableAdvisers(), ordered=True, unrestricted=True)
1091
            for org in selectableAdviserOrgs:
1✔
1092
                _insert_term_and_users(
1✔
1093
                    resNonDelayAwareAdvisers, org.UID(), org.get_full_title())
1094
            return resNonDelayAwareAdvisers
1✔
1095

1096
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1097
        cfg = tool.getMeetingConfig(context)
1✔
1098
        selectableAdviserUsers = cfg.getSelectableAdviserUsers()
1✔
1099
        resDelayAwareAdvisers = []
1✔
1100
        delayAwareAdvisers = self._getDelayAwareAdvisers(context, cfg)
1✔
1101
        # a delay-aware adviser has a special id so we can handle it specifically after
1102
        for delayAwareAdviser in delayAwareAdvisers:
1✔
1103
            adviserId = "%s__rowid__%s" % \
1✔
1104
                        (delayAwareAdviser['org_uid'],
1105
                         delayAwareAdviser['row_id'])
1106
            delay = delayAwareAdviser['delay']
1✔
1107
            delay_label = delayAwareAdviser['delay_label']
1✔
1108
            group_name = delayAwareAdviser['org_title']
1✔
1109
            value_to_display = _displayDelayAwareValue(delay_label, group_name, delay)
1✔
1110
            _insert_term_and_users(
1✔
1111
                resDelayAwareAdvisers, adviserId, value_to_display)
1112

1113
        # _getNonDelayAwareAdvisers uses ram.cache, create a new list
1114
        resNonDelayAwareAdvisers = list(_getNonDelayAwareAdvisers(cfg))
1✔
1115

1116
        # make sure optionalAdvisers actually stored have their corresponding
1117
        # term in the vocabulary, if not, add it
1118
        if include_selected:
1✔
1119
            optionalAdvisers = context.getOptionalAdvisers()
1✔
1120
            if optionalAdvisers:
1✔
1121
                optionalAdvisersInVocab = [org_infos.token for org_infos in resNonDelayAwareAdvisers] + \
1✔
1122
                                          [org_infos.token for org_infos in resDelayAwareAdvisers]
1123
                for optionalAdviser in optionalAdvisers:
1✔
1124
                    if optionalAdviser not in optionalAdvisersInVocab:
1✔
1125
                        user_id = org = None
1✔
1126
                        org_uid = optionalAdviser
1✔
1127
                        if '__userid__' in optionalAdviser:
1✔
1128
                            org_uid, user_id = optionalAdviser.split('__userid__')
1✔
1129
                        if '__rowid__' in optionalAdviser:
1✔
1130
                            org_uid, row_id = decodeDelayAwareId(org_uid)
1✔
1131
                            delay = cfg._dataForCustomAdviserRowId(row_id)['delay']
1✔
1132
                            delay_label = context.adviceIndex[org_uid]['delay_label']
1✔
1133
                            org = get_organization(org_uid)
1✔
1134
                            if not org:
1✔
1135
                                continue
×
1136
                            value_to_display = _displayDelayAwareValue(
1✔
1137
                                delay_label, org.get_full_title(), delay)
1138
                            if not user_id:
1✔
1139
                                _insert_term_and_users(
1✔
1140
                                    resDelayAwareAdvisers,
1141
                                    optionalAdviser,
1142
                                    value_to_display,
1143
                                    add_users=False)
1144
                        else:
1145
                            org = get_organization(org_uid)
1✔
1146
                            if not org:
1✔
1147
                                continue
×
1148
                            if not user_id:
1✔
1149
                                _insert_term_and_users(
1✔
1150
                                    resNonDelayAwareAdvisers,
1151
                                    optionalAdviser,
1152
                                    org.get_full_title(),
1153
                                    add_users=False)
1154
                        # it is a userid, add a special value including the org title
1155
                        if org and user_id:
1✔
1156
                            user_term_title = u"{0} ({1})".format(
1✔
1157
                                org.get_full_title(), get_user_fullname(user_id))
1158
                            user_term = SimpleTerm(optionalAdviser, optionalAdviser, user_term_title)
1✔
1159
                            user_term.sortable_title = user_term_title
1✔
1160
                            resDelayAwareAdvisers.append(user_term)
1✔
1161

1162
        # now create the listing
1163
        # sort elements by value before potentially prepending a special value here under
1164
        # for delay-aware advisers, the order is defined in the configuration, so we do not .sortedByValue()
1165
        resNonDelayAwareAdvisers = humansorted(resNonDelayAwareAdvisers, key=attrgetter('sortable_title'))
1✔
1166

1167
        # we add a special value at the beginning of the vocabulary
1168
        # if we have delay-aware advisers
1169
        if resDelayAwareAdvisers:
1✔
1170
            delay_aware_optional_advisers_msg = translate('delay_aware_optional_advisers_term',
1✔
1171
                                                          domain='PloneMeeting',
1172
                                                          context=request)
1173
            resDelayAwareAdvisers.insert(
1✔
1174
                0, SimpleTerm('not_selectable_value_delay_aware_optional_advisers',
1175
                              'not_selectable_value_delay_aware_optional_advisers',
1176
                              delay_aware_optional_advisers_msg))
1177

1178
            # if we have delay-aware advisers, we add another special value
1179
            # that explain that under are 'normal' optional advisers
1180
            if resNonDelayAwareAdvisers:
1✔
1181
                non_delay_aware_optional_advisers_msg = translate(
1✔
1182
                    'non_delay_aware_optional_advisers_term',
1183
                    domain='PloneMeeting',
1184
                    context=request)
1185
                resNonDelayAwareAdvisers.insert(
1✔
1186
                    0, SimpleTerm('not_selectable_value_non_delay_aware_optional_advisers',
1187
                                  'not_selectable_value_non_delay_aware_optional_advisers',
1188
                                  non_delay_aware_optional_advisers_msg))
1189
        return SimpleVocabulary(resDelayAwareAdvisers + resNonDelayAwareAdvisers)
1✔
1190

1191
    # do ram.cache have a different key name
1192
    __call__ = ItemOptionalAdvicesVocabulary__call__
1✔
1193

1194

1195
ItemOptionalAdvicesVocabularyFactory = ItemOptionalAdvicesVocabulary()
1✔
1196

1197

1198
class AdviceTypesVocabulary(object):
1✔
1199
    implements(IVocabularyFactory)
1✔
1200

1201
    def __call___cachekey(method, self, context):
1✔
1202
        '''cachekey method for self.__call__.'''
1203
        date = get_cachekey_volatile('Products.PloneMeeting.vocabularies.advicetypesvocabulary')
1✔
1204
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1205
        cfg = tool.getMeetingConfig(context)
1✔
1206
        return date, repr(cfg)
1✔
1207

1208
    @ram.cache(__call___cachekey)
1✔
1209
    def AdviceTypesVocabulary__call__(self, context):
×
1210
        """ """
1211
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1212
        cfg = tool.getMeetingConfig(context)
1✔
1213
        res = []
1✔
1214
        # add the 'not_given' advice_type
1215
        res.append(SimpleTerm(NOT_GIVEN_ADVICE_VALUE,
1✔
1216
                              NOT_GIVEN_ADVICE_VALUE,
1217
                              translate(NOT_GIVEN_ADVICE_VALUE,
1218
                                        domain='PloneMeeting',
1219
                                        context=context.REQUEST))
1220
                   )
1221
        # add the 'asked_again' advice_type
1222
        res.append(SimpleTerm("asked_again",
1✔
1223
                              "asked_again",
1224
                              translate("asked_again",
1225
                                        domain='PloneMeeting',
1226
                                        context=context.REQUEST))
1227
                   )
1228
        for advice_type in cfg.getUsedAdviceTypes():
1✔
1229
            res.append(SimpleTerm(advice_type,
1✔
1230
                                  advice_type,
1231
                                  translate(advice_type,
1232
                                            domain='PloneMeeting',
1233
                                            context=context.REQUEST))
1234
                       )
1235
        # finally add the 'hidden_during_redaction' and
1236
        # 'considered_not_given_hidden_during_redaction' advice_types
1237
        res.append(SimpleTerm(HIDDEN_DURING_REDACTION_ADVICE_VALUE,
1✔
1238
                              HIDDEN_DURING_REDACTION_ADVICE_VALUE,
1239
                              translate(HIDDEN_DURING_REDACTION_ADVICE_VALUE,
1240
                                        domain='PloneMeeting',
1241
                                        context=context.REQUEST))
1242
                   )
1243
        res.append(SimpleTerm(CONSIDERED_NOT_GIVEN_ADVICE_VALUE,
1✔
1244
                              CONSIDERED_NOT_GIVEN_ADVICE_VALUE,
1245
                              translate(CONSIDERED_NOT_GIVEN_ADVICE_VALUE,
1246
                                        domain='PloneMeeting',
1247
                                        context=context.REQUEST))
1248
                   )
1249
        return SimpleVocabulary(res)
1✔
1250

1251
    # do ram.cache have a different key name
1252
    __call__ = AdviceTypesVocabulary__call__
1✔
1253

1254

1255
AdviceTypesVocabularyFactory = AdviceTypesVocabulary()
1✔
1256

1257

1258
class SentToInfosVocabulary(object):
1✔
1259
    implements(IVocabularyFactory)
1✔
1260

1261
    def __call__(self, context):
1✔
1262
        """ """
1263
        res = []
1✔
1264
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1265
        cfg = tool.getMeetingConfig(context)
1✔
1266
        # the 'not to be cloned anywhere' term
1267
        res.append(SimpleTerm('not_to_be_cloned_to',
1✔
1268
                              'not_to_be_cloned_to',
1269
                              safe_unicode(translate('not_to_be_cloned_to_term',
1270
                                                     domain='PloneMeeting',
1271
                                                     context=context.REQUEST)))
1272
                   )
1273
        for cfgInfo in cfg.getMeetingConfigsToCloneTo():
1✔
1274
            cfgId = cfgInfo['meeting_config']
1✔
1275
            cfgTitle = getattr(tool, cfgId).Title()
1✔
1276
            # add 'clonable to' and 'cloned to' options
1277
            for suffix in ('__clonable_to', '__clonable_to_emergency',
1✔
1278
                           '__cloned_to', '__cloned_to_emergency'):
1279
                termId = cfgId + suffix
1✔
1280
                res.append(SimpleTerm(termId,
1✔
1281
                                      termId,
1282
                                      translate('sent_to_other_mc_term' + suffix,
1283
                                                mapping={'meetingConfigTitle': safe_unicode(cfgTitle)},
1284
                                                domain='PloneMeeting',
1285
                                                context=context.REQUEST))
1286
                           )
1287
        return SimpleVocabulary(res)
1✔
1288

1289

1290
SentToInfosVocabularyFactory = SentToInfosVocabulary()
1✔
1291

1292

1293
class FacetedAnnexesVocabulary(object):
1✔
1294
    implements(IVocabularyFactory)
1✔
1295

1296
    def __call__(self, context):
1✔
1297
        """ """
1298
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1299
        cfg = tool.getMeetingConfig(context)
1✔
1300
        annexes_configs = [annex_config for annex_config in
1✔
1301
                           cfg.annexes_types.objectValues()]
1302
        config = OrderedDict([
1✔
1303
            ('to_be_printed_activated', ("to_print", "not_to_print")),
1304
            ('confidentiality_activated', ("confidential", "not_confidential")),
1305
            ('publishable_activated', ("publishable", "not_publishable")),
1306
            ('signed_activated', ("to_sign", "not_to_sign", "signed"))])
1307
        res = []
1✔
1308
        values_enabled = []
1✔
1309
        for k, values in config.items():
1✔
1310
            for annexes_config in annexes_configs:
1✔
1311
                if getattr(annexes_config, k, False) is True:
1✔
1312
                    for value in values:
1✔
1313
                        if value not in values_enabled:
1✔
1314
                            values_enabled.append(value)
1✔
1315
        for value in values_enabled:
1✔
1316
            res.append(SimpleTerm(
1✔
1317
                value,
1318
                value,
1319
                translate('annex_term_{0}'.format(value),
1320
                          domain='PloneMeeting',
1321
                          context=context.REQUEST)))
1322
        return SimpleVocabulary(res)
1✔
1323

1324

1325
FacetedAnnexesVocabularyFactory = FacetedAnnexesVocabulary()
1✔
1326

1327

1328
class BooleanForFacetedVocabulary(object):
1✔
1329
    implements(IVocabularyFactory)
1✔
1330

1331
    def __call__(self, context, prefix=''):
1✔
1332
        """ """
1333
        res = []
×
1334
        res.append(SimpleTerm(prefix + '0',
×
1335
                              prefix + '0',
1336
                              safe_unicode(translate('boolean_value_false',
1337
                                                     domain='PloneMeeting',
1338
                                                     context=context.REQUEST)))
1339
                   )
1340
        res.append(SimpleTerm(prefix + '1',
×
1341
                              prefix + '1',
1342
                              safe_unicode(translate('boolean_value_true',
1343
                                                     domain='PloneMeeting',
1344
                                                     context=context.REQUEST)))
1345
                   )
1346
        return SimpleVocabulary(res)
×
1347

1348

1349
BooleanForFacetedVocabularyFactory = BooleanForFacetedVocabulary()
1✔
1350

1351

1352
class DownOrUpWorkflowAgainVocabulary(object):
1✔
1353
    implements(IVocabularyFactory)
1✔
1354

1355
    def __call__(self, context):
1✔
1356
        """ """
1357
        res = []
×
1358
        res.append(SimpleTerm('down',
×
1359
                              'down',
1360
                              safe_unicode(translate('item_down_wf_term',
1361
                                                     domain='PloneMeeting',
1362
                                                     context=context.REQUEST)))
1363
                   )
1364
        res.append(SimpleTerm('up',
×
1365
                              'up',
1366
                              safe_unicode(translate('item_up_wf_term',
1367
                                                     domain='PloneMeeting',
1368
                                                     context=context.REQUEST)))
1369
                   )
1370

1371
        return SimpleVocabulary(res)
×
1372

1373

1374
DownOrUpWorkflowAgainVocabularyFactory = DownOrUpWorkflowAgainVocabulary()
1✔
1375

1376

1377
class YearlyInitMeetingNumbersVocabulary(object):
1✔
1378
    implements(IVocabularyFactory)
1✔
1379

1380
    def __call__(self, context):
1✔
1381
        """ """
1382
        res = []
1✔
1383
        res.append(SimpleTerm('meeting_number',
1✔
1384
                              'meeting_number',
1385
                              safe_unicode(translate('title_meeting_number',
1386
                                                     domain='PloneMeeting',
1387
                                                     context=context.REQUEST)))
1388
                   )
1389
        res.append(SimpleTerm('first_item_number',
1✔
1390
                              'first_item_number',
1391
                              safe_unicode(translate('title_first_item_number',
1392
                                                     domain='PloneMeeting',
1393
                                                     context=context.REQUEST)))
1394
                   )
1395

1396
        return SimpleVocabulary(res)
1✔
1397

1398

1399
YearlyInitMeetingNumbersVocabularyFactory = YearlyInitMeetingNumbersVocabulary()
1✔
1400

1401

1402
class ListTypesVocabulary(object):
1✔
1403
    implements(IVocabularyFactory)
1✔
1404

1405
    def __call__(self, context):
1✔
1406
        """ """
1407
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1408
        cfg = tool.getMeetingConfig(context)
1✔
1409
        res = []
1✔
1410
        for listType in cfg.getListTypes():
1✔
1411
            res.append(SimpleTerm(listType['identifier'],
1✔
1412
                                  listType['identifier'],
1413
                                  translate(safe_unicode(listType['label']),
1414
                                            domain='PloneMeeting',
1415
                                            context=context.REQUEST))
1416
                       )
1417
        return SimpleVocabulary(res)
1✔
1418

1419

1420
ListTypesVocabularyFactory = ListTypesVocabulary()
1✔
1421

1422

1423
class UsedVoteValuesVocabulary(object):
1✔
1424
    implements(IVocabularyFactory)
1✔
1425

1426
    def is_first_linked_vote(self, vote_number):
1✔
1427
        """ """
1428
        itemVotes = self.context.get_item_votes()
1✔
1429
        return next_vote_is_linked(itemVotes, vote_number)
1✔
1430

1431
    def is_linked_vote(self):
1✔
1432
        """ """
1433
        return self.item_vote['linked_to_previous']
1✔
1434

1435
    def __call__(self, context, vote_number=None):
1✔
1436
        """ """
1437

1438
        # as used in a datagridfield, context may vary...
1439
        self.context = get_context_with_request(context)
1✔
1440

1441
        # caching as called too much times by datagridfield...
1442
        key = "PloneMeeting-vocabularies-UsedVoteValuesVocabulary-{0}-{1}".format(
1✔
1443
            repr(self.context), vote_number)
1444
        cache = IAnnotations(self.context.REQUEST)
1✔
1445
        vocab = cache.get(key, None)
1✔
1446
        if vocab is None:
1✔
1447
            tool = api.portal.get_tool('portal_plonemeeting')
1✔
1448
            cfg = tool.getMeetingConfig(self.context)
1✔
1449
            res = []
1✔
1450
            # get vote_number, as _voter_number when editing
1451
            # as form.widgets.vote_number when saving
1452
            if vote_number is None:
1✔
1453
                vote_number = int(self.context.REQUEST.form.get(
1✔
1454
                    'vote_number',
1455
                    self.context.REQUEST.form.get('form.widgets.vote_number')))
1456
            self.item_vote = self.context.get_item_votes(vote_number=vote_number)
1✔
1457
            used_values_attr = 'usedVoteValues'
1✔
1458
            if self.is_linked_vote():
1✔
1459
                used_values_attr = 'nextLinkedVotesUsedVoteValues'
1✔
1460
            elif self.is_first_linked_vote(vote_number):
1✔
1461
                used_values_attr = 'firstLinkedVoteUsedVoteValues'
×
1462
            for usedVoteValue in cfg.getUsedVoteValues(
1✔
1463
                    used_values_attr=used_values_attr,
1464
                    include_not_encoded=not self.context.get_vote_is_secret(vote_number)):
1465
                res.append(
1✔
1466
                    SimpleTerm(
1467
                        usedVoteValue,
1468
                        usedVoteValue,
1469
                        translate(
1470
                            'vote_value_{0}'.format(usedVoteValue),
1471
                            domain='PloneMeeting',
1472
                            context=self.context.REQUEST)))
1473
            vocab = SimpleVocabulary(res)
1✔
1474
            cache[key] = vocab
1✔
1475
        return vocab
1✔
1476

1477

1478
UsedVoteValuesVocabularyFactory = UsedVoteValuesVocabulary()
1✔
1479

1480

1481
class SelectablePrivaciesVocabulary(object):
1✔
1482
    implements(IVocabularyFactory)
1✔
1483

1484
    def __call__(self, context):
1✔
1485
        """ """
1486
        res = []
1✔
1487
        keys = ['public_heading', 'public', 'secret_heading', 'secret']
1✔
1488
        for key in keys:
1✔
1489
            res.append(SimpleTerm(
1✔
1490
                key,
1491
                key,
1492
                safe_unicode(translate(key,
1493
                                       domain='PloneMeeting',
1494
                                       context=context.REQUEST))))
1495

1496
        return SimpleVocabulary(res)
1✔
1497

1498

1499
SelectablePrivaciesVocabularyFactory = SelectablePrivaciesVocabulary()
1✔
1500

1501

1502
class PrivaciesVocabulary(object):
1✔
1503
    implements(IVocabularyFactory)
1✔
1504

1505
    def __call__(self, context):
1✔
1506
        """ """
1507
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1508
        cfg = tool.getMeetingConfig(context)
1✔
1509
        res = []
1✔
1510
        keys = cfg.getSelectablePrivacies()
1✔
1511
        for key in keys:
1✔
1512
            res.append(SimpleTerm(
1✔
1513
                key,
1514
                key,
1515
                safe_unicode(translate(key,
1516
                                       domain='PloneMeeting',
1517
                                       context=context.REQUEST))))
1518
        return SimpleVocabulary(res)
1✔
1519

1520

1521
PrivaciesVocabularyFactory = PrivaciesVocabulary()
1✔
1522

1523

1524
class PollTypesVocabulary(object):
1✔
1525
    implements(IVocabularyFactory)
1✔
1526

1527
    def __call__(self, context):
1✔
1528
        """ """
1529
        res = []
1✔
1530
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1531
        cfg = tool.getMeetingConfig(context)
1✔
1532
        usedPollTypes = list(cfg.getUsedPollTypes())
1✔
1533
        # if on an item, include values not unselected in config
1534
        if context.meta_type == 'MeetingItem' and context.getPollType() not in usedPollTypes:
1✔
1535
            usedPollTypes.append(context.getPollType())
×
1536

1537
        for usedPollType in usedPollTypes:
1✔
1538
            res.append(SimpleTerm(usedPollType,
1✔
1539
                                  usedPollType,
1540
                                  safe_unicode(translate("polltype_{0}".format(usedPollType),
1541
                                                         domain='PloneMeeting',
1542
                                                         context=context.REQUEST))))
1543
        return SimpleVocabulary(res)
1✔
1544

1545

1546
PollTypesVocabularyFactory = PollTypesVocabulary()
1✔
1547

1548

1549
class StorePodTemplateAsAnnexVocabulary(object):
1✔
1550
    """
1551
    Vocabulary factory for 'ConfigurablePodTemplate.store_as_annex' field.
1552
    """
1553
    implements(IVocabularyFactory)
1✔
1554

1555
    def __call__(self, context):
1✔
1556
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1557
        cfg = tool.getMeetingConfig(context)
1✔
1558
        res = []
1✔
1559
        # do not fail when displaying the schema in the dexterity types control panel
1560
        if not cfg:
1✔
1561
            return SimpleVocabulary(res)
×
1562

1563
        for annexes_group in cfg.annexes_types.objectValues():
1✔
1564
            for cat in annexes_group.objectValues():
1✔
1565
                res.append(SimpleTerm(
1✔
1566
                    cat.UID(),
1567
                    cat.UID(),
1568
                    u'{0} âž” {1}'.format(
1569
                        safe_unicode(annexes_group.Title()),
1570
                        safe_unicode(cat.Title()))))
1571
                for subcat in cat.objectValues():
1✔
1572
                    res.append(SimpleTerm(
1✔
1573
                        subcat.UID(),
1574
                        subcat.UID(),
1575
                        u'{0} âž” {1} âž” {2}'.format(
1576
                            safe_unicode(annexes_group.Title()),
1577
                            safe_unicode(cat.Title()),
1578
                            safe_unicode(subcat.Title()))))
1579
        return SimpleVocabulary(res)
1✔
1580

1581

1582
StorePodTemplateAsAnnexVocabularyFactory = StorePodTemplateAsAnnexVocabulary()
1✔
1583

1584

1585
class ItemTemplatesStorableAsAnnexVocabulary(object):
1✔
1586
    implements(IVocabularyFactory)
1✔
1587

1588
    def __call__(self, context):
1✔
1589
        """ """
1590
        res = []
1✔
1591
        # get every POD templates that have a defined 'store_as_annex'
1592
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1593
        cfg = tool.getMeetingConfig(context)
1✔
1594
        meetingItemTemplatesToStoreAsAnnex = cfg.getMeetingItemTemplatesToStoreAsAnnex()
1✔
1595
        for pod_template in cfg.podtemplates.objectValues():
1✔
1596
            store_as_annex = getattr(pod_template, 'store_as_annex', None)
1✔
1597
            if store_as_annex:
1✔
1598
                annex_type = uuidToObject(store_as_annex, unrestricted=True)
1✔
1599
                annex_group_title = annex_type.get_category_group().Title()
1✔
1600
                for output_format in pod_template.pod_formats:
1✔
1601
                    term_id = '{0}__output_format__{1}'.format(
1✔
1602
                        pod_template.getId(), output_format)
1603
                    # when called on another context than MeetingConfig
1604
                    # only keep meetingItemTemplatesToStoreAsAnnex
1605
                    if context.portal_type != 'MeetingConfig' and \
1✔
1606
                       term_id not in meetingItemTemplatesToStoreAsAnnex:
1607
                        continue
×
1608
                    res.append(SimpleTerm(
1✔
1609
                        term_id,
1610
                        term_id,
1611
                        u'{0} ({1} / {2})'.format(
1612
                            safe_unicode(pod_template.Title()),
1613
                            output_format,
1614
                            u'{0} âž” {1}'.format(
1615
                                safe_unicode(annex_group_title),
1616
                                safe_unicode(annex_type.Title())))))
1617
        return SimpleVocabulary(res)
1✔
1618

1619

1620
ItemTemplatesStorableAsAnnexVocabularyFactory = ItemTemplatesStorableAsAnnexVocabulary()
1✔
1621

1622

1623
class PMPortalTypesVocabulary(PortalTypesVocabularyFactory):
1✔
1624
    """
1625
    Vocabulary factory for 'pod_portal_types' field, make it MeetingConfig aware.
1626
    """
1627
    implements(IVocabularyFactory)
1✔
1628

1629
    def __call__(self, context):
1✔
1630
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1631
        cfg = tool.getMeetingConfig(context)
1✔
1632
        res = []
1✔
1633
        if cfg:
1✔
1634
            # available for item, meeting and advice
1635
            itemTypeName = cfg.getItemTypeName()
1✔
1636
            res.append(SimpleTerm(itemTypeName,
1✔
1637
                                  itemTypeName,
1638
                                  translate(itemTypeName,
1639
                                            domain="plone",
1640
                                            context=context.REQUEST)))
1641
            meetingTypeName = cfg.getMeetingTypeName()
1✔
1642
            res.append(SimpleTerm(meetingTypeName,
1✔
1643
                                  meetingTypeName,
1644
                                  translate(meetingTypeName,
1645
                                            domain="plone",
1646
                                            context=context.REQUEST)))
1647
            # manage multiple 'meetingadvice' portal_types
1648
            for portal_type in getAdvicePortalTypes():
1✔
1649
                res.append(SimpleTerm(portal_type.id,
1✔
1650
                                      portal_type.id,
1651
                                      translate(portal_type.title,
1652
                                                domain="PloneMeeting",
1653
                                                context=context.REQUEST)))
1654
            return SimpleVocabulary(res)
1✔
1655
        else:
1656
            return super(PMPortalTypesVocabulary, self).__call__(context)
1✔
1657

1658

1659
PMPortalTypesVocabularyFactory = PMPortalTypesVocabulary()
1✔
1660

1661

1662
class PMExistingPODTemplate(ExistingPODTemplateFactory):
1✔
1663
    """
1664
    Vocabulary factory for 'pod_template_to_use' field, include MeetingConfig title in term.
1665
    """
1666
    implements(IVocabularyFactory)
1✔
1667

1668
    def _renderTermTitle(self, brain):
1✔
1669
        """If template in podtemplates folder of a MeetingConfig,
1670
           include MeetingConfig title (2 levels above), else include parent title.
1671
           This could be a template stored in "contacts" or somewhere else."""
1672
        template = brain.getObject()
1✔
1673
        if template.aq_inner.aq_parent.id == "podtemplates":
1✔
1674
            parent_title = template.aq_inner.aq_parent.aq_parent.Title(
1✔
1675
                include_config_group=True)
1676
        else:
1677
            parent_title = template.aq_inner.aq_parent.Title()
×
1678
        return u'{} âž” {} âž” {}'.format(
1✔
1679
            safe_unicode(parent_title),
1680
            safe_unicode(template.Title()),
1681
            safe_unicode(template.odt_file.filename))
1682

1683

1684
PMExistingPODTemplateFactory = PMExistingPODTemplate()
1✔
1685

1686

1687
class PMStyleTemplatesVocabulary(StyleTemplatesVocabularyFactory):
1✔
1688
    """
1689
    Override to display the MeetingConfig title in the term title as
1690
    style templates are useable cross MetingConfigs.
1691
    """
1692
    implements(IVocabularyFactory)
1✔
1693

1694
    def _renderTermTitle(self, brain):
1✔
1695
        obj = brain.getObject()
1✔
1696
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1697
        cfg = tool.getMeetingConfig(obj)
1✔
1698
        return '{0} ({1})'.format(brain.Title, cfg.Title())
1✔
1699

1700

1701
PMStyleTemplatesVocabularyFactory = PMStyleTemplatesVocabulary()
1✔
1702

1703

1704
class PMDashboardCollectionsVocabulary(DashboardCollectionsVocabulary):
1✔
1705
    """
1706
    Vocabulary factory for 'dashboard_collections' field of DashboardPODTemplate.
1707
    """
1708

1709
    implements(IVocabularyFactory)
1✔
1710

1711
    def __call__(self, context):
1✔
1712
        catalog = api.portal.get_tool('portal_catalog')
1✔
1713
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1714
        cfg = tool.getMeetingConfig(context)
1✔
1715
        query = {'object_provides': {}}
1✔
1716
        query['object_provides']['query'] = IDashboardCollection.__identifier__
1✔
1717
        if cfg:
1✔
1718
            query['path'] = {'query': '/'.join(cfg.getPhysicalPath())}
1✔
1719
            query['sort_on'] = 'sortable_title'
1✔
1720
        else:
1721
            # out of a MeetingConfig
1722
            query['getConfigId'] = EMPTY_STRING
1✔
1723
        collection_brains = catalog.unrestrictedSearchResults(**query)
1✔
1724
        vocabulary = SimpleVocabulary(
1✔
1725
            [SimpleTerm(b.UID, b.UID, b.Title) for b in collection_brains]
1726
        )
1727
        return vocabulary
1✔
1728

1729

1730
PMDashboardCollectionsVocabularyFactory = PMDashboardCollectionsVocabulary()
1✔
1731

1732

1733
class PMCategoryVocabulary(CategoryVocabulary):
1✔
1734
    """Override to take into account field 'only_for_meeting_managers' on the category
1735
       for annexes added on items."""
1736

1737
    def __call___cachekey(method, self, context, use_category_uid_as_token=False, only_enabled=True):
1✔
1738
        '''cachekey method for self.__call__.'''
1739
        annex_config = get_config_root(context)
1✔
1740
        annex_group = get_group(annex_config, context)
1✔
1741
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1742
        cfg = tool.getMeetingConfig(context)
1✔
1743
        # cfg may be None when using the quickupload portlet outside of PloneMeeting
1744
        # like in a "Documents" folder at the root of the site, but the quickupload
1745
        # form is initialized with content_category field
1746
        cfg_modified = isManager = None
1✔
1747
        if cfg is not None:
1✔
1748
            isManager = tool.isManager(cfg)
1✔
1749
            # when a ContentCategory is added/edited/removed, the MeetingConfig is modified
1750
            cfg_modified = cfg.modified()
1✔
1751
        # we do not cache per context as we manage missing terms using an adapter
1752
        return annex_group.getId(), isManager, use_category_uid_as_token, cfg_modified, only_enabled
1✔
1753

1754
    @ram.cache(__call___cachekey)
1✔
1755
    def PMCategoryVocabulary__call__(self, context, use_category_uid_as_token=False, only_enabled=True):
1✔
1756
        return super(PMCategoryVocabulary, self).__call__(
1✔
1757
            context,
1758
            use_category_uid_as_token=use_category_uid_as_token,
1759
            only_enabled=only_enabled)
1760

1761
    # do ram.cache have a different key name
1762
    __call__ = PMCategoryVocabulary__call__
1✔
1763

1764
    def _get_categories(self, context, only_enabled=True):
1✔
1765
        """ """
1766
        categories = super(PMCategoryVocabulary, self)._get_categories(
1✔
1767
            context, only_enabled=only_enabled)
1768
        # filter container on only_for_meeting_managers if it is an item
1769
        container = context
1✔
1770
        if IAnnex.providedBy(context):
1✔
1771
            container = context.aq_parent
1✔
1772
        if container.__class__.__name__ == "MeetingItem":
1✔
1773
            tool = api.portal.get_tool('portal_plonemeeting')
1✔
1774
            cfg = tool.getMeetingConfig(context)
1✔
1775
            isManager = tool.isManager(cfg)
1✔
1776
            categories = [cat for cat in categories if
1✔
1777
                          not cat.only_for_meeting_managers or isManager]
1778
        return categories
1✔
1779

1780
    def _get_subcategories(self, context, category, only_enabled=True):
1✔
1781
        """Return subcategories for given category.
1782
           This needs to return a list of subcategory brains."""
1783
        subcategories = super(PMCategoryVocabulary, self)._get_subcategories(
1✔
1784
            context, category, only_enabled=only_enabled)
1785
        # filter container on only_for_meeting_managers if it is an item
1786
        container = context
1✔
1787
        if IAnnex.providedBy(context):
1✔
1788
            container = context.aq_parent
1✔
1789
        if container.__class__.__name__ == "MeetingItem":
1✔
1790
            tool = api.portal.get_tool('portal_plonemeeting')
1✔
1791
            cfg = tool.getMeetingConfig(context)
1✔
1792
            isManager = tool.isManager(cfg)
1✔
1793
            tmp = []
1✔
1794
            for subcat_brain in subcategories:
1✔
1795
                if not isManager:
1✔
1796
                    subcat = subcat_brain.getObject()
1✔
1797
                    if subcat.only_for_meeting_managers:
1✔
1798
                        continue
1✔
1799
                tmp.append(subcat_brain)
1✔
1800
            subcategories = tmp
1✔
1801
        return subcategories
1✔
1802

1803

1804
class PMCategoryTitleVocabulary(CategoryTitleVocabulary, PMCategoryVocabulary):
1✔
1805
    """Override to use same _get_categories as PMCategoryVocabulary."""
1806

1807
    def __call___cachekey(method, self, context, only_enabled=True):
1✔
1808
        '''cachekey method for self.__call__.'''
1809
        annex_config = get_config_root(context)
1✔
1810
        annex_group = get_group(annex_config, context)
1✔
1811
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1812
        cfg = tool.getMeetingConfig(context)
1✔
1813
        # cfg may be None when using the quickupload portlet outside of PloneMeeting
1814
        # like in a "Documents" folder at the root of the site, but the quickupload
1815
        # form is initialized with content_category field
1816
        cfg_modified = isManager = None
1✔
1817
        if cfg is not None:
1✔
1818
            isManager = tool.isManager(cfg)
1✔
1819
            # when a ContentCategory is added/edited/removed, the MeetingConfig is modified
1820
            cfg_modified = cfg.modified()
1✔
1821
        # we do not cache per context as we manage missing terms using an adapter
1822
        return annex_group.getId(), isManager, cfg_modified, only_enabled
1✔
1823

1824
    @ram.cache(__call___cachekey)
1✔
1825
    def PMCategoryTitleVocabulary__call__(self, context, only_enabled=True):
1✔
1826
        return super(PMCategoryTitleVocabulary, self).__call__(
1✔
1827
            context,
1828
            only_enabled=only_enabled)
1829

1830
    # do ram.cache have a different key name
1831
    __call__ = PMCategoryTitleVocabulary__call__
1✔
1832

1833

1834
class PMEveryCategoryVocabulary(EveryCategoryVocabulary):
1✔
1835
    """Override to add ram.cache."""
1836

1837
    def __call___cachekey(method, self, context, use_category_uid_as_token=False, only_enabled=False):
1✔
1838
        '''cachekey method for self.__call__.'''
1839
        annex_config = get_config_root(context)
×
1840
        annex_group = get_group(annex_config, context)
×
1841
        tool = api.portal.get_tool('portal_plonemeeting')
×
1842
        cfg = tool.getMeetingConfig(context)
×
1843
        # when a ContentCategory is added/edited/removed, the MeetingConfig is modified
1844
        cfg_modified = cfg.modified()
×
1845
        return annex_group.getId(), use_category_uid_as_token, cfg_modified, only_enabled
×
1846

1847
    @ram.cache(__call___cachekey)
1✔
1848
    def PMEveryCategoryVocabulary__call__(
1✔
1849
            self, context, use_category_uid_as_token=False, only_enabled=False):
1850
        return super(PMEveryCategoryVocabulary, self).__call__(
×
1851
            context,
1852
            use_category_uid_as_token=use_category_uid_as_token,
1853
            only_enabled=only_enabled)
1854

1855
    # do ram.cache have a different key name
1856
    __call__ = PMEveryCategoryVocabulary__call__
1✔
1857

1858

1859
class PMEveryCategoryTitleVocabulary(EveryCategoryTitleVocabulary):
1✔
1860
    """Override to add ram.cache."""
1861

1862
    def __call___cachekey(method, self, context, use_category_uid_as_token=False, only_enabled=False):
1✔
1863
        '''cachekey method for self.__call__.'''
1864
        annex_config = get_config_root(context)
×
1865
        annex_group = get_group(annex_config, context)
×
1866
        tool = api.portal.get_tool('portal_plonemeeting')
×
1867
        cfg = tool.getMeetingConfig(context)
×
1868
        # when a ContentCategory is added/edited/removed, the MeetingConfig is modified
1869
        cfg_modified = cfg.modified()
×
1870
        return annex_group.getId(), use_category_uid_as_token, cfg_modified, only_enabled
×
1871

1872
    @ram.cache(__call___cachekey)
1✔
1873
    def PMEveryCategoryTitleVocabulary__call__(self, context, only_enabled=False):
1✔
1874
        return super(PMEveryCategoryTitleVocabulary, self).__call__(
×
1875
            context,
1876
            only_enabled=only_enabled)
1877

1878
    # do ram.cache have a different key name
1879
    __call__ = PMEveryCategoryTitleVocabulary__call__
1✔
1880

1881

1882
class HeldPositionUsagesVocabulary(object):
1✔
1883
    """ """
1884
    implements(IVocabularyFactory)
1✔
1885

1886
    def __call__(self, context):
1✔
1887
        res = []
1✔
1888
        res.append(
1✔
1889
            SimpleTerm('assemblyMember', 'assemblyMember', _('assemblyMember')))
1890
        res.append(
1✔
1891
            SimpleTerm('asker', 'asker', _('asker')))
1892
        return SimpleVocabulary(res)
1✔
1893

1894

1895
HeldPositionUsagesVocabularyFactory = HeldPositionUsagesVocabulary()
1✔
1896

1897

1898
class HeldPositionDefaultsVocabulary(object):
1✔
1899
    """ """
1900
    implements(IVocabularyFactory)
1✔
1901

1902
    def __call__(self, context):
1✔
1903
        res = []
1✔
1904
        res.append(
1✔
1905
            SimpleTerm('present', 'present', _('present')))
1906
        res.append(
1✔
1907
            SimpleTerm('voter', 'voter', _('voter')))
1908
        return SimpleVocabulary(res)
1✔
1909

1910

1911
HeldPositionDefaultsVocabularyFactory = HeldPositionDefaultsVocabulary()
1✔
1912

1913

1914
class ItemNotPresentTypeVocabulary(object):
1✔
1915
    """ """
1916
    implements(IVocabularyFactory)
1✔
1917

1918
    def __call__(self, context):
1✔
1919
        tool = api.portal.get_tool('portal_plonemeeting')
×
1920
        cfg = tool.getMeetingConfig(context)
×
1921
        res = []
×
1922
        usedMeetingAttributes = cfg.getUsedMeetingAttributes()
×
1923
        if 'absents' in usedMeetingAttributes:
×
1924
            res.append(SimpleTerm('absent', 'absent', _(u"absent")))
×
1925
        if 'excused' in usedMeetingAttributes:
×
1926
            res.append(SimpleTerm('excused', 'excused', _(u"excused")))
×
1927
        return SimpleVocabulary(res)
×
1928

1929

1930
ItemNotPresentTypeVocabularyFactory = ItemNotPresentTypeVocabulary()
1✔
1931

1932

1933
class NumbersVocabulary(object):
1✔
1934
    """ """
1935
    implements(IVocabularyFactory)
1✔
1936

1937
    def __call__(self, context, start=1, end=21):
1✔
1938
        res = []
1✔
1939
        for number in range(start, end):
1✔
1940
            # make number a str
1941
            num_str = str(number)
1✔
1942
            res.append(SimpleTerm(num_str, num_str, num_str))
1✔
1943
        return SimpleVocabulary(res)
1✔
1944

1945

1946
NumbersVocabularyFactory = NumbersVocabulary()
1✔
1947

1948

1949
class NumbersFromZeroVocabulary(NumbersVocabulary):
1✔
1950
    """ """
1951
    implements(IVocabularyFactory)
1✔
1952

1953
    def __call__(self, context, start=0, end=11):
1✔
1954
        return super(NumbersFromZeroVocabulary, self).__call__(
×
1955
            start, end)
1956

1957

1958
NumbersFromZeroVocabularyFactory = NumbersFromZeroVocabulary()
1✔
1959

1960

1961
class ItemAllStatesVocabulary(object):
1✔
1962
    """ """
1963
    implements(IVocabularyFactory)
1✔
1964

1965
    def __call__(self, context):
1✔
1966
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
1967
        res = []
1✔
1968
        for cfg in tool.getActiveConfigs():
1✔
1969
            cfgItemStates = cfg.listStates('Item')
1✔
1970
            cfgId = cfg.getId()
1✔
1971
            u_cfg_title = safe_unicode(cfg.Title(include_config_group=True))
1✔
1972
            # cfgItemStates is a list of tuple, ready to move to a DisplayList
1973
            for key, value in cfgItemStates:
1✔
1974
                # build a strong id
1975
                term_key = u"{0}__state__{1}".format(cfgId, key)
1✔
1976
                term_value = u"{0} - {1}".format(u_cfg_title, value)
1✔
1977
                res.append(
1✔
1978
                    SimpleTerm(term_key, term_key, term_value))
1979

1980
        res = humansorted(res, key=attrgetter('title'))
1✔
1981
        return SimpleVocabulary(res)
1✔
1982

1983

1984
ItemAllStatesVocabularyFactory = ItemAllStatesVocabulary()
1✔
1985

1986

1987
class AnnexRestrictShownAndEditableAttributesVocabulary(object):
1✔
1988
    """ """
1989
    implements(IVocabularyFactory)
1✔
1990

1991
    def __call__(self, context):
1✔
1992
        res = []
1✔
1993
        annex_attributes = ['confidentiality', 'to_be_printed', 'signed', 'publishable']
1✔
1994
        for annex_attr in annex_attributes:
1✔
1995
            for suffix in ('display', 'edit'):
1✔
1996
                term_id = '{0}_{1}'.format(annex_attr, suffix)
1✔
1997
                res.append(SimpleTerm(
1✔
1998
                    term_id,
1999
                    term_id,
2000
                    translate(term_id,
2001
                              domain='PloneMeeting',
2002
                              context=context.REQUEST)
2003
                ))
2004
        return SimpleVocabulary(res)
1✔
2005

2006

2007
AnnexRestrictShownAndEditableAttributesVocabularyFactory = AnnexRestrictShownAndEditableAttributesVocabulary()
1✔
2008

2009

2010
class KeepAccessToItemWhenAdviceVocabulary(object):
1✔
2011
    """ """
2012
    implements(IVocabularyFactory)
1✔
2013

2014
    def __call__(self, context):
1✔
2015
        res = []
1✔
2016
        values = ('default', 'was_giveable', 'is_given')
1✔
2017
        if context.portal_type != 'MeetingConfig':
1✔
2018
            values = ('use_meetingconfig_value', ) + values
1✔
2019
        for value in values:
1✔
2020
            res.append(
1✔
2021
                SimpleTerm(value, value, translate(
2022
                    'keep_access_to_item_when_advice_' + value,
2023
                    domain='PloneMeeting',
2024
                    context=context.REQUEST)))
2025
        return SimpleVocabulary(res)
1✔
2026

2027

2028
KeepAccessToItemWhenAdviceVocabularyFactory = KeepAccessToItemWhenAdviceVocabulary()
1✔
2029

2030

2031
class PMMergeTemplatesVocabulary(MergeTemplatesVocabularyFactory):
1✔
2032
    """Override pod_template.merge_templates vocabulary to display the MeetingConfig title."""
2033
    implements(IVocabularyFactory)
1✔
2034

2035
    def _portal_types(self):
1✔
2036
        return ['ConfigurablePODTemplate']
1✔
2037

2038
    def _render_term_title(self, brain):
1✔
2039
        obj = brain.getObject()
1✔
2040
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2041
        cfg = tool.getMeetingConfig(obj)
1✔
2042
        term_title = safe_unicode('{0} ({1})'.format(obj.Title(), cfg.Title()))
1✔
2043
        if obj.enabled is False:
1✔
2044
            term_title = translate(
×
2045
                msgid='${element_title} (Inactive)',
2046
                domain='PloneMeeting',
2047
                mapping={'element_title': term_title},
2048
                context=obj.REQUEST)
2049
        return term_title
1✔
2050

2051

2052
PMMergeTemplatesVocabularyFactory = PMMergeTemplatesVocabulary()
1✔
2053

2054

2055
class BaseHeldPositionsVocabulary(object):
1✔
2056
    """ """
2057
    implements(IVocabularyFactory)
1✔
2058

2059
    def _is_editing_config(self, context):
1✔
2060
        """Force highlight=True person_label in title
2061
           when displayed in the MeetingConfig view."""
2062
        return IMeetingConfig.providedBy(context) and \
1✔
2063
            'base_edit' not in context.REQUEST.getURL()
2064

2065
    def __call___cachekey(method,
1✔
2066
                          self,
2067
                          context,
2068
                          usage=None,
2069
                          uids=[],
2070
                          highlight_missing=False,
2071
                          include_usages=True,
2072
                          include_defaults=True,
2073
                          include_signature_number=True,
2074
                          include_voting_group=False,
2075
                          pattern=u"{0}",
2076
                          review_state=['active']):
2077
        '''cachekey method for self.__call__.'''
2078
        date = get_cachekey_volatile(
1✔
2079
            'Products.PloneMeeting.vocabularies.allheldpositionsvocabularies')
2080
        return date, repr(context), usage, uids, self._is_editing_config(context),
1✔
2081
        highlight_missing, include_usages, include_defaults,
2082
        include_signature_number, include_voting_group, pattern, review_state
2083

2084
    @ram.cache(__call___cachekey)
1✔
2085
    def BaseHeldPositionsVocabulary__call__(
1✔
2086
            self,
2087
            context,
2088
            usage=None,
2089
            uids=[],
2090
            highlight_missing=False,
2091
            include_usages=True,
2092
            include_defaults=True,
2093
            include_signature_number=True,
2094
            include_voting_group=False,
2095
            pattern=u"{0}",
2096
            review_state=['active']):
2097
        catalog = api.portal.get_tool('portal_catalog')
1✔
2098
        query = {'portal_type': 'held_position',
1✔
2099
                 'sort_on': 'sortable_title'}
2100
        if review_state:
1✔
2101
            query['review_state'] = review_state
1✔
2102
        if uids:
1✔
2103
            query['UID'] = uids
1✔
2104
        brains = catalog.unrestrictedSearchResults(**query)
1✔
2105
        res = []
1✔
2106
        highlight = False
1✔
2107
        is_item = False
1✔
2108
        context_uid = None
1✔
2109
        meeting = None
1✔
2110
        if self._is_editing_config(context):
1✔
2111
            highlight = True
1✔
2112
            if highlight_missing:
1✔
2113
                pattern = u"<span class='highlight-red'>{0}</span>".format(pattern)
×
2114
        elif IMeetingItem.providedBy(context) and context.hasMeeting():
1✔
2115
            is_item = True
1✔
2116
            context_uid = context.UID()
1✔
2117
            meeting = context.getMeeting()
1✔
2118

2119
        forced_position_type_value = None
1✔
2120
        for brain in brains:
1✔
2121
            held_position = brain.getObject()
1✔
2122
            if held_position.usages and (not usage or usage in held_position.usages):
1✔
2123
                if is_item:
1✔
2124
                    forced_position_type_value = meeting.get_attendee_position_for(
1✔
2125
                        context_uid, brain.UID)
2126
                res.append(
1✔
2127
                    SimpleTerm(
2128
                        brain.UID,
2129
                        brain.UID,
2130
                        pattern.format(
2131
                            held_position.get_short_title(
2132
                                include_usages=include_usages,
2133
                                include_defaults=include_defaults,
2134
                                include_signature_number=include_signature_number,
2135
                                include_voting_group=include_voting_group,
2136
                                highlight=highlight,
2137
                                forced_position_type_value=forced_position_type_value))))
2138
        return SimpleVocabulary(res)
1✔
2139

2140
    # do ram.cache have a different key name
2141
    __call__ = BaseHeldPositionsVocabulary__call__
1✔
2142

2143

2144
class SelectableHeldPositionsVocabulary(BaseHeldPositionsVocabulary):
1✔
2145
    """ """
2146

2147
    def __call__(self, context, usage=None, uids=[]):
1✔
2148
        res = super(SelectableHeldPositionsVocabulary, self).__call__(context, usage=None)
×
2149
        return res
×
2150

2151

2152
SelectableHeldPositionsVocabularyFactory = SelectableHeldPositionsVocabulary()
1✔
2153

2154

2155
class BaseSimplifiedHeldPositionsVocabulary(BaseHeldPositionsVocabulary):
1✔
2156
    """ """
2157

2158
    def __call__(self, context, usage=None, uids=[]):
1✔
2159
        res = super(BaseSimplifiedHeldPositionsVocabulary, self).__call__(
1✔
2160
            context,
2161
            usage=None,
2162
            uids=uids,
2163
            include_usages=False,
2164
            include_defaults=False,
2165
            include_signature_number=False,
2166
            include_voting_group=False)
2167
        return res
1✔
2168

2169

2170
BaseSimplifiedHeldPositionsVocabularyFactory = BaseSimplifiedHeldPositionsVocabulary()
1✔
2171

2172

2173
class SelectableCommitteeAttendeesVocabulary(BaseSimplifiedHeldPositionsVocabulary):
1✔
2174
    """ """
2175

2176
    def __call__(self, context):
1✔
2177
        # as vocabulary is used in a DataGridField
2178
        # context is often NO_VALUE...
2179
        if not hasattr(context, "getTagName"):
1✔
2180
            context = get_context_with_request(context)
1✔
2181
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2182
        cfg = tool.getMeetingConfig(context)
1✔
2183
        uids = []
1✔
2184
        if cfg:
1✔
2185
            # manage missing terms manually as used in a datagridfield...
2186
            current_values = set()
1✔
2187
            if base_hasattr(context, "committees"):
1✔
2188
                current_values = set(
1✔
2189
                    itertools.chain.from_iterable(
2190
                        [data.get('attendees') or []
2191
                         for data in context.committees or []]))
2192
            cfg_values = list(cfg.getOrderedCommitteeContacts())
1✔
2193
            missing_values = list(current_values.difference(cfg_values))
1✔
2194
            uids = cfg_values + missing_values
1✔
2195
        return super(SelectableCommitteeAttendeesVocabulary, self).__call__(
1✔
2196
            context=context,
2197
            uids=uids)
2198

2199

2200
SelectableCommitteeAttendeesVocabularyFactory = SelectableCommitteeAttendeesVocabulary()
1✔
2201

2202

2203
class SelectableAssemblyMembersVocabulary(BaseHeldPositionsVocabulary):
1✔
2204
    """ """
2205

2206
    def __call__(self, context, usage=None, uids=[]):
1✔
2207
        terms = super(SelectableAssemblyMembersVocabulary, self).__call__(
1✔
2208
            context, usage='assemblyMember')
2209
        stored_terms = []
1✔
2210
        if IMeetingConfig.providedBy(context):
1✔
2211
            stored_terms = context.getOrderedContacts()
1✔
2212
        else:
2213
            # IOrganization or the datagrid field of it...
2214
            # stored in datagridfield 'certified_signatures'
2215
            if context != NO_VALUE:
×
2216
                if isinstance(context, dict):
×
2217
                    data = [context]
×
2218
                else:
2219
                    data = context.get_certified_signatures()
×
2220
                stored_held_positions = tuple(set(
×
2221
                    [elt['held_position'] for elt in data if elt['held_position']]))
2222
                stored_terms = stored_held_positions
×
2223
        # add missing terms, do not use by_token or by_value,
2224
        # it is not completed, maybe because of cached call?
2225
        term_uids = [term.token for term in terms]
1✔
2226
        missing_term_uids = [uid for uid in stored_terms if uid not in term_uids]
1✔
2227
        terms = terms._terms
1✔
2228
        if missing_term_uids:
1✔
2229
            missing_terms = super(SelectableAssemblyMembersVocabulary, self).__call__(
×
2230
                context,
2231
                usage=None,
2232
                uids=missing_term_uids,
2233
                highlight_missing=True,
2234
                review_state=[])
2235
            terms += missing_terms._terms
×
2236
        return SimpleVocabulary(terms)
1✔
2237

2238

2239
SelectableAssemblyMembersVocabularyFactory = SelectableAssemblyMembersVocabulary()
1✔
2240

2241

2242
class SelectableItemInitiatorsVocabulary(BaseHeldPositionsVocabulary):
1✔
2243
    """ """
2244

2245
    def __call__(self, context):
1✔
2246
        terms = super(SelectableItemInitiatorsVocabulary, self).__call__(
1✔
2247
            context, usage='asker')
2248
        if IMeetingConfig.providedBy(context):
1✔
2249
            stored_terms = context.getOrderedItemInitiators()
1✔
2250
        else:
2251
            # MeetingItem, XXX not used for now
2252
            stored_terms = context.getItemInitiator()
×
2253
        # add missing terms as inactive held_positions are not in the vocabulary
2254
        # do not use by_token or by_value, it is not completed, maybe because of cached call?
2255
        term_uids = [term.token for term in terms]
1✔
2256
        missing_term_uids = [uid for uid in stored_terms if uid not in term_uids]
1✔
2257
        # do not modify original terms
2258
        terms = list(terms._terms)
1✔
2259
        if missing_term_uids:
1✔
2260
            missing_terms = super(SelectableItemInitiatorsVocabulary, self).__call__(
×
2261
                context,
2262
                usage=None,
2263
                uids=missing_term_uids,
2264
                highlight_missing=True,
2265
                review_state=[])
2266
            terms += missing_terms._terms
×
2267
        # add selectable organizations
2268
        terms += list(get_vocab(
1✔
2269
            context,
2270
            'Products.PloneMeeting.vocabularies.detailedorganizationsvocabulary')._terms)
2271
        return SimpleVocabulary(terms)
1✔
2272

2273

2274
SelectableItemInitiatorsVocabularyFactory = SelectableItemInitiatorsVocabulary()
1✔
2275

2276

2277
class ItemVotersVocabulary(BaseHeldPositionsVocabulary):
1✔
2278
    """ """
2279

2280
    def __call___cachekey(method, self, context):
1✔
2281
        '''cachekey method for self.__call__.'''
2282
        date = get_cachekey_volatile('Products.PloneMeeting.vocabularies.itemvotersvocabulary')
1✔
2283
        # as used in a datagridfield, context may vary...
2284
        context = get_context_with_request(context)
1✔
2285
        return date, repr(context), self._is_editing_config(context)
1✔
2286

2287
    @ram.cache(__call___cachekey)
1✔
2288
    def ItemVotersVocabulary__call__(self, context):
×
2289
        context = get_context_with_request(context)
1✔
2290
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2291
        cfg = tool.getMeetingConfig(context)
1✔
2292
        item_voter_uids = context.get_item_voters()
1✔
2293
        terms = super(ItemVotersVocabulary, self).__call__(
1✔
2294
            context,
2295
            uids=item_voter_uids,
2296
            include_usages=False,
2297
            include_defaults=False,
2298
            include_signature_number=False,
2299
            include_voting_group=cfg.getDisplayVotingGroup(),
2300
            review_state=[], )
2301
        # do not modify original terms
2302
        terms = list(terms._terms)
1✔
2303

2304
        # keep order of item attendees
2305
        def getKey(term):
1✔
2306
            return item_voter_uids.index(term.token)
1✔
2307
        terms = sorted(terms, key=getKey)
1✔
2308
        return SimpleVocabulary(terms)
1✔
2309

2310
    # do ram.cache have a different key name
2311
    __call__ = ItemVotersVocabulary__call__
1✔
2312

2313

2314
ItemVotersVocabularyFactory = ItemVotersVocabulary()
1✔
2315

2316

2317
class PMDetailedEveryOrganizationsVocabulary(EveryOrganizationsVocabulary):
1✔
2318
    """Use BaseOrganizationServicesVocabulary and call it from contacts directory then
2319
       adapt title of the terms to show organizations that are in plonegroup and others that are not."""
2320
    implements(IVocabularyFactory)
1✔
2321

2322
    def __call__(self, context):
1✔
2323
        """ """
2324
        terms = super(PMDetailedEveryOrganizationsVocabulary, self).__call__(context)
1✔
2325
        selected_orgs = get_registry_organizations()
1✔
2326
        own_org_uid = get_own_organization().UID()
1✔
2327
        res = []
1✔
2328
        for term in terms:
1✔
2329
            if term.token == own_org_uid:
1✔
2330
                continue
1✔
2331
            if term.value not in selected_orgs:
1✔
2332
                term.title = translate(msgid=u'${term_title} (Not selected in plonegroup)',
1✔
2333
                                       domain='PloneMeeting',
2334
                                       mapping={'term_title': term.title, },
2335
                                       context=context.REQUEST)
2336
            res.append(term)
1✔
2337
        res = humansorted(res, key=attrgetter('title'))
1✔
2338
        return SimpleVocabulary(res)
1✔
2339

2340

2341
PMDetailedEveryOrganizationsVocabularyFactory = PMDetailedEveryOrganizationsVocabulary()
1✔
2342

2343

2344
class AssociatedGroupsVocabulary(object):
1✔
2345
    """ """
2346
    implements(IVocabularyFactory)
1✔
2347

2348
    def __call___cachekey(method, self, context, sort=True):
1✔
2349
        '''cachekey method for self.__call__.'''
2350
        # this volatile is invalidated when plonegroup config changed
2351
        date = get_cachekey_volatile(
1✔
2352
            '_users_groups_value')
2353
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2354
        cfg = tool.getMeetingConfig(context)
1✔
2355
        return date, sort, repr(cfg)
1✔
2356

2357
    def _get_organizations(self, context, the_objects=True):
1✔
2358
        """This centralize logic of gettting associated groups."""
2359
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2360
        cfg = tool.getMeetingConfig(context)
1✔
2361
        # selectable associated groups defined in MeetingConfig?
2362
        is_using_cfg_order = False
1✔
2363
        if cfg.getOrderedAssociatedOrganizations():
1✔
2364
            is_using_cfg_order = True
1✔
2365
            orgs = list(cfg.getOrderedAssociatedOrganizations(theObjects=the_objects))
1✔
2366
        else:
2367
            # if not then every selected organizations of plonegroup
2368
            orgs = get_organizations(only_selected=True, the_objects=the_objects)
1✔
2369
        return is_using_cfg_order, orgs
1✔
2370

2371
    @ram.cache(__call___cachekey)
1✔
2372
    def AssociatedGroupsVocabulary__call__(self, context, sort=True):
1✔
2373
        """ """
2374
        is_using_cfg_order, orgs = self._get_organizations(context)
1✔
2375
        terms = []
1✔
2376
        for org in orgs:
1✔
2377
            term_value = org.UID()
1✔
2378
            terms.append(SimpleTerm(term_value, term_value, org.get_full_title()))
1✔
2379

2380
        if sort or not is_using_cfg_order:
1✔
2381
            terms = humansorted(terms, key=attrgetter('title'))
1✔
2382
        return SimpleVocabulary(terms)
1✔
2383

2384
    # do ram.cache have a different key name
2385
    __call__ = AssociatedGroupsVocabulary__call__
1✔
2386

2387

2388
AssociatedGroupsVocabularyFactory = AssociatedGroupsVocabulary()
1✔
2389

2390

2391
class ItemAssociatedGroupsVocabulary(AssociatedGroupsVocabulary):
1✔
2392
    """Manage missing terms if context is a MeetingItem."""
2393
    implements(IVocabularyFactory)
1✔
2394

2395
    def __call__(self, context):
1✔
2396
        """This is not ram.cached."""
2397
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2398
        cfg = tool.getMeetingConfig(context)
1✔
2399
        sort = True
1✔
2400
        if 'associatedGroups' in cfg.getItemFieldsToKeepConfigSortingFor():
1✔
2401
            sort = False
1✔
2402
        terms = super(ItemAssociatedGroupsVocabulary, self).__call__(context, sort=sort)._terms
1✔
2403
        # make sure we have a copy of _terms because we will add some
2404
        terms = list(terms)
1✔
2405
        # when used on an item, manage missing terms, selected on item
2406
        # but removed from orderedAssociatedOrganizations or from plonegroup
2407
        stored_terms = context.getAssociatedGroups()
1✔
2408
        term_uids = [term.token for term in terms]
1✔
2409
        missing_term_uids = [uid for uid in stored_terms
1✔
2410
                             if uid not in term_uids]
2411
        if missing_term_uids:
1✔
2412
            # we may query any org_uids as we accept org outside own organization
2413
            missing_terms = uuidsToObjects(missing_term_uids, ordered=False, unrestricted=True)
1✔
2414
            for org in missing_terms:
1✔
2415
                org_uid = org.UID()
1✔
2416
                terms.append(SimpleTerm(org_uid, org_uid, org.get_full_title()))
1✔
2417

2418
        return SimpleVocabulary(terms)
1✔
2419

2420

2421
ItemAssociatedGroupsVocabularyFactory = ItemAssociatedGroupsVocabulary()
1✔
2422

2423

2424
class CopyGroupsVocabulary(object):
1✔
2425
    """ """
2426
    implements(IVocabularyFactory)
1✔
2427

2428
    def __call___cachekey(method, self, context):
1✔
2429
        '''cachekey method for self.__call__.'''
2430
        # this volatile is invalidated when plonegroup config changed
2431
        date = get_cachekey_volatile(
1✔
2432
            '_users_groups_value')
2433
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2434
        cfg = tool.getMeetingConfig(context)
1✔
2435
        return date, repr(cfg)
1✔
2436

2437
    @ram.cache(__call___cachekey)
1✔
2438
    def CopyGroupsVocabulary__call__(self, context):
×
2439
        '''Lists the groups that will be selectable to be in copy for this item.'''
2440
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2441
        cfg = tool.getMeetingConfig(context)
1✔
2442
        portal_groups = api.portal.get_tool('portal_groups')
1✔
2443
        terms = []
1✔
2444
        for groupId in cfg.getSelectableCopyGroups():
1✔
2445
            group = portal_groups.getGroupById(groupId)
1✔
2446
            terms.append(SimpleTerm(groupId, groupId, safe_unicode(group.getProperty('title'))))
1✔
2447

2448
        terms = humansorted(terms, key=attrgetter('title'))
1✔
2449
        return SimpleVocabulary(terms)
1✔
2450

2451
    # do ram.cache have a different key name
2452
    __call__ = CopyGroupsVocabulary__call__
1✔
2453

2454

2455
CopyGroupsVocabularyFactory = CopyGroupsVocabulary()
1✔
2456

2457

2458
class ItemCopyGroupsVocabulary(CopyGroupsVocabulary):
1✔
2459
    """Manage missing terms if context is a MeetingItem."""
2460

2461
    implements(IVocabularyFactory)
1✔
2462

2463
    def __call__(self, context, include_auto=False):
1✔
2464
        """This is not ram.cached."""
2465
        terms = super(ItemCopyGroupsVocabulary, self).__call__(context)._terms
1✔
2466
        # make sure we have a copy of _terms because we will add some
2467
        terms = list(terms)
1✔
2468
        # include terms for autoCopyGroups if relevant
2469
        portal_groups = api.portal.get_tool('portal_groups')
1✔
2470
        if include_auto and context.autoCopyGroups:
1✔
2471
            for autoGroupId in context.autoCopyGroups:
1✔
2472
                groupId = context._realCopyGroupId(autoGroupId)
1✔
2473
                group = portal_groups.getGroupById(groupId)
1✔
2474
                if group:
1✔
2475
                    terms.append(SimpleTerm(autoGroupId,
1✔
2476
                                            autoGroupId,
2477
                                            safe_unicode(group.getProperty('title')) + u' [auto]'))
2478
                else:
2479
                    terms.append(SimpleTerm(autoGroupId, autoGroupId, autoGroupId))
×
2480

2481
        # manage missing terms
2482
        copyGroups = context.getCopyGroups()
1✔
2483
        if copyGroups:
1✔
2484
            copyGroupsInVocab = [term.value for term in terms]
1✔
2485
            for groupId in copyGroups:
1✔
2486
                if groupId not in copyGroupsInVocab:
1✔
2487
                    realGroupId = context._realCopyGroupId(groupId)
1✔
2488
                    group = portal_groups.getGroupById(realGroupId)
1✔
2489
                    if group:
1✔
2490
                        if realGroupId == groupId:
1✔
2491
                            terms.append(
1✔
2492
                                SimpleTerm(groupId, groupId, safe_unicode(group.getProperty('title'))))
2493
                        else:
2494
                            # auto copy group
2495
                            terms.append(
×
2496
                                SimpleTerm(groupId,
2497
                                           groupId,
2498
                                           safe_unicode(group.getProperty('title')) + u' [auto]'))
2499
                    else:
2500
                        terms.append(SimpleTerm(groupId, groupId, groupId))
×
2501

2502
        terms = humansorted(terms, key=attrgetter('title'))
1✔
2503
        return SimpleVocabulary(terms)
1✔
2504

2505

2506
ItemCopyGroupsVocabularyFactory = ItemCopyGroupsVocabulary()
1✔
2507

2508

2509
class SelectableCommitteesVocabulary(object):
1✔
2510
    implements(IVocabularyFactory)
1✔
2511

2512
    def _get_stored_values(self):
1✔
2513
        """ """
2514
        return []
1✔
2515

2516
    def _get_term_title(self, committee, term_title_attr):
1✔
2517
        """ """
2518
        term_title = committee[term_title_attr]
1✔
2519
        # manage when no term_title (no acronym defined)
2520
        term_title = term_title or translate("None",
1✔
2521
                                             domain="PloneMeeting",
2522
                                             context=self.context.REQUEST)
2523
        return safe_unicode(term_title)
1✔
2524

2525
    def __call___cachekey(method,
1✔
2526
                          self,
2527
                          context,
2528
                          term_title_attr="label",
2529
                          include_suppl=True,
2530
                          check_is_manager_for_suppl=False,
2531
                          include_all_disabled=True,
2532
                          include_item_only=True,
2533
                          cfg_committees=None,
2534
                          add_no_committee_value=True,
2535
                          check_using_groups=False,
2536
                          include_empty_string=True):
2537
        '''cachekey method for self.__call__.'''
2538
        date = get_cachekey_volatile(
1✔
2539
            'Products.PloneMeeting.vocabularies.selectable_committees_vocabulary')
2540
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2541
        # as vocabulary is used in a DataGridField
2542
        # context is often NO_VALUE or the dict...
2543
        if not hasattr(context, "getTagName"):
1✔
2544
            context = get_context_with_request(context)
1✔
2545
        cfg = tool.getMeetingConfig(context)
1✔
2546
        if cfg is None:
1✔
2547
            return None
1✔
2548
        # if current context is an item, cache by stored committees
2549
        # so we avoid cache by context
2550
        committees = []
1✔
2551
        if context.getTagName() == "MeetingItem":
1✔
2552
            committees = context.getCommittees()
1✔
2553
        # check_is_manager_for_suppl depend on isManager
2554
        isManager = tool.isManager(cfg)
1✔
2555
        # cache by user_plone_groups if using committees "using_groups"
2556
        user_plone_groups = []
1✔
2557
        if cfg.is_committees_using("using_groups"):
1✔
2558
            user_plone_groups = get_plone_groups_for_user()
1✔
2559
        return date, repr(cfg), committees, user_plone_groups, isManager, \
1✔
2560
            term_title_attr, include_suppl, \
2561
            check_is_manager_for_suppl, include_all_disabled, include_item_only, \
2562
            cfg_committees, add_no_committee_value, \
2563
            check_using_groups, include_empty_string
2564

2565
    @ram.cache(__call___cachekey)
1✔
2566
    def SelectableCommitteesVocabulary__call__(
1✔
2567
            self,
2568
            context,
2569
            term_title_attr="label",
2570
            include_suppl=True,
2571
            check_is_manager_for_suppl=False,
2572
            include_all_disabled=True,
2573
            include_item_only=True,
2574
            cfg_committees=None,
2575
            add_no_committee_value=True,
2576
            check_using_groups=False,
2577
            include_empty_string=True):
2578
        """ """
2579
        terms = []
1✔
2580
        if include_empty_string:
1✔
2581
            terms.append(
1✔
2582
                SimpleTerm(EMPTY_STRING,
2583
                           EMPTY_STRING,
2584
                           translate('(None)',
2585
                                     domain='PloneMeeting',
2586
                                     context=context.REQUEST)))
2587

2588
        # as vocabulary is used in a DataGridField
2589
        # context is often NO_VALUE or the dict...
2590
        if not hasattr(context, "getTagName"):
1✔
2591
            context = get_context_with_request(context)
1✔
2592
        self.context = context
1✔
2593
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2594
        cfg = tool.getMeetingConfig(context)
1✔
2595
        if cfg is None:
1✔
2596
            # can happen while creating a new MeetingConfig TTW
2597
            return SimpleVocabulary(terms)
1✔
2598

2599
        cfg_committees = cfg_committees or cfg.getCommittees()
1✔
2600
        is_manager = tool.isManager(cfg)
1✔
2601

2602
        if add_no_committee_value:
1✔
2603
            no_committee_msgid = "no_committee_term_title_{0}".format(term_title_attr)
1✔
2604
            term_title = translate(
1✔
2605
                no_committee_msgid,
2606
                domain="PloneMeeting",
2607
                context=context.REQUEST,
2608
                default=u"No committee")
2609
            terms.append(SimpleTerm(NO_COMMITTEE, NO_COMMITTEE, term_title))
1✔
2610

2611
        def _add_suppl(committee, enabled=True):
1✔
2612
            suppl_terms = []
1✔
2613
            suppl_ids = cfg.get_supplements_for_committee(committee=committee)
1✔
2614
            i = 1
1✔
2615
            for suppl_id in suppl_ids:
1✔
2616
                term_title = self._get_term_title(committee, term_title_attr)
1✔
2617
                if not enabled:
1✔
2618
                    term_title = translate(
×
2619
                        '${element_title} (Inactive)',
2620
                        domain='PloneMeeting',
2621
                        mapping={'element_title': term_title},
2622
                        context=context.REQUEST)
2623
                suppl_msgid = term_title_attr == "label" and \
1✔
2624
                    'committee_title_with_suppl' or 'committee_title_with_abbr_suppl'
2625
                term_title = translate(
1✔
2626
                    suppl_msgid,
2627
                    domain="PloneMeeting",
2628
                    mapping={'title': term_title, 'number': number_word(i)},
2629
                    context=context.REQUEST,
2630
                    default=u"${title} (${number} supplement)")
2631
                i += 1
1✔
2632
                suppl_terms.append(SimpleTerm(suppl_id,
1✔
2633
                                              suppl_id,
2634
                                              term_title))
2635
            return suppl_terms
1✔
2636

2637
        stored_values = self._get_stored_values()
1✔
2638
        for committee in cfg_committees:
1✔
2639
            # bypass new value still not having a valid row_id
2640
            if (committee['enabled'] == '1' and committee['row_id']) or \
1✔
2641
               (include_item_only and committee['enabled'] == 'item_only' and committee['row_id']) or \
2642
               committee['row_id'] in stored_values:
2643
                # check_using_groups only if not a stored value
2644
                if check_using_groups and \
1✔
2645
                   committee['row_id'] not in stored_values and \
2646
                   not is_manager and \
2647
                   committee['using_groups']:
2648
                    org_uids = tool.get_selectable_orgs(
1✔
2649
                        cfg, only_selectable=True, the_objects=False)
2650
                    if not set(org_uids).intersection(committee['using_groups']):
1✔
2651
                        continue
1✔
2652
                term_title = self._get_term_title(committee, term_title_attr)
1✔
2653
                terms.append(SimpleTerm(committee['row_id'],
1✔
2654
                                        committee['row_id'],
2655
                                        term_title))
2656
                # manage supplements
2657
                if include_suppl and (not check_is_manager_for_suppl or is_manager):
1✔
2658
                    terms += _add_suppl(committee)
1✔
2659

2660
        if include_all_disabled:
1✔
2661
            for committee in cfg_committees:
1✔
2662
                if committee['enabled'] == '0':
1✔
2663
                    term_title = self._get_term_title(committee, term_title_attr)
×
2664
                    label = translate(
×
2665
                        '${element_title} (Inactive)',
2666
                        domain='PloneMeeting',
2667
                        mapping={'element_title': term_title},
2668
                        context=context.REQUEST)
2669
                    terms.append(SimpleTerm(committee['row_id'],
×
2670
                                            committee['row_id'],
2671
                                            label))
2672
                    # manage supplements
2673
                    if include_suppl:
×
2674
                        terms += _add_suppl(committee, enabled=False)
×
2675
        return SimpleVocabulary(terms)
1✔
2676

2677
    # do ram.cache have a different key name
2678
    __call__ = SelectableCommitteesVocabulary__call__
1✔
2679

2680

2681
SelectableCommitteesVocabularyFactory = SelectableCommitteesVocabulary()
1✔
2682

2683

2684
class SelectableCommitteesAcronymsVocabulary(SelectableCommitteesVocabulary):
1✔
2685

2686
    def __call__(self, context, term_title_attr="acronym"):
1✔
2687
        """ """
2688
        return super(SelectableCommitteesAcronymsVocabulary, self).__call__(
×
2689
            context, term_title_attr)
2690

2691

2692
SelectableCommitteesAcronymsVocabularyFactory = SelectableCommitteesAcronymsVocabulary()
1✔
2693

2694

2695
class ItemSelectableCommitteesVocabulary(SelectableCommitteesVocabulary):
1✔
2696

2697
    def _get_stored_values(self):
1✔
2698
        """ """
2699
        return self.context.getCommittees()
1✔
2700

2701
    def __call__(self, context):
1✔
2702
        """ """
2703
        res = super(ItemSelectableCommitteesVocabulary, self).__call__(
1✔
2704
            context,
2705
            check_is_manager_for_suppl=True,
2706
            include_all_disabled=False,
2707
            include_item_only=True,
2708
            check_using_groups=True,
2709
            include_empty_string=False)
2710
        # characters &nbsp; are shown when editing an item...
2711
        for term in res._terms:
1✔
2712
            term.title = term.title.replace('&nbsp;', ' ')
1✔
2713
        return res
1✔
2714

2715

2716
ItemSelectableCommitteesVocabularyFactory = ItemSelectableCommitteesVocabulary()
1✔
2717

2718

2719
class MeetingSelectableCommitteesVocabulary(SelectableCommitteesVocabulary):
1✔
2720

2721
    def _get_stored_values(self):
1✔
2722
        """ """
2723
        stored_values = []
1✔
2724
        if self.context.getTagName() == "Meeting":
1✔
2725
            stored_values = get_datagridfield_column_value(self.context.committees, "row_id")
1✔
2726
        return stored_values
1✔
2727

2728
    def __call__(self, context):
1✔
2729
        """ """
2730
        return super(MeetingSelectableCommitteesVocabulary, self).__call__(
1✔
2731
            context,
2732
            include_suppl=False,
2733
            include_all_disabled=False,
2734
            include_item_only=False,
2735
            add_no_committee_value=False,
2736
            include_empty_string=False)
2737

2738

2739
MeetingSelectableCommitteesVocabularyFactory = MeetingSelectableCommitteesVocabulary()
1✔
2740

2741

2742
class OtherMCsClonableToVocabulary(object):
1✔
2743
    """Vocabulary listing other MeetingConfigs clonable to."""
2744

2745
    implements(IVocabularyFactory)
1✔
2746

2747
    def __call___cachekey(method, self, context, term_title=None):
1✔
2748
        '''cachekey method for self.__call__.'''
2749
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2750
        cfg = tool.getMeetingConfig(context)
1✔
2751
        # cache per context values, this way a missing value would create another cachekey
2752
        values = self._get_stored_values(context)
1✔
2753
        return repr(cfg), term_title, values
1✔
2754

2755
    def _get_stored_values(self, context):
1✔
2756
        """ """
2757
        values = []
1✔
2758
        if context.__class__.__name__ == 'MeetingItem':
1✔
2759
            values = context.getOtherMeetingConfigsClonableTo()
1✔
2760
        elif context.__class__.__name__ == 'Meeting':
1✔
2761
            values = context.adopts_next_agenda_of
1✔
2762
        # avoid returning None
2763
        return values or []
1✔
2764

2765
    @ram.cache(__call___cachekey)
1✔
2766
    def OtherMCsClonableToVocabulary__call__(self, context, term_title=None):
1✔
2767
        """ """
2768
        tool = api.portal.get_tool('portal_plonemeeting')
1✔
2769
        cfg = tool.getMeetingConfig(context)
1✔
2770
        terms = []
1✔
2771
        cfg_ids = [mc['meeting_config'] for mc in cfg.getMeetingConfigsToCloneTo()]
1✔
2772
        cfg_ids = list(set(cfg_ids).union(self._get_stored_values(context)))
1✔
2773
        for cfg_id in cfg_ids:
1✔
2774
            terms.append(SimpleTerm(cfg_id,
1✔
2775
                                    cfg_id,
2776
                                    term_title or getattr(tool, cfg_id).Title()))
2777
        return SimpleVocabulary(terms)
1✔
2778

2779
    # do ram.cache have a different key name
2780
    __call__ = OtherMCsClonableToVocabulary__call__
1✔
2781

2782

2783
OtherMCsClonableToVocabularyFactory = OtherMCsClonableToVocabulary()
1✔
2784

2785

2786
class OtherMCsClonableToEmergencyVocabulary(OtherMCsClonableToVocabulary):
1✔
2787
    """Vocabulary listing other MeetingConfigs clonable to emergency."""
2788

2789
    implements(IVocabularyFactory)
1✔
2790

2791
    def __call__(self, context, term_title=None):
1✔
2792
        """ """
2793
        term_title = translate('Emergency while presenting in other MC',
1✔
2794
                               domain='PloneMeeting',
2795
                               context=context.REQUEST)
2796
        return super(OtherMCsClonableToEmergencyVocabulary, self).__call__(context, term_title)
1✔
2797

2798

2799
OtherMCsClonableToEmergencyVocabularyFactory = OtherMCsClonableToEmergencyVocabulary()
1✔
2800

2801

2802
class OtherMCsClonableToPrivacyVocabulary(OtherMCsClonableToVocabulary):
1✔
2803
    """Vocabulary listing other MeetingConfigs clonable to privacy."""
2804

2805
    implements(IVocabularyFactory)
1✔
2806

2807
    def __call__(self, context, term_title=None):
1✔
2808
        """ """
2809
        term_title = translate('Secret while presenting in other MC?',
1✔
2810
                               domain='PloneMeeting',
2811
                               context=context.REQUEST)
2812
        return super(OtherMCsClonableToPrivacyVocabulary, self).__call__(context, term_title)
1✔
2813

2814

2815
OtherMCsClonableToPrivacyVocabularyFactory = OtherMCsClonableToPrivacyVocabulary()
1✔
2816

2817

2818
class BaseContainedAnnexesVocabulary(object):
1✔
2819
    """Base vocabulary that manages displaying contained annexes with
2820
       a functionnality that will let disable some annexes."""
2821

2822
    implements(IVocabularyFactory)
1✔
2823

2824
    def __call__(self, context, portal_type='annex'):
1✔
2825
        """ """
2826
        portal_url = api.portal.get().absolute_url()
1✔
2827
        terms = []
1✔
2828
        i = 1
1✔
2829
        annexes = get_categorized_elements(context, portal_type=portal_type)
1✔
2830
        if annexes:
1✔
2831
            categories_vocab = get_vocab(
1✔
2832
                context,
2833
                'collective.iconifiedcategory.categories',
2834
                use_category_uid_as_token=True)
2835
            for annex in annexes:
1✔
2836
                # term title is annex icon, number and title
2837
                term_title = u'{0}. <img src="{1}/{2}" title="{3}"> {4}'.format(
1✔
2838
                    str(i),
2839
                    portal_url,
2840
                    annex['icon_url'],
2841
                    html.escape(safe_unicode(annex['category_title'])),
2842
                    html.escape(safe_unicode(annex['title'])))
2843
                i += 1
1✔
2844
                if annex['warn_filesize']:
1✔
2845
                    term_title += u' ({0})'.format(render_filesize(annex['filesize']))
×
2846
                term = SimpleTerm(annex['id'], annex['id'], term_title)
1✔
2847
                # check if need to disable term
2848
                self._check_disable_term(context, annex, categories_vocab, term)
1✔
2849
                terms.append(term)
1✔
2850
        return SimpleVocabulary(terms)
1✔
2851

2852
    def _check_disable_term(self, context, annex, categories_vocab, term):
1✔
2853
        """ """
NEW
2854
        return
×
2855

2856

2857
class ItemDuplicationContainedAnnexesVocabulary(BaseContainedAnnexesVocabulary):
1✔
2858
    """ """
2859

2860
    def _check_disable_term(self, context, annex, categories_vocab, term):
1✔
2861
        # check if user able to keep this annex :
2862
        # - annex may not hold a scan_id
2863
        term.disabled = False
1✔
2864
        annex_obj = getattr(context, annex['id'])
1✔
2865
        if getattr(annex_obj, 'scan_id', None):
1✔
2866
            term.disabled = True
1✔
2867
            term.title += translate(' [holds scan_id]',
1✔
2868
                                    domain='PloneMeeting',
2869
                                    context=context.REQUEST)
2870
        # - annexType must be among current user selectable annex types
2871
        elif annex['category_uid'] not in categories_vocab:
1✔
2872
            term.disabled = True
1✔
2873
            term.title += translate(' [reserved MeetingManagers]',
1✔
2874
                                    domain='PloneMeeting',
2875
                                    context=context.REQUEST)
2876
        # annexType ask a PDF but the file is not a PDF
2877
        # could happen if configuration changed after creation of annex
2878
        elif get_category_object(annex_obj, annex_obj.content_category).only_pdf and \
1✔
2879
                annex_obj.file.contentType != 'application/pdf':
2880
            term.disabled = True
1✔
2881
            term.title += translate(' [PDF required]',
1✔
2882
                                    domain='PloneMeeting',
2883
                                    context=context.REQUEST)
2884

2885

2886
ItemDuplicationContainedAnnexesVocabularyFactory = ItemDuplicationContainedAnnexesVocabulary()
1✔
2887

2888

2889
class ItemDuplicationContainedDecisionAnnexesVocabulary(ItemDuplicationContainedAnnexesVocabulary):
1✔
2890
    """ """
2891

2892
    def __call__(self, context, portal_type='annexDecision'):
1✔
2893
        """ """
2894
        context.REQUEST['force_use_item_decision_annexes_group'] = True
1✔
2895
        terms = super(ItemDuplicationContainedDecisionAnnexesVocabulary, self).__call__(
1✔
2896
            context, portal_type=portal_type)
2897
        context.REQUEST['force_use_item_decision_annexes_group'] = False
1✔
2898
        return terms
1✔
2899

2900

2901
ItemDuplicationContainedDecisionAnnexesVocabularyFactory = ItemDuplicationContainedDecisionAnnexesVocabulary()
1✔
2902

2903

2904
class ItemExportPDFContainedAnnexesVocabulary(BaseContainedAnnexesVocabulary):
1✔
2905
    """ """
2906

2907
    def _check_disable_term(self, context, annex, categories_vocab, term):
1✔
2908
        # check if user able to export this annex :
2909
        # - annex must be PDF
NEW
2910
        term.disabled = False
×
NEW
2911
        annex_obj = getattr(context, annex['id'])
×
NEW
2912
        if annex_obj.file.contentType != 'application/pdf':
×
NEW
2913
            term.disabled = True
×
NEW
2914
            term.title += translate(' [PDF required]',
×
2915
                                    domain='PloneMeeting',
2916
                                    context=context.REQUEST)
2917

2918

2919
ItemExportPDFContainedAnnexesVocabularyFactory = ItemExportPDFContainedAnnexesVocabulary()
1✔
2920

2921

2922
class ItemExportPDFContainedDecisionAnnexesVocabulary(ItemExportPDFContainedAnnexesVocabulary):
1✔
2923
    """ """
2924

2925
    def __call__(self, context, portal_type='annexDecision'):
1✔
2926
        """ """
UNCOV
2927
        context.REQUEST['force_use_item_decision_annexes_group'] = True
×
NEW
2928
        terms = super(ItemExportPDFContainedDecisionAnnexesVocabulary, self).__call__(
×
2929
            context, portal_type=portal_type)
UNCOV
2930
        context.REQUEST['force_use_item_decision_annexes_group'] = False
×
UNCOV
2931
        return terms
×
2932

2933

2934
ItemExportPDFContainedDecisionAnnexesVocabularyFactory = ItemExportPDFContainedDecisionAnnexesVocabulary()
1✔
2935

2936

2937
class GenerablePODTemplatesVocabulary(object):
1✔
2938
    implements(IVocabularyFactory)
1✔
2939

2940
    def _get_generable_templates(self, context, output_formats):
1✔
NEW
2941
        res = []
×
NEW
2942
        adapter = getAdapter(context, IGenerablePODTemplates)
×
NEW
2943
        pod_templates = adapter.get_generable_templates()
×
NEW
2944
        for pod_template in pod_templates:
×
NEW
2945
            if not output_formats or \
×
2946
               set(output_formats).intersection(pod_template.get_available_formats()):
NEW
2947
                res.append(pod_template)
×
NEW
2948
        return res
×
2949

2950
    def __call__(self, context, output_formats=['pdf']):
1✔
2951
        """ """
NEW
2952
        terms = []
×
NEW
2953
        for pod_template in self._get_generable_templates(context, output_formats):
×
NEW
2954
            term_id = pod_template.getId()
×
NEW
2955
            terms.append(
×
2956
                SimpleTerm(term_id,
2957
                           term_id,
2958
                           safe_unicode(pod_template.Title()))
2959
            )
NEW
2960
        return SimpleVocabulary(terms)
×
2961

2962

2963
GenerablePODTemplatesVocabularyFactory = GenerablePODTemplatesVocabulary()
1✔
2964

2965

2966
class PMUsers(UsersFactory):
1✔
2967
    """Append ' (userid)' to term title."""
2968

2969
    def __call___cachekey(method, self, context, query=''):
1✔
2970
        '''cachekey method for self.__call__.'''
2971
        date = get_cachekey_volatile(
1✔
2972
            '_users_groups_value')
2973
        return date, query
1✔
2974

2975
    @ram.cache(__call___cachekey)
1✔
2976
    def PMUsers__call__(self, context, query=''):
1✔
2977
        acl_users = api.portal.get_tool('acl_users')
1✔
2978
        users = acl_users.searchUsers(sort_by='')
1✔
2979
        terms = []
1✔
2980
        # manage duplicates, this can be the case when using LDAP and same userid in source_users
2981
        userids = []
1✔
2982
        for user in users:
1✔
2983
            user_id = user['id']
1✔
2984
            if user_id not in userids:
1✔
2985
                userids.append(user_id)
1✔
2986
                # bypass special characters, may happen when using LDAP
2987
                try:
1✔
2988
                    unicode(user_id)
1✔
2989
                except UnicodeDecodeError:
×
2990
                    continue
×
2991
                term_title = get_user_fullname(user_id, with_user_id=True)
1✔
2992
                term = SimpleTerm(user_id, user_id, term_title)
1✔
2993
                terms.append(term)
1✔
2994
        terms = humansorted(terms, key=attrgetter('title'))
1✔
2995
        return SimpleVocabulary(terms)
1✔
2996

2997
    # do ram.cache have a different key name
2998
    __call__ = PMUsers__call__
1✔
2999

3000

3001
PMUsersFactory = PMUsers()
1✔
3002

3003

3004
class PMGroupsVocabulary(GroupsVocabulary):
1✔
3005
    """Add caching."""
3006

3007
    def __call___cachekey(method, self, context):
1✔
3008
        '''cachekey method for self.__call__.'''
3009
        return get_cachekey_volatile('_users_groups_value')
1✔
3010

3011
    @ram.cache(__call___cachekey)
1✔
3012
    def PMGroupsVocabulary__call__(self, context):
×
3013
        return super(PMGroupsVocabulary, self).__call__(context)
1✔
3014

3015
    # do ram.cache have a different key name
3016
    __call__ = PMGroupsVocabulary__call__
1✔
3017

3018

3019
PMGroupsVocabularyFactory = PMGroupsVocabulary()
1✔
3020

3021

3022
class PMPositionTypesVocabulary(PositionTypesVocabulary):
1✔
3023

3024
    def _get_person(self, context):
1✔
3025
        """ """
3026
        person = None
1✔
3027
        # adding a held_position
3028
        if context.portal_type == 'person':
1✔
3029
            person = context
1✔
3030
        # editing a held_position
3031
        elif context.portal_type == 'held_position':
1✔
3032
            person = context.get_person()
1✔
3033
        else:
3034
            # used in attendees management forms
3035
            hp = self._get_current_hp(context)
1✔
3036
            if hp is not None:
1✔
3037
                person = hp.get_person()
1✔
3038
        return person
1✔
3039

3040
    def _get_current_hp(self, context):
1✔
3041
        """ """
3042
        hp = None
1✔
3043
        person_uid = context.REQUEST.get('person_uid', None)
1✔
3044
        if person_uid:
1✔
3045
            hp = uuidToObject(person_uid)
1✔
3046
        return hp
1✔
3047

3048
    def _get_base_terms(self, context):
1✔
3049
        """ """
3050
        return super(PMPositionTypesVocabulary, self).__call__(context)
1✔
3051

3052
    def __call__(self, context):
1✔
3053
        res = self._get_base_terms(context)
1✔
3054
        person = self._get_person(context)
1✔
3055
        if person is not None:
1✔
3056
            gender = person.gender or 'M'
1✔
3057
            terms = res._terms
1✔
3058
            for term in terms:
1✔
3059
                if term.token == 'default':
1✔
3060
                    continue
1✔
3061
                gender_and_numbers = split_gender_and_number(term.title)
1✔
3062
                term.title = gender_and_numbers['{0}S'.format(gender)]
1✔
3063
        # sort alphabetically but keep first value (default) in first position
3064
        res._terms[1:] = humansorted(res._terms[1:], key=attrgetter('title'))
1✔
3065
        return res
1✔
3066

3067

3068
PMPositionTypesVocabularyFactory = PMPositionTypesVocabulary()
1✔
3069

3070

3071
class PMAttendeeRedefinePositionTypesVocabulary(PMPositionTypesVocabulary):
1✔
3072

3073
    def _get_base_terms(self, context):
1✔
3074
        res = super(PMAttendeeRedefinePositionTypesVocabulary, self). \
1✔
3075
            _get_base_terms(context)
3076
        tool = api.portal.get_tool("portal_plonemeeting")
1✔
3077
        cfg = tool.getMeetingConfig(context)
1✔
3078
        selectableRedefinedPositionTypes = cfg.getSelectableRedefinedPositionTypes()
1✔
3079
        hp = self._get_current_hp(context)
1✔
3080
        res._terms = [term for term in res._terms
1✔
3081
                      if not selectableRedefinedPositionTypes or
3082
                      term.token in selectableRedefinedPositionTypes or
3083
                      hp and term.token == hp.position_type]
3084
        return res
1✔
3085

3086

3087
PMAttendeeRedefinePositionTypesVocabularyFactory = PMAttendeeRedefinePositionTypesVocabulary()
1✔
3088

3089

3090
class PMDxPortalTypesVocabulary(DxPortalTypesVocabulary):
1✔
3091
    """Override to take into account AT MeetingItem FTIs."""
3092

3093
    def __call__(self, context):
1✔
3094
        portal_types = api.portal.get_tool('portal_types')
1✔
3095
        terms = super(PMDxPortalTypesVocabulary, self).__call__(context)._terms
1✔
3096
        item_ftis = [fti for fti in portal_types.values()
1✔
3097
                     if fti.id.startswith("MeetingItem") and
3098
                     not (fti.id.startswith("MeetingItemRecurring") or
3099
                          fti.id.startswith("MeetingItemTemplate") or
3100
                          fti.id == "MeetingItem")]
3101
        portal = api.portal.get()
1✔
3102
        for item_fti in item_ftis:
1✔
3103
            terms.append(SimpleTerm(
1✔
3104
                item_fti.id,
3105
                item_fti.id,
3106
                translate(item_fti.title,
3107
                          domain="plone",
3108
                          context=portal.REQUEST)))
3109
        return SimpleVocabulary(terms)
1✔
3110

3111

3112
PMDxPortalTypesVocabularyFactory = PMDxPortalTypesVocabulary()
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc