mirror of
https://github.com/pretix/pretix.git
synced 2026-09-25 18:14:41 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb5ba65a28 | ||
|
|
e518b3f06c | ||
|
|
88a0482838 | ||
|
|
7cfa454c2b | ||
|
|
576b55a995 | ||
|
|
313beb1122 | ||
|
|
a89ebddbc4 |
@@ -268,22 +268,14 @@ class EventSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
||||
return {'seat_category_mapping': result}
|
||||
|
||||
def validate_plugins(self, value):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
plugins_available = {
|
||||
p.module: p for p in get_all_plugins(event=self.instance)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
current_plugins = self.instance.get_plugins() if self.instance and self.instance.pk else []
|
||||
settings_holder = self.instance if self.instance and self.instance.pk else self.context['organizer']
|
||||
obj = self.instance if self.instance and self.instance.pk else self.context['organizer']
|
||||
plugins_available = obj.get_available_plugins(filter_restricted=True)
|
||||
|
||||
allowed_levels = (PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID)
|
||||
for plugin in value.get('plugins'):
|
||||
if plugin not in plugins_available:
|
||||
raise ValidationError(_('Unknown plugin: \'{name}\'.').format(name=plugin))
|
||||
if getattr(plugins_available[plugin], 'restricted', False):
|
||||
if plugin not in settings_holder.settings.allowed_restricted_plugins:
|
||||
raise ValidationError(_('Restricted plugin: \'{name}\'.').format(name=plugin))
|
||||
raise ValidationError(_('Unknown or restricted plugin: \'{name}\'.').format(name=plugin))
|
||||
level = getattr(plugins_available[plugin], 'level', PLUGIN_LEVEL_EVENT)
|
||||
if level not in allowed_levels:
|
||||
raise ValidationError('Plugin cannot be enabled on this level: \'{name}\'.'.format(name=plugin))
|
||||
|
||||
@@ -114,11 +114,11 @@ class UploadedFileField(serializers.Field):
|
||||
class PluginsField(serializers.Field):
|
||||
|
||||
def to_representation(self, obj):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.plugins import iter_all_plugins
|
||||
active_plugins = set(obj.get_plugins())
|
||||
return sorted([
|
||||
p.module for p in get_all_plugins()
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True) and p.module in active_plugins
|
||||
p.module for p in iter_all_plugins(only_visible=True)
|
||||
if p.module in active_plugins
|
||||
])
|
||||
|
||||
def to_internal_value(self, data):
|
||||
|
||||
@@ -80,21 +80,12 @@ class OrganizerSerializer(I18nAwareModelSerializer):
|
||||
fields = ('name', 'slug', 'public_url', 'plugins')
|
||||
|
||||
def validate_plugins(self, value):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
plugins_available = {
|
||||
p.module: p for p in get_all_plugins(organizer=self.instance)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
settings_holder = self.instance
|
||||
plugins_available = self.instance.get_available_plugins(filter_restricted=True)
|
||||
|
||||
allowed_levels = (PLUGIN_LEVEL_ORGANIZER, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID)
|
||||
for plugin in value.get('plugins'):
|
||||
if plugin not in plugins_available:
|
||||
raise ValidationError(_('Unknown plugin: \'{name}\'.').format(name=plugin))
|
||||
if getattr(plugins_available[plugin], 'restricted', False):
|
||||
if plugin not in settings_holder.settings.allowed_restricted_plugins:
|
||||
raise ValidationError(_('Restricted plugin: \'{name}\'.').format(name=plugin))
|
||||
raise ValidationError(_('Unknown or restricted plugin: \'{name}\'.').format(name=plugin))
|
||||
if getattr(plugins_available[plugin], 'level', PLUGIN_LEVEL_EVENT) not in allowed_levels:
|
||||
raise ValidationError('Plugin cannot be enabled on this level: \'{name}\'.'.format(name=plugin))
|
||||
|
||||
|
||||
@@ -342,6 +342,23 @@ class EventViewSet(viewsets.ModelViewSet):
|
||||
raise PermissionDenied('The event could not be deleted as some constraints (e.g. data created by plug-ins) '
|
||||
'do not allow it.')
|
||||
|
||||
@action(detail=True, methods=['GET'])
|
||||
def available_plugins(self, *args, **kwargs):
|
||||
from pretix.base.plugins import get_all_plugins, PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID
|
||||
plugins_available = [
|
||||
{
|
||||
"plugin": p.module,
|
||||
"name": p.name,
|
||||
"level": getattr(p, "level", PLUGIN_LEVEL_EVENT),
|
||||
"restricted": ("allowed" if p.module in self.request.event.settings.allowed_restricted_plugins else "restricted") if getattr(p, "restricted", False) else "no",
|
||||
}
|
||||
for p in get_all_plugins(event=self.request.event, only_visible=True)
|
||||
if getattr(p, "level", PLUGIN_LEVEL_EVENT) in (PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID, PLUGIN_LEVEL_EVENT)
|
||||
]
|
||||
return Response({
|
||||
'results': plugins_available,
|
||||
})
|
||||
|
||||
|
||||
class CloneEventViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = CloneEventSerializer
|
||||
|
||||
@@ -92,8 +92,6 @@ class OrganizerViewSet(mixins.UpdateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
|
||||
@transaction.atomic()
|
||||
def perform_update(self, serializer):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
original_data = self.get_serializer(instance=serializer.instance).data
|
||||
|
||||
current_plugins_value = serializer.instance.get_plugins()
|
||||
@@ -111,11 +109,7 @@ class OrganizerViewSet(mixins.UpdateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
disabled = {m: 'disabled' for m in current_plugins_value if m not in updated_plugins_value}
|
||||
changed = merge_dicts(enabled, disabled)
|
||||
|
||||
plugins_available = {
|
||||
p.module: p
|
||||
for p in get_all_plugins(organizer=serializer.instance)
|
||||
if not p.name.startswith('.') and getattr(p, 'visible', True)
|
||||
}
|
||||
plugins_available = serializer.instance.get_available_plugins()
|
||||
qs = []
|
||||
for module in disabled:
|
||||
pluginmeta = plugins_available[module]
|
||||
@@ -151,6 +145,22 @@ class OrganizerViewSet(mixins.UpdateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
data={'plugin': module}
|
||||
)
|
||||
|
||||
@action(detail=True, methods=['GET'])
|
||||
def available_plugins(self, *args, **kwargs):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
plugins_available = [
|
||||
{
|
||||
"plugin": p.module,
|
||||
"name": p.name,
|
||||
"level": getattr(p, "level", PLUGIN_LEVEL_EVENT),
|
||||
"restricted": ("allowed" if p.module in self.request.organizer.settings.allowed_restricted_plugins else "restricted") if getattr(p, "restricted", False) else "no",
|
||||
}
|
||||
for p in get_all_plugins(organizer=self.request.organizer, only_visible=True)
|
||||
]
|
||||
return Response({
|
||||
'results': plugins_available
|
||||
})
|
||||
|
||||
|
||||
class SeatingPlanViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = SeatingPlanSerializer
|
||||
|
||||
@@ -274,3 +274,55 @@ class LockModel:
|
||||
field.delete_cached_value(self)
|
||||
|
||||
self._state.db = db_instance._state.db
|
||||
|
||||
|
||||
class PluginsMixin:
|
||||
|
||||
def get_plugins(self):
|
||||
"""
|
||||
Returns the names of the plugins activated for this model as a list.
|
||||
"""
|
||||
if self.plugins is None:
|
||||
return []
|
||||
return self.plugins.split(",")
|
||||
|
||||
def set_active_plugins(self, modules, allow_restricted=frozenset()):
|
||||
plugins_available = self.get_available_plugins()
|
||||
plugins_current = set(self.get_plugins())
|
||||
plugins_new = set(modules)
|
||||
|
||||
for module in plugins_new - plugins_current:
|
||||
if module not in plugins_available:
|
||||
continue
|
||||
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)
|
||||
|
||||
for module in plugins_current - plugins_new:
|
||||
if module in plugins_available and hasattr(plugins_available[module].app, 'uninstalled'):
|
||||
getattr(plugins_available[module].app, 'uninstalled')(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 model object, as well as, in case of enabling
|
||||
a hybrid organizer-event plugin on an event, to enable it on the organizer, if necessary.
|
||||
"""
|
||||
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 model object, as well as, in case of disabling
|
||||
a hybrid organizer-event plugin on an organizer, 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)
|
||||
|
||||
@@ -67,7 +67,7 @@ from django.utils.translation import gettext, gettext_lazy as _
|
||||
from django_scopes import ScopedManager, scopes_disabled
|
||||
from i18nfield.fields import I18nCharField, I18nTextField
|
||||
|
||||
from pretix.base.models.base import LoggedModel
|
||||
from pretix.base.models.base import LoggedModel, PluginsMixin
|
||||
from pretix.base.reldate import RelativeDateWrapper
|
||||
from pretix.base.timemachine import time_machine_now
|
||||
from pretix.base.validators import EventSlugBanlistValidator
|
||||
@@ -563,7 +563,7 @@ def default_sales_channels(): # kept for legacy migration
|
||||
|
||||
|
||||
@settings_hierarkey.add(parent_field='organizer', cache_namespace='event')
|
||||
class Event(EventMixin, LoggedModel):
|
||||
class Event(PluginsMixin, EventMixin, LoggedModel):
|
||||
"""
|
||||
This model represents an event. An event is anything you can buy
|
||||
tickets for.
|
||||
@@ -800,14 +800,6 @@ class Event(EventMixin, LoggedModel):
|
||||
self.cache.clear()
|
||||
return obj
|
||||
|
||||
def get_plugins(self):
|
||||
"""
|
||||
Returns the names of the plugins activated for this event as a list.
|
||||
"""
|
||||
if self.plugins is None:
|
||||
return []
|
||||
return self.plugins.split(",")
|
||||
|
||||
def get_cache(self):
|
||||
"""
|
||||
Returns an :py:class:`ObjectRelatedCache` object. This behaves equivalent to
|
||||
@@ -1474,51 +1466,10 @@ class Event(EventMixin, LoggedModel):
|
||||
self.items.all().delete()
|
||||
self.subevents.all().delete()
|
||||
|
||||
def get_available_plugins(self):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
def get_available_plugins(self, filter_restricted=False):
|
||||
from pretix.base.plugins import get_all_plugins_map, ALLOW_ALL
|
||||
|
||||
return {
|
||||
p.module: p for p in get_all_plugins(event=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 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)
|
||||
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)
|
||||
return get_all_plugins_map(event=self, only_visible=True, allow_restricted=self.settings.allowed_restricted_plugins if filter_restricted else ALLOW_ALL)
|
||||
|
||||
@staticmethod
|
||||
def clean_has_subevents(event, has_subevents):
|
||||
|
||||
@@ -52,7 +52,7 @@ from django_scopes import ScopedManager, scope
|
||||
from i18nfield.fields import I18nCharField
|
||||
from i18nfield.strings import LazyI18nString
|
||||
|
||||
from pretix.base.models.base import LoggedModel
|
||||
from pretix.base.models.base import LoggedModel, PluginsMixin
|
||||
from pretix.base.validators import OrganizerSlugBanlistValidator
|
||||
|
||||
from ...helpers.permission_migration import (
|
||||
@@ -67,7 +67,7 @@ if TYPE_CHECKING:
|
||||
|
||||
|
||||
@settings_hierarkey.add(cache_namespace='organizer')
|
||||
class Organizer(LoggedModel):
|
||||
class Organizer(PluginsMixin, LoggedModel):
|
||||
"""
|
||||
This model represents an entity organizing events, e.g. a company, institution,
|
||||
charity, person, …
|
||||
@@ -166,60 +166,10 @@ 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, *, filter_restricted=False):
|
||||
from pretix.base.plugins import get_all_plugins_map, ALLOW_ALL
|
||||
|
||||
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)
|
||||
return get_all_plugins_map(organizer=self, only_visible=True, allow_restricted=self.settings.allowed_restricted_plugins if filter_restricted else ALLOW_ALL)
|
||||
|
||||
@property
|
||||
def timezone(self):
|
||||
|
||||
+42
-18
@@ -22,7 +22,7 @@
|
||||
import os
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
from typing import Iterable, List
|
||||
|
||||
import importlib_metadata as metadata
|
||||
from django.apps import AppConfig, apps
|
||||
@@ -37,6 +37,10 @@ PLUGIN_LEVEL_ORGANIZER = 'organizer'
|
||||
PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID = 'event_organizer'
|
||||
|
||||
|
||||
class ALLOW_ALL:
|
||||
pass
|
||||
|
||||
|
||||
class PluginType(Enum):
|
||||
"""
|
||||
Plugin type classification. THIS IS DEPRECATED, DO NOT USE ANY MORE.
|
||||
@@ -49,7 +53,13 @@ class PluginType(Enum):
|
||||
EXPORT = 4
|
||||
|
||||
|
||||
def plugin_is_available(meta, event=None, organizer=None):
|
||||
def plugin_is_available(meta, event=None, organizer=None, only_visible=False, allow_restricted: type[ALLOW_ALL] | List[str]=ALLOW_ALL):
|
||||
if only_visible and (meta.name.startswith('.') or not getattr(meta, 'visible', True)):
|
||||
return False
|
||||
|
||||
if allow_restricted is not ALLOW_ALL and getattr(meta, 'restricted', False) and meta.module not in allow_restricted:
|
||||
return False
|
||||
|
||||
if not hasattr(meta.app, 'is_available'):
|
||||
return True
|
||||
|
||||
@@ -76,30 +86,44 @@ def plugin_is_available(meta, event=None, organizer=None):
|
||||
return True
|
||||
|
||||
|
||||
def get_all_plugins(*, event=None, organizer=None) -> List[type]:
|
||||
def get_plugin_meta_from_app_config(app):
|
||||
if not hasattr(app, 'PretixPluginMeta'):
|
||||
return None
|
||||
meta = app.PretixPluginMeta
|
||||
meta.module = app.name
|
||||
meta.app = app
|
||||
if app.name in settings.PRETIX_PLUGINS_EXCLUDE:
|
||||
return None
|
||||
return meta
|
||||
|
||||
|
||||
def iter_all_plugins(*, event=None, organizer=None, only_visible=False, allow_restricted: type[ALLOW_ALL] | List[str]=ALLOW_ALL) -> Iterable[type]:
|
||||
assert not event or not organizer
|
||||
for app in apps.get_app_configs():
|
||||
if meta := get_plugin_meta_from_app_config(app):
|
||||
if plugin_is_available(meta, event, organizer, only_visible, allow_restricted):
|
||||
yield meta
|
||||
|
||||
|
||||
def get_all_plugins(*, event=None, organizer=None, only_visible=False, allow_restricted: type[ALLOW_ALL] | List[str]=ALLOW_ALL) -> List[type]:
|
||||
"""
|
||||
Returns the PretixPluginMeta classes of all plugins found in the installed Django apps.
|
||||
|
||||
Of the `event` and `organizer` params, at most one may be filled, and they are only used for
|
||||
calling `is_available`, not for filtering by plugin level.
|
||||
"""
|
||||
assert not event or not organizer
|
||||
plugins = []
|
||||
for app in apps.get_app_configs():
|
||||
if hasattr(app, 'PretixPluginMeta'):
|
||||
meta = app.PretixPluginMeta
|
||||
meta.module = app.name
|
||||
meta.app = app
|
||||
if app.name in settings.PRETIX_PLUGINS_EXCLUDE:
|
||||
continue
|
||||
|
||||
if not plugin_is_available(meta, event, organizer):
|
||||
continue
|
||||
|
||||
plugins.append(meta)
|
||||
return sorted(
|
||||
plugins,
|
||||
iter_all_plugins(event=event, organizer=organizer, only_visible=only_visible, allow_restricted=allow_restricted),
|
||||
key=lambda m: (0 if m.module.startswith('pretix.') else 1, str(m.name).lower().replace('pretix ', ''))
|
||||
)
|
||||
|
||||
|
||||
def get_all_plugins_map(*, event=None, organizer=None, only_visible=False, allow_restricted: type[ALLOW_ALL] | List[str]=ALLOW_ALL) -> dict[str, type]:
|
||||
return {
|
||||
p.module: p for p in iter_all_plugins(event=event, organizer=organizer, only_visible=only_visible, allow_restricted=allow_restricted)
|
||||
}
|
||||
|
||||
|
||||
class PluginConfigMeta(type):
|
||||
def __getattribute__(cls, item):
|
||||
if item == "default" and cls is PluginConfig:
|
||||
|
||||
@@ -1394,16 +1394,27 @@ class SalesChannelForm(I18nModelForm):
|
||||
|
||||
|
||||
class OrganizerPluginEventsForm(forms.Form):
|
||||
active_on_organizer = forms.BooleanField(
|
||||
label=_("Active on organizer-level"),
|
||||
help_text=_("Enables or disables the organizer-wide features of this plugin."),
|
||||
required=False,
|
||||
)
|
||||
events = SafeEventMultipleChoiceField(
|
||||
queryset=Event.objects.none(),
|
||||
widget=forms.CheckboxSelectMultiple(attrs={
|
||||
'class': 'scrolling-multiple-choice scrolling-multiple-choice-large',
|
||||
'data-checkbox-dependency': '#id_active_on_organizer'
|
||||
}),
|
||||
label=_("Events with active plugin"),
|
||||
required=False,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, *args, hybrid, **kwargs):
|
||||
events = kwargs.pop('events')
|
||||
super().__init__(*args, **kwargs)
|
||||
if not hybrid:
|
||||
del self.fields['active_on_organizer']
|
||||
self.fields['events'].widget = forms.CheckboxSelectMultiple(attrs={
|
||||
'class': 'scrolling-multiple-choice scrolling-multiple-choice-large',
|
||||
})
|
||||
self.fields['events'].queryset = events
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<legend>{{ catlabel }}</legend>
|
||||
<div class="plugin-list">
|
||||
{% for plugin, is_active, settings_links, navigation_links, events_counter in plist %}
|
||||
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}" data-plugin-module="{{ plugin.module }}" data-plugin-name="{{ plugin.name }}">
|
||||
<div class="plugin-container {% if plugin.featured %}featured-plugin{% endif %}" id="plugin_{{ plugin.module }}" data-plugin-module="{{ plugin.module }}" data-plugin-name="{{ plugin.name }}" data-plugin-level="{{ plugin.level }}">
|
||||
{% if plugin.featured %}
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
@@ -89,8 +89,8 @@
|
||||
Active ({{ count }} events)
|
||||
{% endblocktrans %}
|
||||
</span>
|
||||
{% elif level == "event_organizer" %}
|
||||
<span class="label label-info" data-is-active>
|
||||
{% elif plugin.level == "event_organizer" %}
|
||||
<span class="label label-success" data-is-active>
|
||||
<span class="fa fa-check" aria-hidden="true"></span>
|
||||
{% blocktrans trimmed count count=0 %}
|
||||
Active ({{ count }} event)
|
||||
@@ -143,13 +143,14 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<button class="btn btn-default{% if plugin.featured %} btn-lg{% endif %}" name="plugin:{{ plugin.module }}"
|
||||
value="disable">{% trans "Disable" %}</button>
|
||||
{% if plugin.level == "event_organizer" %}
|
||||
<a class="btn btn-default {% if plugin.featured %} btn-lg{% endif %}"
|
||||
href="{% url "control:organizer.settings.plugin-events" organizer=request.organizer.slug plugin=plugin.module %}">
|
||||
{% trans "Manage events" %}
|
||||
{% trans "Manage plugin and events ..." %}
|
||||
</a>
|
||||
{% else %}
|
||||
<button class="btn btn-default{% if plugin.featured %} btn-lg{% endif %}" name="plugin:{{ plugin.module }}"
|
||||
value="disable">{% trans "Disable" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
|
||||
@@ -363,12 +363,6 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
def get_object(self, queryset=None) -> Event:
|
||||
return self.request.event
|
||||
|
||||
def available_plugins(self, event):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return (p for p in get_all_plugins(event=event) if not p.name.startswith('.')
|
||||
and getattr(p, 'visible', True))
|
||||
|
||||
def prepare_links(self, pluginmeta, key):
|
||||
links = getattr(pluginmeta, key, [])
|
||||
try:
|
||||
@@ -391,14 +385,13 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
return []
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
from pretix.base.plugins import CATEGORY_LABELS, CATEGORY_ORDER
|
||||
from pretix.base.plugins import iter_all_plugins, CATEGORY_LABELS, CATEGORY_ORDER
|
||||
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
plugins = list(self.available_plugins(self.object))
|
||||
|
||||
plugins_grouped = groupby(
|
||||
sorted(
|
||||
plugins,
|
||||
iter_all_plugins(event=self.object, only_visible=True),
|
||||
key=lambda p: (
|
||||
str(getattr(p, 'category', _('Other'))),
|
||||
(0 if getattr(p, 'featured', False) else 1),
|
||||
@@ -439,9 +432,7 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
plugins_available = {
|
||||
p.module: p for p in self.available_plugins(self.object)
|
||||
}
|
||||
plugins_available = self.object.get_available_plugins(filter_restricted=True)
|
||||
plugin_enabled = None
|
||||
|
||||
with transaction.atomic():
|
||||
@@ -451,10 +442,6 @@ class EventPlugins(EventSettingsViewMixin, EventPermissionRequiredMixin, Templat
|
||||
module = key.split(":")[1]
|
||||
if value == "enable" and module in plugins_available:
|
||||
pluginmeta = plugins_available[module]
|
||||
if getattr(pluginmeta, 'restricted', False):
|
||||
if module not in request.event.settings.allowed_restricted_plugins:
|
||||
continue
|
||||
|
||||
if getattr(pluginmeta, 'level', PLUGIN_LEVEL_EVENT) not in (PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID):
|
||||
continue
|
||||
|
||||
|
||||
@@ -596,13 +596,6 @@ class OrganizerCreate(CreateView):
|
||||
})
|
||||
|
||||
|
||||
def available_plugins(organizer):
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
|
||||
return (p for p in get_all_plugins(organizer=organizer) if not p.name.startswith('.')
|
||||
and getattr(p, 'visible', True))
|
||||
|
||||
|
||||
class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, TemplateView, SingleObjectMixin):
|
||||
model = Organizer
|
||||
context_object_name = 'organizer'
|
||||
@@ -634,10 +627,9 @@ class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
return []
|
||||
|
||||
def get_context_data(self, *args, **kwargs) -> dict:
|
||||
from pretix.base.plugins import CATEGORY_LABELS, CATEGORY_ORDER
|
||||
from pretix.base.plugins import iter_all_plugins, CATEGORY_LABELS, CATEGORY_ORDER
|
||||
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
plugins = list(available_plugins(self.object))
|
||||
|
||||
active_counter = Counter()
|
||||
events_total = 0
|
||||
@@ -647,7 +639,7 @@ class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
active_counter[p] += 1
|
||||
plugins_grouped = groupby(
|
||||
sorted(
|
||||
plugins,
|
||||
iter_all_plugins(organizer=self.object, only_visible=True),
|
||||
key=lambda p: (
|
||||
str(getattr(p, 'category', _('Other'))),
|
||||
(0 if getattr(p, 'featured', False) else 1),
|
||||
@@ -684,9 +676,7 @@ class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
||||
plugins_available = {
|
||||
p.module: p for p in available_plugins(self.object)
|
||||
}
|
||||
plugins_available = self.object.get_available_plugins(filter_restricted=True)
|
||||
choose_events_next = False
|
||||
with transaction.atomic():
|
||||
for key, value in request.POST.items():
|
||||
@@ -694,10 +684,6 @@ class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
module = key.split(":")[1]
|
||||
if value == "enable" and module in plugins_available:
|
||||
pluginmeta = plugins_available[module]
|
||||
if getattr(pluginmeta, 'restricted', False):
|
||||
if module not in request.organizer.settings.allowed_restricted_plugins:
|
||||
continue
|
||||
|
||||
level = getattr(pluginmeta, 'level', PLUGIN_LEVEL_EVENT)
|
||||
if level not in (PLUGIN_LEVEL_ORGANIZER, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID):
|
||||
continue
|
||||
@@ -729,27 +715,9 @@ class OrganizerPlugins(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
elif value == "disable" and module in plugins_available:
|
||||
pluginmeta = plugins_available[module]
|
||||
level = getattr(pluginmeta, 'level', PLUGIN_LEVEL_EVENT)
|
||||
if level not in (PLUGIN_LEVEL_ORGANIZER, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID):
|
||||
if level != PLUGIN_LEVEL_ORGANIZER:
|
||||
continue
|
||||
|
||||
if level == PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID:
|
||||
events_to_disable = set(self.request.organizer.events.filter(
|
||||
plugins__regex='(^|,)' + module + '(,|$)'
|
||||
).values_list("pk", flat=True))
|
||||
logentries_to_save = []
|
||||
events_to_save = []
|
||||
|
||||
for e in self.request.organizer.events.filter(pk__in=events_to_disable):
|
||||
logentries_to_save.append(
|
||||
e.log_action('pretix.event.plugins.disabled', user=self.request.user,
|
||||
data={'plugin': module}, save=False)
|
||||
)
|
||||
e.disable_plugin(module)
|
||||
events_to_save.append(e)
|
||||
|
||||
Event.objects.bulk_update(events_to_save, fields=["plugins"])
|
||||
LogEntry.objects.bulk_create(logentries_to_save)
|
||||
|
||||
self.object.log_action('pretix.organizer.plugins.disabled', user=self.request.user,
|
||||
data={'plugin': module})
|
||||
self.object.disable_plugin(module)
|
||||
@@ -781,7 +749,9 @@ class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
# Assumption: Who has access to modify organizer settings may see all events and disable/enable plugins
|
||||
# for them. Otherwise, inconsistent situations occur.
|
||||
kwargs["events"] = self.request.organizer.events.all()
|
||||
kwargs["hybrid"] = self.plugin_level == PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID
|
||||
kwargs["initial"] = {
|
||||
"active_on_organizer": self.plugin.module in self.request.organizer.get_plugins(),
|
||||
"events": self.request.organizer.events.filter(plugins__regex='(^|,)' + self.plugin.module + '(,|$)')
|
||||
}
|
||||
return kwargs
|
||||
@@ -793,20 +763,12 @@ class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
self.plugin = next(p for p in available_plugins(self.request.organizer) if p.module == kwargs["plugin"])
|
||||
except StopIteration:
|
||||
self.plugin = self.request.organizer.get_available_plugins(filter_restricted=True).get(kwargs["plugin"])
|
||||
self.plugin_level = getattr(self.plugin, "level", PLUGIN_LEVEL_EVENT)
|
||||
if not self.plugin:
|
||||
raise Http404(_("Unknown plugin."))
|
||||
level = getattr(self.plugin, "level", PLUGIN_LEVEL_EVENT)
|
||||
if level == PLUGIN_LEVEL_ORGANIZER:
|
||||
if self.plugin_level == PLUGIN_LEVEL_ORGANIZER:
|
||||
raise Http404(_("This plugin can only be enabled for the entire organizer account."))
|
||||
if level == PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID and self.plugin.module not in self.request.organizer.get_plugins():
|
||||
raise Http404(_("This plugin is currently not active on the organizer account."))
|
||||
|
||||
if getattr(self.plugin, 'restricted', False):
|
||||
if self.plugin.module not in request.organizer.settings.allowed_restricted_plugins:
|
||||
raise Http404(_("This plugin is currently not allowed for this organizer account."))
|
||||
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self) -> str:
|
||||
@@ -816,27 +778,43 @@ class OrganizerPluginEvents(OrganizerDetailViewMixin, OrganizerPermissionRequire
|
||||
|
||||
@transaction.atomic()
|
||||
def form_valid(self, form):
|
||||
organizer = self.request.organizer
|
||||
enabled_events_before = set(
|
||||
self.request.organizer.events.filter(plugins__regex='(^|,)' + self.plugin.module + '(,|$)').values_list("pk", flat=True)
|
||||
organizer.events.filter(plugins__regex='(^|,)' + self.plugin.module + '(,|$)').values_list("pk", flat=True)
|
||||
)
|
||||
enabled_events_now = {e.pk for e in form.cleaned_data["events"]}
|
||||
|
||||
if self.plugin_level == PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID:
|
||||
if not form.cleaned_data["active_on_organizer"]:
|
||||
enabled_events_now = set()
|
||||
if self.plugin.module in organizer.get_plugins():
|
||||
organizer.log_action('pretix.organizer.plugins.disabled', user=self.request.user,
|
||||
data={'plugin': self.plugin.module})
|
||||
organizer.disable_plugin(self.plugin.module)
|
||||
organizer.save()
|
||||
else:
|
||||
if self.plugin.module not in organizer.get_plugins():
|
||||
organizer.log_action('pretix.organizer.plugins.enabled', user=self.request.user,
|
||||
data={'plugin': self.plugin.module})
|
||||
organizer.enable_plugin(self.plugin.module)
|
||||
organizer.save()
|
||||
|
||||
events_to_enable = enabled_events_now - enabled_events_before
|
||||
events_to_disable = enabled_events_before - enabled_events_now
|
||||
events_to_save = []
|
||||
logentries_to_save = []
|
||||
|
||||
for e in self.request.organizer.events.filter(pk__in=events_to_enable):
|
||||
if not plugin_is_available(self.plugin, organizer=self.request.organizer, event=e):
|
||||
for e in organizer.events.filter(pk__in=events_to_enable):
|
||||
if not plugin_is_available(self.plugin, organizer=organizer, event=e):
|
||||
messages.warning(self.request, _("This plugin cannot be activated for event {}.").format(e.name))
|
||||
continue
|
||||
logentries_to_save.append(
|
||||
e.log_action('pretix.event.plugins.enabled', user=self.request.user, data={'plugin': self.plugin.module}, save=False)
|
||||
)
|
||||
e.enable_plugin(self.plugin.module, allow_restricted=self.request.organizer.settings.allowed_restricted_plugins)
|
||||
e.enable_plugin(self.plugin.module, allow_restricted=organizer.settings.allowed_restricted_plugins)
|
||||
events_to_save.append(e)
|
||||
|
||||
for e in self.request.organizer.events.filter(pk__in=events_to_disable):
|
||||
for e in organizer.events.filter(pk__in=events_to_disable):
|
||||
logentries_to_save.append(
|
||||
e.log_action('pretix.event.plugins.disabled', user=self.request.user, data={'plugin': self.plugin.module}, save=False)
|
||||
)
|
||||
|
||||
@@ -69,7 +69,7 @@ from pretix.base.models import (
|
||||
from pretix.base.payment import (
|
||||
BasePaymentProvider, PaymentException, WalletQueries,
|
||||
)
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.plugins import get_all_plugins_map
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.views.redirect import safelink
|
||||
from pretix.helpers import OF_SELF
|
||||
@@ -234,7 +234,7 @@ class StripeSettingsHolder(BasePaymentProvider):
|
||||
|
||||
@property
|
||||
def settings_form_fields(self):
|
||||
if 'pretix_resellers' in [p.module for p in get_all_plugins()]:
|
||||
if 'pretix_resellers' in get_all_plugins_map():
|
||||
moto_settings = [
|
||||
('reseller_moto',
|
||||
forms.BooleanField(
|
||||
|
||||
@@ -109,30 +109,7 @@ let form_handlers = function (el) {
|
||||
}
|
||||
$(this).datetimepicker(opts)
|
||||
})
|
||||
el.find("button[data-wait-seconds-enable], input[data-wait-seconds-enable]").each(function(i, input) {
|
||||
var s = parseInt(input.getAttribute("data-wait-seconds-enable")) || 0;
|
||||
var time = $("time", input) || $("time").appendTo(input);
|
||||
// for a11y do not disable input, but do not allow submit
|
||||
function disable_submit(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (s) {
|
||||
input.addEventListener("click", disable_submit);
|
||||
}
|
||||
function wait() {
|
||||
time.attr("datetime", s+"s");
|
||||
if (s > 0) {
|
||||
time.text("(" + s + "s)");
|
||||
window.setTimeout(wait, 1000);
|
||||
s--;
|
||||
} else {
|
||||
time.remove();
|
||||
input.disabled = false;
|
||||
input.removeEventListener("click", disable_submit);
|
||||
}
|
||||
}
|
||||
wait();
|
||||
});
|
||||
|
||||
el.find('.input-item-count-dec, .input-item-count-inc').on('click', function (e) {
|
||||
e.preventDefault()
|
||||
let step = parseFloat(this.getAttribute('data-step'))
|
||||
|
||||
Reference in New Issue
Block a user