Organizer-level plugins (#5305)

* Add version notes to the docs

* Adapt signal handling

* Add UI

* Add API

* API and tests

* Fix registry

* Update doc/development/api/plugins.rst

Co-authored-by: Felix Rindt <felix@rindt.me>

* Fix failing tests

* Apply suggestions from code review

Co-authored-by: Richard Schreiber <schreiber@rami.io>

* Update src/pretix/control/templates/pretixcontrol/organizers/plugin_events.html

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/control/templates/pretixcontrol/organizers/plugins.html

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/control/templates/pretixcontrol/organizers/plugins.html

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/control/navigation.py

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/control/urls.py

Co-authored-by: luelista <weller@rami.io>

* Apply suggestion from @wiffbi

* REbase migration

* Fix review note

* Fix test cases

* Remove plugin from all events if disabled on org level

* Update doc/development/api/plugins.rst

* Unify registries

* Rebase migration

---------

Co-authored-by: Felix Rindt <felix@rindt.me>
Co-authored-by: Richard Schreiber <schreiber@rami.io>
Co-authored-by: luelista <weller@rami.io>
This commit is contained in:
Raphael Michel
2025-08-19 11:33:34 +02:00
committed by GitHub
parent 56964b6764
commit a51a6123f5
50 changed files with 1623 additions and 192 deletions

View File

@@ -327,6 +327,28 @@ class EventsTest(SoupTest):
self.event1.refresh_from_db()
assert "testdummyrestricted" in self.event1.plugins
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyorga': 'enable'})
self.event1.refresh_from_db()
assert "testdummyorga" not in self.event1.plugins
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyhybrid': 'enable'})
self.event1.refresh_from_db()
assert "tests.testdummyhybrid" not in self.event1.plugins
self.orga1.refresh_from_db()
assert "tests.testdummyhybrid" not in self.orga1.plugins
t2 = Team.objects.create(organizer=self.orga1, can_change_organizer_settings=True)
t2.members.add(self.user)
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
{'plugin:tests.testdummyhybrid': 'enable'})
self.event1.refresh_from_db()
assert "tests.testdummyhybrid" in self.event1.plugins
self.orga1.refresh_from_db()
assert "tests.testdummyhybrid" in self.orga1.plugins
def test_testmode_enable(self):
self.event1.testmode = False
self.event1.save()

View File

@@ -380,3 +380,76 @@ class OrganizerTest(SoupTest):
assert doc.select('.alert-danger')
with scopes_disabled():
assert self.orga1.sales_channels.filter(identifier="web").exists()
def test_plugins(self):
doc = self.get_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug)
self.assertIn("Stripe", doc.select(".form-plugins")[0].text)
self.assertIn("Enable", doc.select("[name=\"plugin:tests.testdummyorga\"]")[0].text)
self.assertIn("Enable", doc.select("[name=\"plugin:tests.testdummyhybrid\"]")[0].text)
assert not doc.select("[name=\"plugin:pretix.plugins.stripe\"]")
assert not doc.select("[name=\"plugin:tests.testdummy\"]")
assert not doc.select("[name=\"plugin:tests.testdummyrestricted\"]")
assert not doc.select("[name=\"plugin:tests.testdummyorgarestricted\"]")
assert not doc.select("[name=\"plugin:tests.testdummyhidden\"]")
doc = self.post_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug,
{'plugin:tests.testdummyorga': 'enable'})
self.assertIn("Disable", doc.select("[name=\"plugin:tests.testdummyorga\"]")[0].text)
doc = self.post_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug,
{'plugin:tests.testdummyhybrid': 'enable'})
self.assertIn("Events with plugin testdummyhybrid", doc.select("h1")[0].text)
self.orga1.refresh_from_db()
assert "tests.testdummyhybrid" in self.orga1.get_plugins()
doc = self.post_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug,
{'plugin:tests.testdummyhybrid': 'disable'})
self.assertIn("Enable", doc.select("[name=\"plugin:tests.testdummyhybrid\"]")[0].text)
self.post_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug,
{'plugin:tests.testdummy': 'enable'})
self.orga1.refresh_from_db()
assert "tests.testdummy" not in self.orga1.get_plugins()
self.post_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug,
{'plugin:tests.testdummyorgarestricted': 'enable'})
self.orga1.refresh_from_db()
assert "testdummyorgarestricted" not in self.orga1.get_plugins()
self.orga1.settings.allowed_restricted_plugins = ["tests.testdummyorgarestricted"]
self.post_doc('/control/organizer/%s/settings/plugins' % self.orga1.slug,
{'plugin:tests.testdummyorgarestricted': 'enable'})
self.orga1.refresh_from_db()
assert "tests.testdummyorgarestricted" in self.orga1.get_plugins()
def test_plugin_events(self):
resp = self.client.get('/control/organizer/%s/settings/plugins/tests.testdummyorga/events' % self.orga1.slug)
assert resp.status_code == 404
assert b"only be enabled for the entire organizer account" in resp.content
resp = self.client.get(
'/control/organizer/%s/settings/plugins/tests.testdummyrestricted/events' % self.orga1.slug)
assert resp.status_code == 404
assert b"currently not allowed" in resp.content
resp = self.client.get('/control/organizer/%s/settings/plugins/tests.testdummyhybrid/events' % self.orga1.slug)
assert resp.status_code == 404
assert b"currently not active on the organizer" in resp.content
resp = self.client.get('/control/organizer/%s/settings/plugins/pretix.plugins.stripe/events' % self.orga1.slug)
assert resp.status_code == 200
resp = self.client.post('/control/organizer/%s/settings/plugins/pretix.plugins.stripe/events' % self.orga1.slug,
{'events': self.event1.pk})
assert resp.status_code == 302
self.event1.refresh_from_db()
assert 'pretix.plugins.stripe' in self.event1.get_plugins()
assert 'pretix.plugins.banktransfer' in self.event1.get_plugins()
resp = self.client.post('/control/organizer/%s/settings/plugins/pretix.plugins.banktransfer/events' % self.orga1.slug,
{})
assert resp.status_code == 302
self.event1.refresh_from_db()
assert 'pretix.plugins.banktransfer' not in self.event1.get_plugins()
assert 'pretix.plugins.stripe' in self.event1.get_plugins()

