diff --git a/src/pretix/base/models/log.py b/src/pretix/base/models/log.py
index 2c399bb04..39a2b35cb 100644
--- a/src/pretix/base/models/log.py
+++ b/src/pretix/base/models/log.py
@@ -33,6 +33,7 @@
# License for the specific language governing permissions and limitations under the License.
import json
+import logging
from collections import defaultdict
from django.contrib.contenttypes.fields import GenericForeignKey
@@ -43,7 +44,7 @@ from django.utils.functional import cached_property
from django.utils.html import escape
from django.utils.translation import gettext_lazy as _, pgettext_lazy
-from pretix.base.signals import logentry_object_link
+from pretix.base.signals import logentry_object_link, EventPluginRegistry
class VisibleOnlyManager(models.Manager):
@@ -51,6 +52,27 @@ class VisibleOnlyManager(models.Manager):
return super().get_queryset().filter(visible=True)
+def make_link(a_map, wrapper, is_active=True, event=None):
+ if a_map:
+ if is_active:
+ a_map['val'] = '{val}'.format_map(a_map)
+ elif event:
+ a_map['val'] = '{val} '.format_map({
+ **a_map,
+ "errmes": _("The relevant plugin is currently not active. To activate it, click here to go to the plugin settings."),
+ "plugin_href": reverse('control:event.settings.plugins', kwargs={
+ 'organizer': event.organizer.slug,
+ 'event': event.slug,
+ }),
+ })
+ else:
+ a_map['val'] = '{val} '.format_map({
+ **a_map,
+ "errmes": _("The relevant plugin is currently not active."),
+ })
+ return wrapper.format_map(a_map)
+
+
class LogEntry(models.Model):
"""
Represents a change or action that has been performed on another object
@@ -93,7 +115,7 @@ class LogEntry(models.Model):
indexes = [models.Index(fields=["datetime", "id"])]
def display(self):
- log_entry_type = log_entry_types.find(action_type=self.action_type)
+ log_entry_type, meta = log_entry_types.find(action_type=self.action_type)
if log_entry_type:
return log_entry_type.display(self)
@@ -142,92 +164,19 @@ class LogEntry(models.Model):
co = self.content_object
except:
return ''
- a_map = None
- a_text = None
- if isinstance(co, Order):
- a_text = _('Order {val}')
- a_map = {
- 'href': reverse('control:event.order', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'code': co.code
- }),
- 'val': escape(co.code),
- }
- elif isinstance(co, Voucher):
- a_text = _('Voucher {val}…')
- a_map = {
- 'href': reverse('control:event.voucher', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'voucher': co.id
- }),
- 'val': escape(co.code[:6]),
- }
- elif isinstance(co, Item):
- a_text = _('Product {val}')
- a_map = {
- 'href': reverse('control:event.item', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'item': co.id
- }),
- 'val': escape(co.name),
- }
- elif isinstance(co, SubEvent):
- a_text = pgettext_lazy('subevent', 'Date {val}')
- a_map = {
- 'href': reverse('control:event.subevent', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'subevent': co.id
- }),
- 'val': escape(str(co))
- }
- elif isinstance(co, Quota):
- a_text = _('Quota {val}')
- a_map = {
- 'href': reverse('control:event.items.quotas.show', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'quota': co.id
- }),
- 'val': escape(co.name),
- }
- elif isinstance(co, Discount):
- a_text = _('Discount {val}')
- a_map = {
- 'href': reverse('control:event.items.discounts.edit', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'discount': co.id
- }),
- 'val': escape(co.internal_name),
- }
- elif isinstance(co, Question):
- a_text = _('Question {val}')
- a_map = {
- 'href': reverse('control:event.items.questions.show', kwargs={
- 'event': self.event.slug,
- 'organizer': self.event.organizer.slug,
- 'question': co.id
- }),
- 'val': escape(co.question),
- }
+ log_entry_type, meta = log_entry_types.find(action_type=self.action_type)
+ if log_entry_type:
+ return log_entry_type.get_object_link(self, is_active=meta['plugin'] == 'CORE' or (meta['plugin'] and meta['plugin'] in self.event.get_plugins()))
- if a_text and a_map:
- a_map['val'] = '{val}'.format_map(a_map)
- return a_text.format_map(a_map)
- else:
- log_entry_type = log_entry_types.find(action_type=self.action_type)
- if log_entry_type:
- return log_entry_type.get_object_link(self)
+ for receiver, response in logentry_object_link.send(self.event, logentry=self):
+ if response:
+ return response
- for receiver, response in logentry_object_link.send(self.event, logentry=self):
- if response:
- return response
- return ''
+ if isinstance(co, (Order, Voucher, Item, SubEvent, Quota, Discount, Question)):
+ logging.warning("LogEntryType missing or ill-defined: %s", self.action_type)
+
+ return ''
@cached_property
def parsed_data(self):
@@ -250,33 +199,23 @@ class LogEntry(models.Model):
notify_webhooks.apply_async(args=(to_wh,))
-class Registry:
- def __init__(self, keys):
- self.registered_items = list()
- self.keys = keys
- self.by_key = {key: {} for key in self.keys.keys()}
-
- def register(self, *objs):
- for obj in objs:
- self.registered_items.append(obj)
- for key, accessor in self.keys.items():
- self.by_key[key][accessor(obj)] = obj
-
- def register_instance(self, *args, **kwargs):
+class LogEntryTypeRegistry(EventPluginRegistry):
+ def new_from_dict(self, data):
def reg(clz):
- obj = clz(*args, **kwargs)
- self.register(obj)
+ for action_type, plain in data.items():
+ self.register(clz(action_type=action_type, plain=plain))
return reg
- def find(self, **kwargs):
- (key, value), = kwargs.items()
- return self.by_key.get(key).get(value)
-
-log_entry_types = Registry({'action_type': lambda o: getattr(o, 'action_type')})
+log_entry_types = LogEntryTypeRegistry({'action_type': lambda o: getattr(o, 'action_type')})
class LogEntryType:
+ def __init__(self, action_type=None, plain=None):
+ assert self.__module__ != LogEntryType.__module__ # must not instantiate base classes, only derived ones
+ if action_type: self.action_type = action_type
+ if plain: self.plain = plain
+
def display(self, logentry):
if hasattr(self, 'plain'):
plain = str(self.plain)
@@ -289,25 +228,15 @@ class LogEntryType:
def get_object_link_info(self, logentry) -> dict:
pass
- def get_object_link(self, logentry):
+ def get_object_link(self, logentry, is_active):
a_map = self.get_object_link_info(logentry)
- if a_map:
- a_map['val'] = '{val}'.format_map(a_map)
- return self.object_link_wrapper.format_map(a_map)
+ return make_link(a_map, self.object_link_wrapper, is_active, logentry.event)
object_link_wrapper = '{val}'
def shred_pii(self, logentry):
raise NotImplementedError
- @classmethod
- def derive_plains(cls, plains):
- for action_type, plain_display in plains.items():
- obj = cls()
- obj.action_type = action_type
- obj.plain = plain_display
- yield obj
-
class EventLogEntryType(LogEntryType):
def get_object_link_info(self, logentry) -> dict:
diff --git a/src/pretix/base/signals.py b/src/pretix/base/signals.py
index 5e38926ab..b22177f5d 100644
--- a/src/pretix/base/signals.py
+++ b/src/pretix/base/signals.py
@@ -33,7 +33,7 @@
# License for the specific language governing permissions and limitations under the License.
import warnings
-from typing import Any, Callable, List, Tuple
+from typing import Any, Callable, List, Tuple, Optional
import django.dispatch
from django.apps import apps
@@ -64,23 +64,10 @@ class EventPluginSignal(django.dispatch.Signal):
# Send to all events!
return True
- # If sentry packed this in a wrapper, unpack that
- if "sentry" in receiver.__module__:
- receiver = receiver.__wrapped__
-
- # Find the Django application this belongs to
- searchpath = receiver.__module__
-
- # Core modules are always active
- if any(searchpath.startswith(cm) for cm in settings.CORE_MODULES):
+ name, app = get_defining_app(receiver)
+ if app == 'CORE':
return True
- while True:
- app = app_cache.get(searchpath)
- if "." not in searchpath or app:
- break
- searchpath, _ = searchpath.rsplit(".", 1)
-
# Only fire receivers from active plugins and core modules
excluded = settings.PRETIX_PLUGINS_EXCLUDE
if sender and app and app.name in sender.get_plugins() and app.name not in excluded:
@@ -204,6 +191,60 @@ class DeprecatedSignal(django.dispatch.Signal):
super().connect(receiver, sender=None, weak=True, dispatch_uid=None)
+class Registry:
+ def __init__(self, keys):
+ self.registered_items = list()
+ self.keys = keys
+ self.by_key = {key: {} for key in self.keys.keys()}
+
+ def register(self, *objs):
+ for obj in objs:
+ meta = {k: accessor(obj) for k, accessor in self.keys.items()}
+ tup = (obj, meta)
+ for key, accessor in self.keys.items():
+ self.by_key[key][accessor(obj)] = tup
+ self.registered_items.append(tup)
+
+ def new(self, *args, **kwargs):
+ def reg(clz):
+ obj = clz(*args, **kwargs)
+ self.register(obj)
+ return clz
+ return reg
+
+ def find(self, **kwargs):
+ (key, value), = kwargs.items()
+ return self.by_key.get(key).get(value, (None, None))
+
+
+def get_defining_app(o) -> Tuple[Optional[str], Optional["django.apps.AppConfig"]]:
+ # If sentry packed this in a wrapper, unpack that
+ if "sentry" in o.__module__:
+ o = o.__wrapped__
+
+ # Find the Django application this belongs to
+ searchpath = o.__module__
+
+ # Core modules are always active
+ if any(searchpath.startswith(cm) for cm in settings.CORE_MODULES):
+ return 'CORE', 'CORE'
+
+ if not app_cache:
+ _populate_app_cache()
+
+ while True:
+ app = app_cache.get(searchpath)
+ if "." not in searchpath or app:
+ break
+ searchpath, _ = searchpath.rsplit(".", 1)
+ return app and app.name, app
+
+
+class EventPluginRegistry(Registry):
+ def __init__(self, keys):
+ super().__init__({"plugin": lambda o: get_defining_app(o)[0], **keys})
+
+
event_live_issues = EventPluginSignal()
"""
This signal is sent out to determine whether an event can be taken live. If you want to
diff --git a/src/pretix/control/logdisplay.py b/src/pretix/control/logdisplay.py
index 1813f9ebf..307b6b2c7 100644
--- a/src/pretix/control/logdisplay.py
+++ b/src/pretix/control/logdisplay.py
@@ -382,7 +382,7 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
return _('Blocked because of an API integration')
-log_entry_types.register(*OrderLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.event.order.modified': _('The order details have been changed.'),
'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
'pretix.event.order.secret.changed': _('The order\'s secret has been changed.'),
@@ -451,10 +451,12 @@ log_entry_types.register(*OrderLogEntryType.derive_plains({
'approval.'),
'pretix.event.order.email.resend': _('An email with a link to the order detail page has been resent to the user.'),
'pretix.event.order.email.payment_failed': _('An email has been sent to notify the user that the payment failed.'),
-}))
+})
+class CoreOrderLogEntryType(OrderLogEntryType):
+ pass
-log_entry_types.register(*VoucherLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.voucher.added': _('The voucher has been created.'),
'pretix.voucher.sent': _('The voucher has been sent to {recipient}.'),
'pretix.voucher.added.waitinglist': _('The voucher has been created and sent to a person on the waiting list.'),
@@ -462,10 +464,12 @@ log_entry_types.register(*VoucherLogEntryType.derive_plains({
'The voucher has been set to expire because the recipient removed themselves from the waiting list.'),
'pretix.voucher.changed': _('The voucher has been changed.'),
'pretix.voucher.deleted': _('The voucher has been deleted.'),
-}))
+})
+class CoreVoucherLogEntryType(VoucherLogEntryType):
+ pass
-@log_entry_types.register_instance()
+@log_entry_types.new()
class VoucherRedeemedLogEntryType(VoucherLogEntryType):
action_type = 'pretix.voucher.redeemed'
plain = _('The voucher has been redeemed in order {order_code}.')
@@ -483,19 +487,23 @@ class VoucherRedeemedLogEntryType(VoucherLogEntryType):
))
-log_entry_types.register(*ItemCategoryLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.event.category.added': _('The category has been added.'),
'pretix.event.category.deleted': _('The category has been deleted.'),
'pretix.event.category.changed': _('The category has been changed.'),
'pretix.event.category.reordered': _('The category has been reordered.'),
-}))
+})
+class CoreItemCategoryLogEntryType(ItemCategoryLogEntryType):
+ pass
-log_entry_types.register(*TaxRuleLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.event.taxrule.added': _('The tax rule has been added.'),
'pretix.event.taxrule.deleted': _('The tax rule has been deleted.'),
'pretix.event.taxrule.changed': _('The tax rule has been changed.'),
-}))
+})
+class CoreTaxRuleLogEntryType(TaxRuleLogEntryType):
+ pass
class TeamMembershipLogEntryType(LogEntryType):
@@ -503,15 +511,17 @@ class TeamMembershipLogEntryType(LogEntryType):
return self.plain.format(user=logentry.parsed_data.get('email'))
-log_entry_types.register(*TeamMembershipLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.team.member.added': _('{user} has been added to the team.'),
'pretix.team.member.removed': _('{user} has been removed from the team.'),
'pretix.team.invite.created': _('{user} has been invited to the team.'),
'pretix.team.invite.resent': _('Invite for {user} has been resent.'),
-}))
+})
+class CoreTeamMembershipLogEntryType(TeamMembershipLogEntryType):
+ pass
-@log_entry_types.register_instance()
+@log_entry_types.new()
class TeamMemberJoinedLogEntryType(LogEntryType):
action_type = 'pretix.team.member.joined'
@@ -521,7 +531,7 @@ class TeamMemberJoinedLogEntryType(LogEntryType):
)
-@log_entry_types.register_instance()
+@log_entry_types.new()
class UserSettingsChangedLogEntryType(LogEntryType):
action_type = 'pretix.user.settings.changed'
@@ -544,13 +554,15 @@ class UserImpersonatedLogEntryType(LogEntryType):
return self.plain.format(logentry.parsed_data['other_email'])
-log_entry_types.register(*UserImpersonatedLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.control.auth.user.impersonated': _('You impersonated {}.'),
'pretix.control.auth.user.impersonate_stopped': _('You stopped impersonating {}.'),
-}))
+})
+class CoreUserImpersonatedLogEntryType(UserImpersonatedLogEntryType):
+ pass
-log_entry_types.register(*LogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.object.cloned': _('This object has been created by cloning.'),
'pretix.organizer.changed': _('The organizer has been changed.'),
'pretix.organizer.settings': _('The organizer settings have been changed.'),
@@ -658,9 +670,11 @@ log_entry_types.register(*LogEntryType.derive_plains({
'pretix.giftcards.transaction.manual': _('A manual transaction has been performed.'),
'pretix.team.token.created': _('The token "{name}" has been created.'),
'pretix.team.token.deleted': _('The token "{name}" has been revoked.'),
-}))
+})
+class CoreLogEntryType(LogEntryType):
+ pass
-log_entry_types.register(*EventLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.event.item_meta_property.added': _('A meta property has been added to this event.'),
'pretix.event.item_meta_property.deleted': _('A meta property has been removed from this event.'),
'pretix.event.item_meta_property.changed': _('A meta property has been changed on this event.'),
@@ -686,9 +700,11 @@ log_entry_types.register(*EventLogEntryType.derive_plains({
'pretix.event.permissions.invited': _('A user has been invited to the event team.'),
'pretix.event.permissions.changed': _('A user\'s permissions have been changed.'),
'pretix.event.permissions.deleted': _('A user has been removed from the event team.'),
-}))
+})
+class CoreEventLogEntryType(EventLogEntryType):
+ pass
-log_entry_types.register(*ItemLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.event.item.added': _('The product has been created.'),
'pretix.event.item.changed': _('The product has been changed.'),
'pretix.event.item.reordered': _('The product has been reordered.'),
@@ -699,9 +715,16 @@ log_entry_types.register(*ItemLogEntryType.derive_plains({
'pretix.event.item.bundles.added': _('A bundled item has been added to this product.'),
'pretix.event.item.bundles.removed': _('A bundled item has been removed from this product.'),
'pretix.event.item.bundles.changed': _('A bundled item has been changed on this product.'),
-}))
+})
+class CoreItemLogEntryType(ItemLogEntryType):
+ pass
+@log_entry_types.new_from_dict({
+ 'pretix.event.item.variation.added': _('The variation "{value}" has been created.'),
+ 'pretix.event.item.variation.deleted': _('The variation "{value}" has been deleted.'),
+ 'pretix.event.item.variation.changed': _('The variation "{value}" has been changed.'),
+})
class VariationLogEntryType(ItemLogEntryType):
def display(self, logentry):
if 'value' not in logentry.parsed_data:
@@ -716,13 +739,7 @@ class VariationLogEntryType(ItemLogEntryType):
return super().display(logentry_display)
-log_entry_types.register(*VariationLogEntryType.derive_plains({
- 'pretix.event.item.variation.added': _('The variation "{value}" has been created.'),
- 'pretix.event.item.variation.deleted': _('The variation "{value}" has been deleted.'),
- 'pretix.event.item.variation.changed': _('The variation "{value}" has been changed.'),
-}))
-
-log_entry_types.register(*OrderLogEntryType.derive_plains({
+@log_entry_types.new_from_dict({
'pretix.event.order.payment.confirmed': _('Payment {local_id} has been confirmed.'),
'pretix.event.order.payment.canceled': _('Payment {local_id} has been canceled.'),
'pretix.event.order.payment.canceled.failed': _('Canceling payment {local_id} has failed.'),
@@ -736,31 +753,42 @@ log_entry_types.register(*OrderLogEntryType.derive_plains({
'pretix.event.order.refund.done': _('Refund {local_id} has been completed.'),
'pretix.event.order.refund.canceled': _('Refund {local_id} has been canceled.'),
'pretix.event.order.refund.failed': _('Refund {local_id} has failed.'),
-}))
+})
+class CoreOrderLogEntryType(OrderLogEntryType):
+ pass
-log_entry_types.register(*QuotaLogEntryType.derive_plains({
+
+@log_entry_types.new_from_dict({
'pretix.event.quota.added': _('The quota has been added.'),
'pretix.event.quota.deleted': _('The quota has been deleted.'),
'pretix.event.quota.changed': _('The quota has been changed.'),
'pretix.event.quota.closed': _('The quota has closed.'),
'pretix.event.quota.opened': _('The quota has been re-opened.'),
-}))
+})
+class CoreQuotaLogEntryType(QuotaLogEntryType):
+ pass
-log_entry_types.register(*QuestionLogEntryType.derive_plains({
+
+@log_entry_types.new_from_dict({
'pretix.event.question.added': _('The question has been added.'),
'pretix.event.question.deleted': _('The question has been deleted.'),
'pretix.event.question.changed': _('The question has been changed.'),
'pretix.event.question.reordered': _('The question has been reordered.'),
-}))
+})
+class CoreQuestionLogEntryType(QuestionLogEntryType):
+ pass
-log_entry_types.register(*DiscountLogEntryType.derive_plains({
+
+@log_entry_types.new_from_dict({
'pretix.event.discount.added': _('The discount has been added.'),
'pretix.event.discount.deleted': _('The discount has been deleted.'),
'pretix.event.discount.changed': _('The discount has been changed.'),
-}))
+})
+class CoreDiscountLogEntryType(DiscountLogEntryType):
+ pass
-@log_entry_types.register_instance()
+@log_entry_types.new()
class LegacyCheckinLogEntryType(OrderLogEntryType):
action_type = 'pretix.control.views.checkin'
diff --git a/src/pretix/plugins/badges/signals.py b/src/pretix/plugins/badges/signals.py
index c867ae0dc..58919a08d 100644
--- a/src/pretix/plugins/badges/signals.py
+++ b/src/pretix/plugins/badges/signals.py
@@ -172,15 +172,13 @@ def control_order_info(sender: Event, request, order: Order, **kwargs):
return template.render(ctx, request=request)
+@log_entry_types.new_from_dict({
+ 'pretix.plugins.badges.layout.added': _('Badge layout created.'),
+ 'pretix.plugins.badges.layout.deleted': _('Badge layout deleted.'),
+ 'pretix.plugins.badges.layout.changed': _('Badge layout changed.'),
+})
class BadgeLogEntryType(EventLogEntryType):
object_type = BadgeLayout
object_link_wrapper = _('Badge layout {val}')
object_link_viewname = 'plugins:badges:edit'
object_link_argname = 'layout'
-
-
-log_entry_types.register(*BadgeLogEntryType.derive_plains({
- 'pretix.plugins.badges.layout.added': _('Badge layout created.'),
- 'pretix.plugins.badges.layout.deleted': _('Badge layout deleted.'),
- 'pretix.plugins.badges.layout.changed': _('Badge layout changed.'),
-}))
diff --git a/src/pretix/plugins/banktransfer/signals.py b/src/pretix/plugins/banktransfer/signals.py
index 4a4924cd6..040889eca 100644
--- a/src/pretix/plugins/banktransfer/signals.py
+++ b/src/pretix/plugins/banktransfer/signals.py
@@ -118,7 +118,7 @@ def html_head_presale(sender, request=None, **kwargs):
return ""
-@log_entry_types.register_instance()
+@log_entry_types.new()
class BanktransferOrderEmailInvoiceLogEntryType(OrderLogEntryType):
action_type = 'pretix.plugins.banktransfer.order.email.invoice'
plain = _('The invoice was sent to the designated email address.')
diff --git a/src/pretix/plugins/paypal2/signals.py b/src/pretix/plugins/paypal2/signals.py
index bbb3e5b5d..ac9765e67 100644
--- a/src/pretix/plugins/paypal2/signals.py
+++ b/src/pretix/plugins/paypal2/signals.py
@@ -32,7 +32,7 @@ from django.utils.translation import gettext_lazy as _, pgettext_lazy
from pretix.base.forms import SecretKeySettingsField
from pretix.base.middleware import _merge_csp, _parse_csp, _render_csp
-from pretix.base.models.log import EventLogEntryType
+from pretix.base.models.log import EventLogEntryType, log_entry_types
from pretix.base.settings import settings_hierarkey
from pretix.base.signals import (
register_global_settings, register_payment_providers,
@@ -47,6 +47,7 @@ def register_payment_provider(sender, **kwargs):
return [PaypalSettingsHolder, PaypalWallet, PaypalAPM]
+@log_entry_types.new()
class PaypalEventLogEntryType(EventLogEntryType):
action_type = 'pretix.plugins.paypal.event'
diff --git a/src/pretix/plugins/sendmail/signals.py b/src/pretix/plugins/sendmail/signals.py
index fb71c3eea..da0b4849f 100644
--- a/src/pretix/plugins/sendmail/signals.py
+++ b/src/pretix/plugins/sendmail/signals.py
@@ -119,26 +119,23 @@ def control_nav_import(sender, request=None, **kwargs):
]
+@log_entry_types.new('pretix.plugins.sendmail.sent', _('Mass email was sent to customers or attendees.'))
+@log_entry_types.new('pretix.plugins.sendmail.sent.waitinglist', _('Mass email was sent to waiting list entries.'))
class SendmailPluginLogEntryType(EventLogEntryType):
pass
-log_entry_types.register(*SendmailPluginLogEntryType.derive_plains({
- 'pretix.plugins.sendmail.sent': _('Mass email was sent to customers or attendees.'),
- 'pretix.plugins.sendmail.sent.waitinglist': _('Mass email was sent to waiting list entries.'),
-}))
-
-
+@log_entry_types.new('pretix.plugins.sendmail.order.email.sent', _('The order received a mass email.'))
+@log_entry_types.new('pretix.plugins.sendmail.order.email.sent.attendee', _('A ticket holder of this order received a mass email.'))
class SendmailPluginOrderLogEntryType(OrderLogEntryType):
pass
-log_entry_types.register(*SendmailPluginOrderLogEntryType.derive_plains({
- 'pretix.plugins.sendmail.order.email.sent': _('The order received a mass email.'),
- 'pretix.plugins.sendmail.order.email.sent.attendee': _('A ticket holder of this order received a mass email.'),
-}))
-
-
+@log_entry_types.new('pretix.plugins.sendmail.rule.added', _('An email rule was created'))
+@log_entry_types.new('pretix.plugins.sendmail.rule.changed', _('An email rule was updated'))
+@log_entry_types.new('pretix.plugins.sendmail.rule.order.email.sent', _('A scheduled email was sent to the order'))
+@log_entry_types.new('pretix.plugins.sendmail.rule.order.position.email.sent', _('A scheduled email was sent to a ticket holder'))
+@log_entry_types.new('pretix.plugins.sendmail.rule.deleted', _('An email rule was deleted'))
class SendmailPluginRuleLogEntryType(EventLogEntryType):
object_type = Rule
object_link_wrapper = _('Mail rule {val}')
@@ -146,15 +143,6 @@ class SendmailPluginRuleLogEntryType(EventLogEntryType):
object_link_argname = 'rule'
-log_entry_types.register(*SendmailPluginRuleLogEntryType.derive_plains({
- 'pretix.plugins.sendmail.rule.added': _('An email rule was created'),
- 'pretix.plugins.sendmail.rule.changed': _('An email rule was updated'),
- 'pretix.plugins.sendmail.rule.order.email.sent': _('A scheduled email was sent to the order'),
- 'pretix.plugins.sendmail.rule.order.position.email.sent': _('A scheduled email was sent to a ticket holder'),
- 'pretix.plugins.sendmail.rule.deleted': _('An email rule was deleted'),
-}))
-
-
@receiver(periodic_task)
def sendmail_run_rules(sender, **kwargs):
with scopes_disabled():
diff --git a/src/pretix/plugins/ticketoutputpdf/signals.py b/src/pretix/plugins/ticketoutputpdf/signals.py
index a6c54ea1e..64f44d713 100644
--- a/src/pretix/plugins/ticketoutputpdf/signals.py
+++ b/src/pretix/plugins/ticketoutputpdf/signals.py
@@ -133,6 +133,11 @@ def pdf_event_copy_data_receiver(sender, other, item_map, question_map, **kwargs
return layout_map
+@log_entry_types.new_from_dict({
+ 'pretix.plugins.ticketoutputpdf.layout.added': _('Ticket layout created.'),
+ 'pretix.plugins.ticketoutputpdf.layout.deleted': _('Ticket layout deleted.'),
+ 'pretix.plugins.ticketoutputpdf.layout.changed': _('Ticket layout changed.'),
+})
class PdfTicketLayoutLogEntryType(EventLogEntryType):
object_type = TicketLayout
object_link_wrapper = _('Ticket layout {val}')
@@ -140,13 +145,6 @@ class PdfTicketLayoutLogEntryType(EventLogEntryType):
object_link_argname = 'layout'
-log_entry_types.register(*PdfTicketLayoutLogEntryType.derive_plains({
- 'pretix.plugins.ticketoutputpdf.layout.added': _('Ticket layout created.'),
- 'pretix.plugins.ticketoutputpdf.layout.deleted': _('Ticket layout deleted.'),
- 'pretix.plugins.ticketoutputpdf.layout.changed': _('Ticket layout changed.'),
-}))
-
-
def _ticket_layouts_for_item(request, item):
if not hasattr(request, '_ticket_layouts_for_item'):
request._ticket_layouts_for_item = {}