forked from CGM_Public/pretix_original
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:
@@ -551,8 +551,7 @@ class Event(EventMixin, LoggedModel):
|
||||
:type presale_end: datetime
|
||||
:param location: venue
|
||||
:type location: str
|
||||
:param plugins: A comma-separated list of plugin names that are active for this
|
||||
event.
|
||||
:param plugins: A comma-separated list of plugin names that are active for this event.
|
||||
:type plugins: str
|
||||
:param has_subevents: Enable event series functionality
|
||||
:type has_subevents: bool
|
||||
@@ -1393,7 +1392,7 @@ class Event(EventMixin, LoggedModel):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return {
|
||||
p.module: p for p in get_all_plugins(self)
|
||||
p.module: p for p in get_all_plugins(event=self)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
|
||||
@@ -1412,12 +1411,20 @@ class Event(EventMixin, LoggedModel):
|
||||
self.plugins = ",".join(modules)
|
||||
|
||||
def enable_plugin(self, module, allow_restricted=frozenset()):
|
||||
"""
|
||||
Adds a plugin to the list of plugins, calling its ``installed`` hook (if available).
|
||||
It is the caller's responsibility to save the event object.
|
||||
"""
|
||||
plugins_active = self.get_plugins()
|
||||
if module not in plugins_active:
|
||||
plugins_active.append(module)
|
||||
self.set_active_plugins(plugins_active, allow_restricted=allow_restricted)
|
||||
|
||||
def disable_plugin(self, module):
|
||||
"""
|
||||
Adds a plugin to the list of plugins, calling its ``uninstalled`` hook (if available).
|
||||
It is the caller's responsibility to save the event object.
|
||||
"""
|
||||
plugins_active = self.get_plugins()
|
||||
if module in plugins_active:
|
||||
plugins_active.remove(module)
|
||||
|
||||
@@ -40,9 +40,6 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import connections, models
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from pretix.base.logentrytype_registry import log_entry_types, make_link
|
||||
from pretix.base.signals import is_app_active, logentry_object_link
|
||||
|
||||
|
||||
class VisibleOnlyManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
@@ -91,6 +88,8 @@ class LogEntry(models.Model):
|
||||
indexes = [models.Index(fields=["datetime", "id"])]
|
||||
|
||||
def display(self):
|
||||
from pretix.base.logentrytype_registry import log_entry_types
|
||||
|
||||
log_entry_type, meta = log_entry_types.get(action_type=self.action_type)
|
||||
if log_entry_type:
|
||||
return log_entry_type.display(self, self.parsed_data)
|
||||
@@ -128,6 +127,11 @@ class LogEntry(models.Model):
|
||||
|
||||
@cached_property
|
||||
def display_object(self):
|
||||
from pretix.base.logentrytype_registry import (
|
||||
log_entry_types, make_link,
|
||||
)
|
||||
from pretix.base.signals import is_app_active, logentry_object_link
|
||||
|
||||
from . import (
|
||||
Discount, Event, Item, Order, Question, Quota, SubEvent, Voucher,
|
||||
)
|
||||
|
||||
@@ -68,6 +68,8 @@ class Organizer(LoggedModel):
|
||||
:param slug: A globally unique, short name for this organizer, to be used
|
||||
in URLs and similar places.
|
||||
:type slug: str
|
||||
:param plugins: A comma-separated list of plugin names that are active for this organizer.
|
||||
:type plugins: str
|
||||
"""
|
||||
|
||||
settings_namespace = 'organizer'
|
||||
@@ -91,6 +93,10 @@ class Organizer(LoggedModel):
|
||||
verbose_name=_("Short form"),
|
||||
unique=True
|
||||
)
|
||||
plugins = models.TextField(
|
||||
verbose_name=_("Plugins"),
|
||||
null=False, blank=True, default="",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Organizer")
|
||||
@@ -119,6 +125,11 @@ class Organizer(LoggedModel):
|
||||
"""
|
||||
self.settings.cookie_consent = True
|
||||
|
||||
plugins = [p for p in settings.PRETIX_PLUGINS_ORGANIZER_DEFAULT.split(",") if p]
|
||||
if plugins:
|
||||
self.set_active_plugins(plugins, allow_restricted=plugins)
|
||||
self.save()
|
||||
|
||||
def get_cache(self):
|
||||
"""
|
||||
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
|
||||
@@ -143,6 +154,61 @@ class Organizer(LoggedModel):
|
||||
|
||||
return ObjectRelatedCache(self)
|
||||
|
||||
def get_plugins(self):
|
||||
"""
|
||||
Returns the names of the plugins activated for this organizer as a list.
|
||||
"""
|
||||
if not self.plugins:
|
||||
return []
|
||||
return self.plugins.split(",")
|
||||
|
||||
def get_available_plugins(self):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return {
|
||||
p.module: p for p in get_all_plugins(organizer=self)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
|
||||
def set_active_plugins(self, modules, allow_restricted=frozenset()):
|
||||
plugins_active = self.get_plugins()
|
||||
plugins_available = self.get_available_plugins()
|
||||
|
||||
enable = [m for m in modules if m not in plugins_active and m in plugins_available]
|
||||
|
||||
for module in enable:
|
||||
if getattr(plugins_available[module].app, 'restricted', False) and module not in allow_restricted:
|
||||
modules.remove(module)
|
||||
elif hasattr(plugins_available[module].app, 'installed'):
|
||||
getattr(plugins_available[module].app, 'installed')(self)
|
||||
|
||||
self.plugins = ",".join(modules)
|
||||
|
||||
def enable_plugin(self, module, allow_restricted=frozenset()):
|
||||
"""
|
||||
Adds a plugin to the list of plugins, calling its ``installed`` hook (if available).
|
||||
It is the caller's responsibility to save the organizer object.
|
||||
"""
|
||||
plugins_active = self.get_plugins()
|
||||
if module not in plugins_active:
|
||||
plugins_active.append(module)
|
||||
self.set_active_plugins(plugins_active, allow_restricted=allow_restricted)
|
||||
|
||||
def disable_plugin(self, module):
|
||||
"""
|
||||
Removes a plugin from the list of plugins, calling its ``uninstalled`` hook (if available).
|
||||
It is the caller's responsibility to save the organizer object and, in case of a hybrid organizer-event plugin,
|
||||
to remove it from all events.
|
||||
"""
|
||||
plugins_active = self.get_plugins()
|
||||
if module in plugins_active:
|
||||
plugins_active.remove(module)
|
||||
self.set_active_plugins(plugins_active)
|
||||
|
||||
plugins_available = self.get_available_plugins()
|
||||
if module in plugins_available and hasattr(plugins_available[module].app, 'uninstalled'):
|
||||
getattr(plugins_available[module].app, 'uninstalled')(self)
|
||||
|
||||
@property
|
||||
def timezone(self):
|
||||
return pytz_deprecation_shim.timezone(self.settings.timezone)
|
||||
|
||||
Reference in New Issue
Block a user