Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/news/6.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add optional presenter roles behavior to let sessions inherit local roles from their connected presenters @datakurre
12 changes: 12 additions & 0 deletions backend/src/collective/techevent/behaviors/configure.zcml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,16 @@
provides=".session.IEventSession"
/>

<plone:behavior
name="collective.techevent.presenter_roles"
title="Tech Event: Apply presenter roles"
description=""
provides=".presenter.IPresenterLocalRoles"
/>

<adapter
factory=".presenter.PresenterRoleProvider"
name="collective.techevent.presenter_roles"
/>

</configure>
57 changes: 57 additions & 0 deletions backend/src/collective/techevent/behaviors/presenter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
from borg.localrole.interfaces import ILocalRoleProvider
from plone import api
from z3c.relationfield import RelationValue
from zope.component import adapter
from zope.interface import implementer
from zope.interface import Interface


class IPresenterLocalRoles(Interface):
"""Marker interface for the local roles of a presenter."""


@implementer(ILocalRoleProvider)
@adapter(IPresenterLocalRoles)
class PresenterRoleProvider:
def __init__(self, context):
self.context = context

def getRoles(self, user_id):
"""
Return the roles assigned to the given user ID in the context of presenters for the current event session.
"""
mtool = api.portal.get_tool("portal_membership")
user = mtool.getMemberById(user_id)
if not user:
return []
roles = set()
presenters = getattr(self.context, "presenters", [])
for presenter_relation in presenters:
if (
isinstance(presenter_relation, RelationValue)
and presenter_relation.to_object
):
presenter = presenter_relation.to_object
roles.update(user.getRolesInContext(presenter))
return list(roles)

def getAllRoles(self):
"""
Retrieve all roles assigned to users in the context of presenters for the current event session.

This method merges roles from all presenters for each user. It does not consider additional role adapters.
"""
roles = {}
presenters = getattr(self.context, "presenters", [])
for presenter_relation in presenters:
if (
isinstance(presenter_relation, RelationValue)
and presenter_relation.to_object
):
presenter = presenter_relation.to_object
local_roles = presenter.get_local_roles()
for user_id, user_roles in local_roles:
if user_id not in roles:
roles[user_id] = set()
roles[user_id].update(user_roles)
return [(user_id, tuple(user_roles)) for user_id, user_roles in roles.items()]
15 changes: 15 additions & 0 deletions backend/tests/content_types/schedule/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ def func(portal_type: str):
return brains

return func


@pytest.fixture
def enable_presenter_roles():
def func(portal: DexterityContent, portal_type: str):
with api.env.adopt_roles(["Manager"]):
fti = portal.portal_types.get(portal_type)
if fti:
behaviors = list(fti.behaviors)
if "collective.techevent.presenter_roles" not in behaviors:
behaviors.append("collective.techevent.presenter_roles")
fti.behaviors = tuple(behaviors)
return fti

return func
81 changes: 81 additions & 0 deletions backend/tests/content_types/schedule/test_ct_talk.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from collective.techevent.content.schedule.talk import Talk
from plone.app.testing import TEST_USER_ID
from z3c.relationfield import RelationValue
from zope.component import getUtility
from zope.intid.interfaces import IIntIds

import pytest

Expand All @@ -8,6 +12,16 @@ def portal_type() -> str:
return "Talk"


@pytest.fixture
def test_user(portal):
return portal.acl_users.getUserById(TEST_USER_ID)


@pytest.fixture
def intids():
return getUtility(IIntIds)


class TestContentType:
@pytest.fixture(autouse=True)
def _setup(self, container):
Expand Down Expand Up @@ -65,3 +79,70 @@ def test_create_version_on_save(
version = last_version(content_instance)
assert version.comment is None
assert version.version_id == 1


@pytest.fixture
def setup_talk_and_presenter(
portal,
container,
intids,
content_factory,
payloads,
portal_type,
):
def _setup(enable_roles=False, enable_presenter_roles=None):
if enable_roles and enable_presenter_roles:
enable_presenter_roles(portal, portal_type)

talk = content_factory(container, payloads[portal_type][0])
talk.__ac_local_roles__ = {}
talk.__ac_local_roles_block__ = True

presenter = content_factory(portal, payloads["Presenter"][0])
presenter.__ac_local_roles_block__ = True

talk.presenters = [RelationValue(intids.getId(presenter))]

return talk, presenter

return _setup


class TestPresenterRolesDisabled:
@pytest.fixture(autouse=True)
def _setup(self, setup_talk_and_presenter):
self.talk, self.presenter = setup_talk_and_presenter()

def test_talk_without_presenter_roles(
self,
portal,
test_user,
):
talk_roles = portal.acl_users._getAllLocalRoles(self.talk)
presenter_roles = portal.acl_users._getAllLocalRoles(self.presenter)

assert talk_roles == {}
assert presenter_roles != {}
assert talk_roles != presenter_roles
assert "Owner" not in test_user.getRolesInContext(self.talk)


class TestPresenterRolesEnabled:
@pytest.fixture(autouse=True)
def _setup(self, setup_talk_and_presenter, enable_presenter_roles):
self.talk, self.presenter = setup_talk_and_presenter(
enable_roles=True, enable_presenter_roles=enable_presenter_roles
)

def test_talk_with_presenter_roles(
self,
portal,
test_user,
):
talk_roles = portal.acl_users._getAllLocalRoles(self.talk)
presenter_roles = portal.acl_users._getAllLocalRoles(self.presenter)

assert talk_roles != {}
assert presenter_roles != {}
assert talk_roles == presenter_roles
assert "Owner" in test_user.getRolesInContext(self.talk)
2 changes: 1 addition & 1 deletion backend/tests/services/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def portal(functional):
portal = functional["portal"]
setSite(portal)
tool: SetupTool = api.portal.get_tool("portal_setup")
with api.env.adopt_roles(["Manager", "Member"]):
with api.env.adopt_roles(["Manager"]):
tool.runAllImportStepsFromProfile(f"{PACKAGE_NAME}:demo")
transaction.commit()
return portal
Expand Down