forked from CGM_Public/pretix_original
Add webhooks for changes to events and subevents
This commit is contained in:
@@ -31,8 +31,10 @@ action_types list of strings A list of actio
|
|||||||
The following values for ``action_types`` are valid with pretix core:
|
The following values for ``action_types`` are valid with pretix core:
|
||||||
|
|
||||||
* ``pretix.event.order.placed``
|
* ``pretix.event.order.placed``
|
||||||
|
* ``pretix.event.order.placed.require_approval``
|
||||||
* ``pretix.event.order.paid``
|
* ``pretix.event.order.paid``
|
||||||
* ``pretix.event.order.canceled``
|
* ``pretix.event.order.canceled``
|
||||||
|
* ``pretix.event.order.reactivated``
|
||||||
* ``pretix.event.order.expired``
|
* ``pretix.event.order.expired``
|
||||||
* ``pretix.event.order.modified``
|
* ``pretix.event.order.modified``
|
||||||
* ``pretix.event.order.contact.changed``
|
* ``pretix.event.order.contact.changed``
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import requests
|
|||||||
from celery.exceptions import MaxRetriesExceededError
|
from celery.exceptions import MaxRetriesExceededError
|
||||||
from django.db.models import Exists, OuterRef, Q
|
from django.db.models import Exists, OuterRef, Q
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||||
from django_scopes import scope, scopes_disabled
|
from django_scopes import scope, scopes_disabled
|
||||||
from requests import RequestException
|
from requests import RequestException
|
||||||
|
|
||||||
@@ -97,6 +97,67 @@ class ParametrizedOrderWebhookEvent(WebhookEvent):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ParametrizedEventWebhookEvent(WebhookEvent):
|
||||||
|
def __init__(self, action_type, verbose_name):
|
||||||
|
self._action_type = action_type
|
||||||
|
self._verbose_name = verbose_name
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_type(self):
|
||||||
|
return self._action_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def verbose_name(self):
|
||||||
|
return self._verbose_name
|
||||||
|
|
||||||
|
def build_payload(self, logentry: LogEntry):
|
||||||
|
if logentry.action_type == 'pretix.event.deleted':
|
||||||
|
organizer = logentry.content_object
|
||||||
|
return {
|
||||||
|
'notification_id': logentry.pk,
|
||||||
|
'organizer': organizer.slug,
|
||||||
|
'event': logentry.parsed_data.get('slug'),
|
||||||
|
'action': logentry.action_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
event = logentry.content_object
|
||||||
|
if not event:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
'notification_id': logentry.pk,
|
||||||
|
'organizer': event.organizer.slug,
|
||||||
|
'event': event.slug,
|
||||||
|
'action': logentry.action_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ParametrizedSubEventWebhookEvent(WebhookEvent):
|
||||||
|
def __init__(self, action_type, verbose_name):
|
||||||
|
self._action_type = action_type
|
||||||
|
self._verbose_name = verbose_name
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def action_type(self):
|
||||||
|
return self._action_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def verbose_name(self):
|
||||||
|
return self._verbose_name
|
||||||
|
|
||||||
|
def build_payload(self, logentry: LogEntry):
|
||||||
|
# do not use content_object, this is also called in deletion
|
||||||
|
return {
|
||||||
|
'notification_id': logentry.pk,
|
||||||
|
'organizer': logentry.event.organizer.slug,
|
||||||
|
'event': logentry.event.slug,
|
||||||
|
'subevent': logentry.object_id,
|
||||||
|
'action': logentry.action_type,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class ParametrizedOrderPositionWebhookEvent(ParametrizedOrderWebhookEvent):
|
class ParametrizedOrderPositionWebhookEvent(ParametrizedOrderWebhookEvent):
|
||||||
|
|
||||||
def build_payload(self, logentry: LogEntry):
|
def build_payload(self, logentry: LogEntry):
|
||||||
@@ -169,44 +230,69 @@ def register_default_webhook_events(sender, **kwargs):
|
|||||||
'pretix.event.checkin.reverted',
|
'pretix.event.checkin.reverted',
|
||||||
_('Ticket check-in reverted'),
|
_('Ticket check-in reverted'),
|
||||||
),
|
),
|
||||||
|
ParametrizedEventWebhookEvent(
|
||||||
|
'pretix.event.added',
|
||||||
|
_('Event created'),
|
||||||
|
),
|
||||||
|
ParametrizedEventWebhookEvent(
|
||||||
|
'pretix.event.changed',
|
||||||
|
_('Event details changed'),
|
||||||
|
),
|
||||||
|
ParametrizedEventWebhookEvent(
|
||||||
|
'pretix.event.deleted',
|
||||||
|
_('Event details changed'),
|
||||||
|
),
|
||||||
|
ParametrizedSubEventWebhookEvent(
|
||||||
|
'pretix.subevent.added',
|
||||||
|
pgettext_lazy('subevent', 'Event series date added'),
|
||||||
|
),
|
||||||
|
ParametrizedSubEventWebhookEvent(
|
||||||
|
'pretix.subevent.changed',
|
||||||
|
pgettext_lazy('subevent', 'Event series date changed'),
|
||||||
|
),
|
||||||
|
ParametrizedSubEventWebhookEvent(
|
||||||
|
'pretix.subevent.deleted',
|
||||||
|
pgettext_lazy('subevent', 'Event series date deleted'),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.task(base=TransactionAwareTask, acks_late=True)
|
@app.task(base=TransactionAwareTask, acks_late=True)
|
||||||
def notify_webhooks(logentry_id: int):
|
def notify_webhooks(logentry_ids: list):
|
||||||
logentry = LogEntry.all.select_related('event', 'event__organizer').get(id=logentry_id)
|
if not isinstance(logentry_ids, list):
|
||||||
|
logentry_ids = [logentry_ids]
|
||||||
|
qs = LogEntry.all.select_related('event', 'event__organizer').filter(id__in=logentry_ids)
|
||||||
|
_org, _at, webhooks = None, None, None
|
||||||
|
for logentry in qs:
|
||||||
|
if not logentry.organizer:
|
||||||
|
break # We need to know the organizer
|
||||||
|
|
||||||
if not logentry.organizer:
|
notification_type = logentry.webhook_type
|
||||||
return # We need to know the organizer
|
|
||||||
|
|
||||||
types = get_all_webhook_events()
|
if not notification_type:
|
||||||
notification_type = None
|
break # Ignore, no webhooks for this event type
|
||||||
typepath = logentry.action_type
|
|
||||||
while not notification_type and '.' in typepath:
|
|
||||||
notification_type = types.get(typepath + ('.*' if typepath != logentry.action_type else ''))
|
|
||||||
typepath = typepath.rsplit('.', 1)[0]
|
|
||||||
|
|
||||||
if not notification_type:
|
if _org != logentry.organizer or _at != logentry.action_type or webhooks is None:
|
||||||
return # Ignore, no webhooks for this event type
|
_org = logentry.organizer
|
||||||
|
_at = logentry.action_type
|
||||||
|
|
||||||
# All webhooks that registered for this notification
|
# All webhooks that registered for this notification
|
||||||
event_listener = WebHookEventListener.objects.filter(
|
event_listener = WebHookEventListener.objects.filter(
|
||||||
webhook=OuterRef('pk'),
|
webhook=OuterRef('pk'),
|
||||||
action_type=notification_type.action_type
|
action_type=notification_type.action_type
|
||||||
)
|
)
|
||||||
|
webhooks = WebHook.objects.annotate(has_el=Exists(event_listener)).filter(
|
||||||
|
organizer=logentry.organizer,
|
||||||
|
has_el=True,
|
||||||
|
enabled=True
|
||||||
|
)
|
||||||
|
if logentry.event_id:
|
||||||
|
webhooks = webhooks.filter(
|
||||||
|
Q(all_events=True) | Q(limit_events__pk=logentry.event_id)
|
||||||
|
)
|
||||||
|
|
||||||
webhooks = WebHook.objects.annotate(has_el=Exists(event_listener)).filter(
|
for wh in webhooks:
|
||||||
organizer=logentry.organizer,
|
send_webhook.apply_async(args=(logentry.id, notification_type.action_type, wh.pk))
|
||||||
has_el=True,
|
|
||||||
enabled=True
|
|
||||||
)
|
|
||||||
if logentry.event_id:
|
|
||||||
webhooks = webhooks.filter(
|
|
||||||
Q(all_events=True) | Q(limit_events__pk=logentry.event_id)
|
|
||||||
)
|
|
||||||
|
|
||||||
for wh in webhooks:
|
|
||||||
send_webhook.apply_async(args=(logentry_id, notification_type.action_type, wh.pk))
|
|
||||||
|
|
||||||
|
|
||||||
@app.task(base=ProfiledTask, bind=True, max_retries=9, acks_late=True)
|
@app.task(base=ProfiledTask, bind=True, max_retries=9, acks_late=True)
|
||||||
|
|||||||
@@ -397,7 +397,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
|||||||
'pretix.event.testmode.activated': _('The shop has been taken into test mode.'),
|
'pretix.event.testmode.activated': _('The shop has been taken into test mode.'),
|
||||||
'pretix.event.testmode.deactivated': _('The test mode has been disabled.'),
|
'pretix.event.testmode.deactivated': _('The test mode has been disabled.'),
|
||||||
'pretix.event.added': _('The event has been created.'),
|
'pretix.event.added': _('The event has been created.'),
|
||||||
'pretix.event.changed': _('The event settings have been changed.'),
|
'pretix.event.changed': _('The event details have been changed.'),
|
||||||
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
|
'pretix.event.question.option.added': _('An answer option has been added to the question.'),
|
||||||
'pretix.event.question.option.deleted': _('An answer option has been removed from the question.'),
|
'pretix.event.question.option.deleted': _('An answer option has been removed from the question.'),
|
||||||
'pretix.event.question.option.changed': _('An answer option has been changed.'),
|
'pretix.event.question.option.changed': _('An answer option has been changed.'),
|
||||||
|
|||||||
@@ -937,6 +937,7 @@ class EventDelete(RecentAuthenticationRequiredMixin, EventPermissionRequiredMixi
|
|||||||
data={
|
data={
|
||||||
'event_id': self.request.event.pk,
|
'event_id': self.request.event.pk,
|
||||||
'name': str(self.request.event.name),
|
'name': str(self.request.event.name),
|
||||||
|
'slug': self.request.event.slug,
|
||||||
'logentries': list(self.request.event.logentry_set.values_list('pk', flat=True))
|
'logentries': list(self.request.event.logentry_set.values_list('pk', flat=True))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -236,6 +236,10 @@ class EventWizard(SafeSessionWizardView):
|
|||||||
event.has_subevents = foundation_data['has_subevents']
|
event.has_subevents = foundation_data['has_subevents']
|
||||||
event.testmode = True
|
event.testmode = True
|
||||||
form_dict['basics'].save()
|
form_dict['basics'].save()
|
||||||
|
event.log_action(
|
||||||
|
'pretix.event.added',
|
||||||
|
user=self.request.user,
|
||||||
|
)
|
||||||
|
|
||||||
if not EventWizardBasicsForm.has_control_rights(self.request.user, event.organizer):
|
if not EventWizardBasicsForm.has_control_rights(self.request.user, event.organizer):
|
||||||
if basics_data["team"] is not None:
|
if basics_data["team"] is not None:
|
||||||
|
|||||||
Reference in New Issue
Block a user