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 = {}