View File

@@ -183,6 +183,8 @@ event_urls = [
organizer_urls = [
'organizer/abc/edit',
'organizer/abc/',
'organizer/abc/settings/plugins',
'organizer/abc/settings/plugins/pretix.plugins.sendmail/events',
'organizer/abc/settings/email',
'organizer/abc/settings/email/setup',
'organizer/abc/teams',
@@ -287,7 +289,7 @@ def test_wrong_event(perf_patch, client, env, url):
organizer=env[2], name='Dummy', slug='dummy2',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
t = Team.objects.create(organizer=env[2], can_change_event_settings=True)
t = Team.objects.create(pk=2, organizer=env[2], can_change_event_settings=True)
t.members.add(env[1])
t.limit_events.add(event2)
@@ -418,7 +420,7 @@ event_permission_urls = [
@pytest.mark.parametrize("perm,url,code,http_method", event_permission_urls)
def test_wrong_event_permission(perf_patch, client, env, perm, url, code, http_method):
t = Team(
organizer=env[2], all_events=True
pk=2, organizer=env[2], all_events=True
)
setattr(t, perm, False)
t.save()
@@ -438,7 +440,7 @@ def test_limited_event_permission_for_other_event(perf_patch, client, env, perm,
organizer=env[2], name='Dummy', slug='dummy2',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
t = Team.objects.create(organizer=env[2], can_change_event_settings=True)
t = Team.objects.create(pk=2, organizer=env[2], can_change_event_settings=True)
t.members.add(env[1])
t.limit_events.add(event2)
@@ -453,7 +455,7 @@ def test_limited_event_permission_for_other_event(perf_patch, client, env, perm,
@pytest.mark.django_db
def test_current_permission(client, env):
t = Team(
organizer=env[2], all_events=True
pk=2, organizer=env[2], all_events=True
)
setattr(t, 'can_change_event_settings', True)
t.save()
@@ -471,7 +473,7 @@ def test_current_permission(client, env):
@pytest.mark.django_db
@pytest.mark.parametrize("perm,url,code,http_method", event_permission_urls)
def test_correct_event_permission_all_events(perf_patch, client, env, perm, url, code, http_method):
t = Team(organizer=env[2], all_events=True)
t = Team(pk=2, organizer=env[2], all_events=True)
setattr(t, perm, True)
t.save()
t.members.add(env[1])
@@ -489,7 +491,7 @@ def test_correct_event_permission_all_events(perf_patch, client, env, perm, url,
@pytest.mark.django_db
@pytest.mark.parametrize("perm,url,code,http_method", event_permission_urls)
def test_correct_event_permission_limited(perf_patch, client, env, perm, url, code, http_method):
t = Team(organizer=env[2])
t = Team(pk=2, organizer=env[2])
setattr(t, perm, True)
t.save()
t.members.add(env[1])
@@ -522,6 +524,8 @@ organizer_permission_urls = [
("can_change_teams", "organizer/dummy/team/1/edit", 200),
("can_change_teams", "organizer/dummy/team/1/delete", 200),
("can_change_organizer_settings", "organizer/dummy/edit", 200),
("can_change_organizer_settings", "organizer/dummy/settings/plugins", 200),
("can_change_organizer_settings", "organizer/dummy/settings/plugins/pretix.plugins.sendmail/events", 200),
("can_change_organizer_settings", "organizer/dummy/settings/email", 200),
("can_change_organizer_settings", "organizer/dummy/settings/email/setup", 200),
("can_change_organizer_settings", "organizer/dummy/devices", 200),
@@ -580,7 +584,7 @@ organizer_permission_urls = [
@pytest.mark.django_db
@pytest.mark.parametrize("perm,url,code", organizer_permission_urls)
def test_wrong_organizer_permission(perf_patch, client, env, perm, url, code):
t = Team(organizer=env[2])
t = Team(pk=2, organizer=env[2])
setattr(t, perm, False)
t.save()
t.members.add(env[1])
@@ -592,7 +596,7 @@ def test_wrong_organizer_permission(perf_patch, client, env, perm, url, code):
@pytest.mark.django_db
@pytest.mark.parametrize("perm,url,code", organizer_permission_urls)
def test_correct_organizer_permission(perf_patch, client, env, perm, url, code):
t = Team(organizer=env[2])
t = Team(pk=2, organizer=env[2])
setattr(t, perm, True)
t.save()
t.members.add(env[1])