Compare commits

..
15 changed files with 202 additions and 261 deletions
+3 -11
View File
@@ -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))
+3 -3
View File
@@ -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):
+2 -11
View File
@@ -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))
+17
View File
@@ -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
+17 -7
View File
@@ -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
+52
View File
@@ -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)
+5 -54
View File
@@ -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):
+5 -55
View File
@@ -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
View File
@@ -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:
+12 -1
View File
@@ -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 %}
+3 -16
View File
@@ -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
+31 -53
View File
@@ -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)
)
+2 -2
View File
@@ -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(
+1 -24
View File
@@ -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'))