mirror of
https://github.com/pretix/pretix.git
synced 2026-05-04 15:04:03 +00:00
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:
@@ -19,7 +19,9 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import operator
|
||||
from decimal import Decimal
|
||||
from functools import reduce
|
||||
|
||||
import django_filters
|
||||
from django.contrib.auth.hashers import make_password
|
||||
@@ -48,15 +50,18 @@ from pretix.api.serializers.organizer import (
|
||||
TeamInviteSerializer, TeamMemberSerializer, TeamSerializer,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
Customer, Device, GiftCard, GiftCardTransaction, Membership,
|
||||
MembershipType, Organizer, SalesChannel, SeatingPlan, Team, TeamAPIToken,
|
||||
TeamInvite, User,
|
||||
Customer, Device, Event, GiftCard, GiftCardTransaction, LogEntry,
|
||||
Membership, MembershipType, Organizer, SalesChannel, SeatingPlan, Team,
|
||||
TeamAPIToken, TeamInvite, User,
|
||||
)
|
||||
from pretix.base.plugins import (
|
||||
PLUGIN_LEVEL_EVENT, PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID,
|
||||
)
|
||||
from pretix.helpers import OF_SELF
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
|
||||
|
||||
class OrganizerViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
class OrganizerViewSet(mixins.UpdateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
serializer_class = OrganizerSerializer
|
||||
queryset = Organizer.objects.none()
|
||||
lookup_field = 'slug'
|
||||
@@ -65,6 +70,7 @@ class OrganizerViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
filter_backends = (TotalOrderingFilter,)
|
||||
ordering = ('slug',)
|
||||
ordering_fields = ('name', 'slug')
|
||||
write_permission = "can_change_organizer_settings"
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_authenticated:
|
||||
@@ -83,6 +89,67 @@ class OrganizerViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
else:
|
||||
return Organizer.objects.filter(pk=self.request.auth.team.organizer_id)
|
||||
|
||||
@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()
|
||||
updated_plugins_value = serializer.validated_data.get('plugins', None)
|
||||
|
||||
super().perform_update(serializer)
|
||||
|
||||
if serializer.data == original_data:
|
||||
# Performance optimization: If nothing was changed, we do not need to save or log anything.
|
||||
# This costs us a few cycles on save, but avoids thousands of lines in our log.
|
||||
return
|
||||
|
||||
if updated_plugins_value is not None and set(updated_plugins_value) != set(current_plugins_value):
|
||||
enabled = {m: 'enabled' for m in updated_plugins_value if m not in current_plugins_value}
|
||||
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)
|
||||
}
|
||||
qs = []
|
||||
for module in disabled:
|
||||
pluginmeta = plugins_available[module]
|
||||
level = getattr(pluginmeta, 'level', PLUGIN_LEVEL_EVENT)
|
||||
if level == PLUGIN_LEVEL_EVENT_ORGANIZER_HYBRID:
|
||||
qs.append(Q(plugins__regex='(^|,)' + module + '(,|$)'))
|
||||
|
||||
if qs:
|
||||
events_to_disable = set(self.request.organizer.events.filter(
|
||||
reduce(operator.or_, qs)
|
||||
).values_list("pk", flat=True))
|
||||
logentries_to_save = []
|
||||
events_to_save = []
|
||||
|
||||
for e in self.request.organizer.events.filter(pk__in=events_to_disable):
|
||||
for module in disabled:
|
||||
if module in e.get_plugins():
|
||||
logentries_to_save.append(
|
||||
e.log_action('pretix.event.plugins.disabled', user=self.request.user, auth=self.request.auth,
|
||||
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)
|
||||
|
||||
for module, operation in changed.items():
|
||||
serializer.instance.log_action(
|
||||
'pretix.organizer.plugins.' + operation,
|
||||
user=self.request.user,
|
||||
auth=self.request.auth,
|
||||
data={'plugin': module}
|
||||
)
|
||||
|
||||
|
||||
class SeatingPlanViewSet(viewsets.ModelViewSet):
|
||||
serializer_class = SeatingPlanSerializer
|
||||
|
||||
Reference in New Issue
Block a user