Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into MPMBRWP-19_batch_ac…
Browse files Browse the repository at this point in the history
…tion_concatenate_annexes
  • Loading branch information
gbastien committed Mar 21, 2024
2 parents 2724116 + b271158 commit 92e543f
Show file tree
Hide file tree
Showing 114 changed files with 5,001 additions and 2,347 deletions.
411 changes: 410 additions & 1 deletion CHANGES.rst

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from setuptools import setup


version = '4.2.5.dev0'
version = '4.2.9rc6.dev0'

setup(name='Products.PloneMeeting',
version=version,
Expand Down
3 changes: 2 additions & 1 deletion src/Products/PloneMeeting/Meeting.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
from Products.PloneMeeting.utils import getFieldVersion
from Products.PloneMeeting.utils import getWorkflowAdapter
from Products.PloneMeeting.utils import hasHistory
from Products.PloneMeeting.utils import isPowerObserverForCfg
from Products.PloneMeeting.utils import ItemDuplicatedFromConfigEvent
from Products.PloneMeeting.utils import MeetingLocalRolesUpdatedEvent
from Products.PloneMeeting.utils import rememberPreviousData
Expand Down Expand Up @@ -2230,7 +2231,7 @@ def show_votesObservations(self):
res = tool.isManager(meeting)
if not res:
cfg = tool.getMeetingConfig(meeting)
res = tool.isPowerObserverForCfg(cfg) or \
res = isPowerObserverForCfg(cfg) or \
meeting.adapted().isDecided()
return res

Expand Down
264 changes: 96 additions & 168 deletions src/Products/PloneMeeting/MeetingConfig.py

Large diffs are not rendered by default.

344 changes: 225 additions & 119 deletions src/Products/PloneMeeting/MeetingItem.py

Large diffs are not rendered by default.

426 changes: 203 additions & 223 deletions src/Products/PloneMeeting/ToolPloneMeeting.py

Large diffs are not rendered by default.

166 changes: 101 additions & 65 deletions src/Products/PloneMeeting/adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from collective.documentgenerator.adapters import GenerablePODTemplatesAdapter
from collective.eeafaceted.dashboard.adapters import DashboardGenerablePODTemplatesAdapter
from collective.eeafaceted.dashboard.content.pod_template import IDashboardPODTemplate
from collective.eeafaceted.z3ctable.columns import EMPTY_STRING
from collective.iconifiedcategory.adapter import CategorizedObjectAdapter
from collective.iconifiedcategory.adapter import CategorizedObjectInfoAdapter
from collective.iconifiedcategory.utils import get_categories
Expand All @@ -27,6 +28,7 @@
from imio.helpers.cache import get_current_user_id
from imio.helpers.cache import get_plone_groups_for_user
from imio.helpers.catalog import merge_queries
from imio.helpers.content import get_user_fullname
from imio.helpers.content import get_vocab
from imio.helpers.content import get_vocab_values
from imio.helpers.content import richtextval
Expand All @@ -51,7 +53,6 @@
from Products.PloneMeeting.config import ALL_ADVISERS_GROUP_VALUE
from Products.PloneMeeting.config import DUPLICATE_AND_KEEP_LINK_EVENT_ACTION
from Products.PloneMeeting.config import DUPLICATE_EVENT_ACTION
from Products.PloneMeeting.config import EMPTY_STRING
from Products.PloneMeeting.config import HIDDEN_DURING_REDACTION_ADVICE_VALUE
from Products.PloneMeeting.config import ITEM_NO_PREFERRED_MEETING_VALUE
from Products.PloneMeeting.config import NOT_GIVEN_ADVICE_VALUE
Expand All @@ -68,6 +69,7 @@
from Products.PloneMeeting.utils import get_context_with_request
from Products.PloneMeeting.utils import get_dx_attrs
from Products.PloneMeeting.utils import get_referer_obj
from Products.PloneMeeting.utils import getAdvicePortalTypeIds
from Products.PloneMeeting.utils import getCurrentMeetingObject
from Products.PloneMeeting.utils import getHistoryTexts
from Products.PloneMeeting.utils import is_transition_before_date
Expand Down Expand Up @@ -592,7 +594,7 @@ def _leadingIcons(self):
iconName = takenOverByCurrentUser and 'takenOverByCurrentUser.png' or 'takenOverByOtherUser.png'
res.append((iconName, translate(u'Taken over by ${fullname}',
domain="PloneMeeting",
mapping={'fullname': safe_unicode(self.tool.getUserName(takenOverBy))},
mapping={'fullname': get_user_fullname(takenOverBy)},
context=self.request)))

if self.context.getIsAcceptableOutOfMeeting():
Expand Down Expand Up @@ -714,7 +716,7 @@ def mayViewComment(self, event):
return userMayAccessComment

def get_history_data(self):
"""WF hsitory is mixed with datachanges history."""
"""WF history is mixed with data_changes history."""
history = super(PMWfHistoryAdapter, self).get_history_data()
res = []
for event in history:
Expand All @@ -728,9 +730,10 @@ class PMDataChangesHistoryAdapter(ImioWfHistoryAdapter):
""" """

history_type = 'data_changes'
highlight_last_comment = False

def get_history_data(self):
"""WF history is mixed with datachanges history."""
"""WF history is mixed with data_changes history."""
history = super(PMDataChangesHistoryAdapter, self).get_history_data()
full_datachanges_history = []
# first pass, keep datachanges
Expand Down Expand Up @@ -801,6 +804,13 @@ class PMCompletenessChangesHistoryAdapter(BaseImioHistoryAdapter):
history_attr_name = 'completeness_changes_history'


class PMAdviceHideDuringRedactionHistoryAdapter(BaseImioHistoryAdapter):
""" """

history_type = 'advice_hide_during_redaction'
history_attr_name = 'advice_hide_during_redaction_history'


class PMAdviceGivenHistoryAdapter(BaseImioHistoryAdapter):
""" """

Expand Down Expand Up @@ -861,66 +871,77 @@ def compute_criteria(self, context):
if not cfg:
super(Criteria, self).__init__(context)
return self.context, self.criteria
# meeting view
kept_filters = []
resultsperpagedefault = 20
meeting_view = False
if IMeeting.providedBy(context):
meeting_view = True
is_displaying_available_items = displaying_available_items(context)
self.context = cfg.searches.searches_items
if is_displaying_available_items:
kept_filters = cfg.getDashboardMeetingAvailableItemsFilters()
resultsperpagedefault = cfg.getMaxShownAvailableItems()
else:
kept_filters = cfg.getDashboardMeetingLinkedItemsFilters()
resultsperpagedefault = cfg.getMaxShownMeetingItems()
else:
# on a faceted? it is a pmFolder or a subFolder of the pmFolder
resultsperpagedefault = cfg.getMaxShownListings()
if IFacetedNavigable.providedBy(context):
# keep relevant filters depending on configuration
if context.getId() == 'searches_items':
kept_filters = cfg.getDashboardItemsListingsFilters()
self.context = cfg.searches.searches_items
elif context.getId() == 'searches_meetings':
kept_filters = cfg.getDashboardMeetingsListingsFilters()
self.context = cfg.searches.searches_meetings
elif context.getId() == 'searches_decisions':
kept_filters = cfg.getDashboardMeetingsListingsFilters()
self.context = cfg.searches.searches_decisions
else:
self.context = cfg.searches
self.criteria = self._criteria()
return self.context, self.criteria

res = PersistentList()
for criterion in self._criteria():
if meeting_view and criterion.widget == u'sorting':
# keep it only of displaying available items, default sorting
# is set on 'getProposingGroup', if not displaying available items
# the sorting widget is not kept so sorting is disabled for presented items
# meeting view
kept_filters = []
resultsperpagedefault = 20
meeting_view = False
compute = True
if IMeeting.providedBy(context):
meeting_view = True
is_displaying_available_items = displaying_available_items(context)
self.context = cfg.searches.searches_items
if is_displaying_available_items:
new_criterion = Criterion()
new_criterion.update(**criterion.__dict__)
new_criterion.default = u'getProposingGroup'
res.append(new_criterion)
continue
# ignore the collection widget when on meeting_view
if meeting_view and criterion.widget == u'collection-link':
criterion.default = u''

if criterion.section != u'advanced' or \
criterion.__name__ in kept_filters:
# create new object to avoid modifying stored one
new_criterion = Criterion()
new_criterion.update(**criterion.__dict__)
# manage default value for the 'resultsperpage' criterion
if criterion.widget == ResultsPerPageWidget.widget_type:
new_criterion.default = resultsperpagedefault
res.append(new_criterion)
self.criteria = res

kept_filters = cfg.getDashboardMeetingAvailableItemsFilters()
resultsperpagedefault = cfg.getMaxShownAvailableItems()
else:
kept_filters = cfg.getDashboardMeetingLinkedItemsFilters()
resultsperpagedefault = cfg.getMaxShownMeetingItems()
else:
# on a faceted? it is a pmFolder or a subFolder of the pmFolder
resultsperpagedefault = cfg.getMaxShownListings()
if IFacetedNavigable.providedBy(context):
# keep relevant filters depending on configuration
if context.getId() == 'searches_items':
kept_filters = cfg.getDashboardItemsListingsFilters()
self.context = cfg.searches.searches_items
elif context.getId() == 'searches_meetings':
kept_filters = cfg.getDashboardMeetingsListingsFilters()
self.context = cfg.searches.searches_meetings
elif context.getId() == 'searches_decisions':
kept_filters = cfg.getDashboardMeetingsListingsFilters()
self.context = cfg.searches.searches_decisions
else:
compute = False
self.context = cfg.searches
self.criteria = self._criteria()

if compute:
res = PersistentList()
for criterion in self._criteria():
# take care that we have the stored criteria here so
# if one need to be changed, we must create a
# new Criterion or the stored value is changed!
if meeting_view and criterion.widget == u'sorting':
# keep it only of displaying available items, default sorting
# is set on 'getProposingGroup', if not displaying available items
# the sorting widget is not kept so sorting is disabled for presented items
if is_displaying_available_items:
new_criterion = Criterion()
new_criterion.update(**criterion.__dict__)
new_criterion.default = u'getProposingGroup'
res.append(new_criterion)
continue
# ignore the collection widget when on meeting_view
if meeting_view and criterion.widget == u'collection-link':
new_criterion = Criterion()
new_criterion.update(**criterion.__dict__)
new_criterion.default = u''
res.append(new_criterion)
continue

if criterion.section != u'advanced' or \
criterion.__name__ in kept_filters:
# create new object to avoid modifying stored one
new_criterion = Criterion()
new_criterion.update(**criterion.__dict__)
# manage default value for the 'resultsperpage' criterion
if criterion.widget == ResultsPerPageWidget.widget_type:
new_criterion.default = resultsperpagedefault
res.append(new_criterion)
continue
self.criteria = res
cache[key] = self.context, self.criteria
return self.context, self.criteria


Expand Down Expand Up @@ -1458,7 +1479,7 @@ def query_adviseditems(self):
wfTool = api.portal.get_tool('portal_workflow')
adviceStates = []
# manage multiple 'meetingadvice' portal_types
for portal_type_id in self.tool.getAdvicePortalTypeIds():
for portal_type_id in getAdvicePortalTypeIds():
adviceWF = wfTool.getWorkflowsFor(portal_type_id)[0]
adviceStates += adviceWF.states.keys()
# remove duplicates
Expand Down Expand Up @@ -1491,7 +1512,7 @@ def query_adviseditemswithdelay(self):
wfTool = api.portal.get_tool('portal_workflow')
adviceStates = []
# manage multiple 'meetingadvice' portal_types
for portal_type_id in self.tool.getAdvicePortalTypeIds():
for portal_type_id in getAdvicePortalTypeIds():
adviceWF = wfTool.getWorkflowsFor(portal_type_id)[0]
adviceStates += adviceWF.states.keys()
# remove duplicates
Expand Down Expand Up @@ -1523,6 +1544,21 @@ def query_decideditems(self):
query = query_decideditems


class LivingItemsAdapter(CompoundCriterionBaseAdapter):

@property
@ram.cache(query_meeting_config_modified_cachekey)
def query_livingitems(self):
'''Queries living items, items not decided yet.'''
if not self.cfg:
return {}
return {'portal_type': {'query': self.cfg.getItemTypeName()},
'review_state': {'not': self.cfg.getItemDecidedStates()}, }

# we may not ram.cache methods in same file with same name...
query = query_livingitems


class PersonalLabelsAdapter(CompoundCriterionBaseAdapter):

@property
Expand Down Expand Up @@ -1657,7 +1693,7 @@ def _apply_visible_groups_security(self, group_ids):
"every _creators groups" and it is not possible to give the
'AnnexReader' role to all these _creators groups."""
if self.parent.getTagName() == 'MeetingItem' or \
self.parent.portal_type in self.tool.getAdvicePortalTypeIds():
self.parent.portal_type in getAdvicePortalTypeIds():
# reinitialize permissions in case no more confidential
# or confidentiality configuration changed
self.context.__ac_local_roles_block__ = False
Expand Down
27 changes: 27 additions & 0 deletions src/Products/PloneMeeting/behaviors/advice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
#
# File: behaviors.py
#
# GNU General Public License (GPL)
#

from plone.app.textfield import RichText
from plone.autoform.interfaces import IFormFieldProvider
from plone.directives import form
from plone.supermodel import model
from Products.PloneMeeting.config import PMMessageFactory as _
from Products.PloneMeeting.widgets.pm_richtext import PMRichTextFieldWidget
from zope.interface import alsoProvides


class IAdviceAccountingCommitmentBehavior(model.Schema):

form.order_before(advice_accounting_commitment='advice_reference')
form.widget('advice_accounting_commitment', PMRichTextFieldWidget)
advice_accounting_commitment = RichText(
title=_(u"title_advice_accounting_commitment"),
required=False,
allowed_mime_types=(u"text/html", ))


alsoProvides(IAdviceAccountingCommitmentBehavior, IFormFieldProvider)
13 changes: 13 additions & 0 deletions src/Products/PloneMeeting/behaviors/configure.zcml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:plone="http://namespaces.plone.org/plone"
i18n_domain="PloneMeeting">

<include package="plone.behavior" file="meta.zcml" />

<plone:behavior
title="Advice accounting commitment field"
description="Add a rich text field to encode advice accounting commitment."
provides=".advice.IAdviceAccountingCommitmentBehavior" />

</configure>
4 changes: 0 additions & 4 deletions src/Products/PloneMeeting/behaviors/overrides.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
# -*- coding: utf-8 -*-
#
# File: adapters.py
#
# Copyright (c) 2019 by Imio.be
#
# GNU General Public License (GPL)
#

Expand Down
12 changes: 6 additions & 6 deletions src/Products/PloneMeeting/browser/advicechangedelay.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from copy import deepcopy
from DateTime import DateTime
from imio.helpers.cache import get_current_user_id
from imio.helpers.content import get_user_fullname
from plone import api
from plone.z3cform.layout import wrap_form
from Products.CMFCore.permissions import ModifyPortalContent
Expand Down Expand Up @@ -113,8 +114,6 @@ def _mayAccessDelayChangesHistory(self):
advice_uid in userAdviserOrgUids or \
self.context.getProposingGroup() in self.tool.get_orgs_for_user():
return True
else:
return False

def _mayReinitializeDelay(self, advice_uid=None):
'''May current user reinitialize delau for given advice_uid?
Expand Down Expand Up @@ -234,9 +233,8 @@ def handleSaveAdviceDelay(self, action):
self.context.adviceIndex[currentAdviceData['org']]['delay_for_automatic_adviser_changed_manually'] = True
self.context.update_local_roles()
# add a line to the item's adviceIndex advice delay_changes_history
member_id = get_current_user_id()
history_data = {'action': (currentAdviceData['delay'], newAdviceData['delay']),
'actor': member_id,
'actor': get_current_user_id(),
'time': DateTime(),
'comments': data['comment']}
self.context.adviceIndex[currentAdviceData['org']]['delay_changes_history'].append(history_data)
Expand Down Expand Up @@ -301,6 +299,9 @@ def getHistoryInfos(self):
raise Unauthorized
return deepcopy(self.context.adviceIndex[advice_uid])

def get_user_fullname(self, user_id):
return get_user_fullname(user_id)


def _reinit_advice_delay(item, advice_uid):
'''Reinitialize advice delay for given p_item p_advice_uid.'''
Expand All @@ -325,9 +326,8 @@ def __call__(self):
raise Unauthorized
# reinit delay and add a line to the item's adviceIndex advice delay_changes_history
_reinit_advice_delay(self.context, advice_uid)
member_id = get_current_user_id()
history_data = {'action': 'Reinitiatlize delay',
'actor': member_id,
'actor': get_current_user_id(),
'time': DateTime(),
'comments': None}
adviceInfos = self.context.adviceIndex[advice_uid]
Expand Down

0 comments on commit 92e543f

Please sign in to comment.