diff --git a/src/pretix/api/serializers/organizer.py b/src/pretix/api/serializers/organizer.py index 754dc2d65..9df859701 100644 --- a/src/pretix/api/serializers/organizer.py +++ b/src/pretix/api/serializers/organizer.py @@ -27,7 +27,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.db import transaction from django.db.models import Q from django.utils.crypto import get_random_string -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ from rest_framework import serializers from rest_framework.exceptions import ValidationError @@ -492,7 +492,9 @@ class TeamInviteSerializer(serializers.ModelSerializer): def _send_invite(self, instance): mail( instance.email, - _('Account invitation'), + gettext('You\'ve been invited to join %(organizer)s') % { + 'organizer': self.context['organizer'].name, + }, 'pretixcontrol/email/invitation.txt', { 'instance': settings.PRETIX_INSTANCE_NAME, diff --git a/src/pretix/base/models/auth.py b/src/pretix/base/models/auth.py index f8295877a..540ffbc66 100644 --- a/src/pretix/base/models/auth.py +++ b/src/pretix/base/models/auth.py @@ -373,7 +373,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): mail( email or self.email, - _('Account information changed'), + _('Changes to your account'), 'pretixcontrol/email/security_notice.txt', { 'user': self, @@ -400,12 +400,13 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): with language(self.locale): if reason == 'email_change': - msg = str(_('to confirm changing your email address from {old_email}\nto {new_email}, use the following code:').format( + msg = str(_('To change your email address from {old_email} to {new_email}, use the following code:').format( old_email=self.email, new_email=email, )) elif reason == 'email_verify': - msg = str(_('to confirm that your email address {email} belongs to your pretix account, use the following code:').format( + msg = str(_('To verify your email address {email} on {instance}, use the following code:').format( email=self.email, + instance=settings.PRETIX_INSTANCE_NAME, )) else: raise Exception('Invalid confirmation code reason') @@ -418,7 +419,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): } mail( email or self.email, - _('pretix confirmation code'), + _('Your confirmation code'), 'pretixcontrol/email/confirmation_code.txt', { 'user': self, @@ -462,7 +463,9 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin): from pretix.base.services.mail import mail mail( - self.email, _('Password recovery'), 'pretixcontrol/email/forgot.txt', + self.email, + _('Reset your password'), + 'pretixcontrol/email/forgot.txt', { 'instance': settings.PRETIX_INSTANCE_NAME, 'user': self, diff --git a/src/pretix/base/services/cancelevent.py b/src/pretix/base/services/cancelevent.py index 575a13f14..130aae2df 100644 --- a/src/pretix/base/services/cancelevent.py +++ b/src/pretix/base/services/cancelevent.py @@ -22,6 +22,7 @@ import logging from decimal import Decimal +from django.conf import settings from django.db import transaction from django.db.models import Count, Exists, IntegerField, OuterRef, Q, Subquery from django.utils.crypto import get_random_string @@ -377,12 +378,13 @@ def cancel_event(self, event: Event, subevent: int, auto_refund: bool, confirmation_code = get_random_string(8, allowed_chars="01234567890") mail( user.email, - subject=gettext('Bulk-refund confirmation'), + subject=gettext('Confirm event cancellation and bulk refund'), template='pretixbase/email/cancel_confirm.txt', context={ "event": str(event), "amount": money_filter(refund_total, event.currency), "confirmation_code": confirmation_code, + "instance": settings.PRETIX_INSTANCE_NAME, }, locale=user.locale, ) diff --git a/src/pretix/base/services/export.py b/src/pretix/base/services/export.py index 93d214497..5c67effe2 100644 --- a/src/pretix/base/services/export.py +++ b/src/pretix/base/services/export.py @@ -340,12 +340,13 @@ def _run_scheduled_export(schedule, context: Union[Event, Organizer], exporter, if schedule.owner.is_active: mail( email=schedule.owner.email, - subject=gettext('Export failed'), + subject=gettext('Scheduled export failed'), template='pretixbase/email/export_failed.txt', context={ 'configuration_url': config_url, 'reason': msg, 'soft': soft, + 'instance': settings.PRETIX_INSTANCE_NAME, }, event=context if isinstance(context, Event) else None, organizer=context.organizer if isinstance(context, Event) else context, diff --git a/src/pretix/base/services/shredder.py b/src/pretix/base/services/shredder.py index 43d88b362..6e2b68763 100644 --- a/src/pretix/base/services/shredder.py +++ b/src/pretix/base/services/shredder.py @@ -44,7 +44,7 @@ from django.conf import settings from django.utils.crypto import get_random_string from django.utils.formats import date_format from django.utils.timezone import now -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ from pretix.base.i18n import language from pretix.base.models import CachedFile, Event, User, cachedfile_name @@ -171,15 +171,16 @@ def shred(self, event: Event, fileid: str, confirm_code: str, user: int=None, lo if user: with language(user.locale): + event_name = str(event.name) mail( user.email, - _('Data shredding completed'), + gettext('Data shredding completed for %(event)s') % {'event': event_name}, 'pretixbase/email/shred_completed.txt', { 'instance': settings.PRETIX_INSTANCE_NAME, 'user': user, 'organizer': event.organizer.name, - 'event': str(event.name), + 'event': event_name, 'start_time': date_format(parse(indexdata['time']).astimezone(event.timezone), 'SHORT_DATETIME_FORMAT'), 'shredders': ', '.join([str(s.verbose_name) for s in shredders]) }, diff --git a/src/pretix/base/templates/pretixbase/email/cancel_confirm.txt b/src/pretix/base/templates/pretixbase/email/cancel_confirm.txt index 5e3043156..c9da3aa6d 100644 --- a/src/pretix/base/templates/pretixbase/email/cancel_confirm.txt +++ b/src/pretix/base/templates/pretixbase/email/cancel_confirm.txt @@ -1,10 +1,14 @@ {% load i18n %} -{% trans "You have requested us to cancel an event which includes a larger bulk-refund:" %} +{% trans "You requested to cancel an event that involves a large bulk refund:" %} -{% trans "Event" %}: {{ event }} +- {% trans "Event" %}: {{ event }} +- {% trans "Estimated refund" %}: **{{ amount }}** -{% trans "Estimated refund amount" %}: **{{ amount }}** +{% trans "To confirm, paste the following code into the cancellation form:" %} -{% trans "Please confirm that you want to proceed by coping the following confirmation code into the cancellation form:" %} + {{ confirmation_code }} -**{{ confirmation_code }}** +{% blocktrans with instance=instance %}Don't share this code with anyone. The {{ instance }} team will never ask you for it.{% endblocktrans %} + +{% blocktrans with instance=instance %}Thanks, +The {{ instance }} Team{% endblocktrans %} diff --git a/src/pretix/base/templates/pretixbase/email/export_failed.txt b/src/pretix/base/templates/pretixbase/email/export_failed.txt index f11d8e2c2..d3dc36070 100644 --- a/src/pretix/base/templates/pretixbase/email/export_failed.txt +++ b/src/pretix/base/templates/pretixbase/email/export_failed.txt @@ -1,12 +1,13 @@ {% load i18n %} -{% trans "Your export failed." %} +{% trans "Your scheduled export failed." %} -{% trans "Reason:" %} {{ reason }} +- {% trans "Reason" %}: {{ reason }} -{% if not soft %} -{% trans "If your export fails five times in a row, it will no longer be sent." %} -{% endif %} +{% if not soft %}{% trans "If an export fails five times in a row, we'll stop sending it." %}{% endif %} -{% trans "Configuration link:" %} +{% trans "You can adjust or remove this export here:" %} {{ configuration_url }} + +{% blocktrans with instance=instance %}Thanks, +The {{ instance }} Team{% endblocktrans %} diff --git a/src/pretix/base/templates/pretixbase/email/notification.html b/src/pretix/base/templates/pretixbase/email/notification.html index 76b70c2dd..81b673d8f 100644 --- a/src/pretix/base/templates/pretixbase/email/notification.html +++ b/src/pretix/base/templates/pretixbase/email/notification.html @@ -52,13 +52,13 @@
- {% trans "You receive these emails based on your notification settings." %}
+ {% trans "You're receiving this email based on your notification settings." %}
- {% trans "Click here to view and change your notification settings" %} + {% trans "Manage settings" %} {% if disable_url %}
- {% trans "Click here disable all notifications immediately." %} + {% trans "Disable all notifications" %} {% endif %}
diff --git a/src/pretix/base/templates/pretixbase/email/notification.txt b/src/pretix/base/templates/pretixbase/email/notification.txt index b4174b250..8e5614542 100644 --- a/src/pretix/base/templates/pretixbase/email/notification.txt +++ b/src/pretix/base/templates/pretixbase/email/notification.txt @@ -1,19 +1,21 @@ {% load i18n %} {{ notification.title }}{% if notification.detail %} -{{ notification.detail }} -{% endif %}{% if notification.url %} +{{ notification.detail }}{% endif %}{% if notification.url %} -{{ notification.url }}{% endif %}{% for attr in notification.attributes %} +{{ notification.url }}{% endif %}{% if notification.attributes %} -{{ attr.title }}: {{ attr.value }}{% endfor %}{% for action in notification.actions %} +{% for attr in notification.attributes %}- {{ attr.title }}: {{ attr.value }} +{% endfor %}{% endif %}{% for action in notification.actions %} -{{ action.label }} - {{ action.url }}{% endfor %} +{{ action.label }}: -{% trans "You receive these emails based on your notification settings." %} -{% trans "Click here to view and change your notification settings:" %} -{{ settings_url }} -{% if disable_url %}{% trans "Click here disable all notifications immediately:" %} -{{ disable_url }} +{{ action.url }}{% endfor %} + +--- + +{% trans "You're receiving this email based on your notification settings." %} + +- {% trans "Manage settings" %}: {{ settings_url }} +{% if disable_url %}- {% trans "Disable all notifications" %}: {{ disable_url }} {% endif %} diff --git a/src/pretix/base/templates/pretixbase/email/shred_completed.txt b/src/pretix/base/templates/pretixbase/email/shred_completed.txt index 4bde98e16..49e375c92 100644 --- a/src/pretix/base/templates/pretixbase/email/shred_completed.txt +++ b/src/pretix/base/templates/pretixbase/email/shred_completed.txt @@ -1,17 +1,14 @@ -{% load i18n %} -{% load i18n %}{% blocktrans with url=url|safe %}Hello, +{% load i18n %}{% blocktrans %}Hello, -we hereby confirm that the following data shredding job has been completed: +The following data shredding job has been completed: -Organizer: {{ organizer }} +- Organizer: {{ organizer }} +- Event: {{ event }} +- Data selection: {{ shredders }} +- Start time: {{ start_time }} -Event: {{ event }} +Any data added to the event after the start time may not have been deleted. -Data selection: {{ shredders }} - -Start time: {{ start_time }} (new data added after this time might not have been deleted) - -Best regards, - -Your {{ instance }} team +Thanks, +The {{ instance }} Team {% endblocktrans %} diff --git a/src/pretix/control/templates/pretixcontrol/email/confirmation_code.txt b/src/pretix/control/templates/pretixcontrol/email/confirmation_code.txt index 27966e6d9..38fc7c5da 100644 --- a/src/pretix/control/templates/pretixcontrol/email/confirmation_code.txt +++ b/src/pretix/control/templates/pretixcontrol/email/confirmation_code.txt @@ -1,13 +1,13 @@ -{% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello, +{% load i18n %}{% blocktrans with code=code reason=reason instance=instance %}Hello, {{ reason }} {{ code }} -Please do never give this code to another person. Our support team will never ask for this code. +Don't share this code with anyone. The {{ instance }} team will never ask you for it. -If this code was not requested by you, please contact us immediately. +If you didn't request this code, please contact us immediately. -Best regards, -Your {{ instance }} team +Thanks, +The {{ instance }} Team {% endblocktrans %} diff --git a/src/pretix/control/templates/pretixcontrol/email/email_setup.txt b/src/pretix/control/templates/pretixcontrol/email/email_setup.txt index d2b62a290..9635cab84 100644 --- a/src/pretix/control/templates/pretixcontrol/email/email_setup.txt +++ b/src/pretix/control/templates/pretixcontrol/email/email_setup.txt @@ -1,14 +1,15 @@ -{% load i18n %}{% blocktrans with code=code instance=instance %}Hello, +{% load i18n %}{% blocktrans with code=code address=address instance=instance %}Hello, -someone requested to use {{ address }} as a sender address on {{ instance }}. -This will allow them to send emails that are shown to originate from this email address. -If that was you, please enter the following confirmation code: +Someone requested to use {{ address }} as a sender address on {{ instance }}. Once verified, emails sent from {{ instance }} can show this address as the sender. -{{ code }} +If this was you, enter the following code in the setup form: -If this was not requested by you, you can safely ignore this email. + {{ code }} -Best regards, +Don't share this code with anyone. The {{ instance }} team will never ask you for it. -Your {{ instance }} team -{% endblocktrans %} \ No newline at end of file +If you didn't request this, you can safely ignore this email. + +Thanks, +The {{ instance }} Team +{% endblocktrans %} diff --git a/src/pretix/control/templates/pretixcontrol/email/forgot.txt b/src/pretix/control/templates/pretixcontrol/email/forgot.txt index a9292eb52..75d5b0f65 100644 --- a/src/pretix/control/templates/pretixcontrol/email/forgot.txt +++ b/src/pretix/control/templates/pretixcontrol/email/forgot.txt @@ -1,9 +1,11 @@ {% load i18n %}{% blocktrans with url=url|safe %}Hello, -you requested a new password. Please go to the following page to reset your password: +We received a request to reset the password for your {{ instance }} account. To choose a new password, follow the link below: {{ url }} -Best regards, -Your {{ instance }} team +If you didn't request this, you can safely ignore this email — your password won't change. + +Thanks, +The {{ instance }} Team {% endblocktrans %} diff --git a/src/pretix/control/templates/pretixcontrol/email/invitation.txt b/src/pretix/control/templates/pretixcontrol/email/invitation.txt index 7b3b6c160..c49efaa5b 100644 --- a/src/pretix/control/templates/pretixcontrol/email/invitation.txt +++ b/src/pretix/control/templates/pretixcontrol/email/invitation.txt @@ -1,17 +1,16 @@ {% load i18n %}{% blocktrans with url=url|safe %}Hello, -you have been invited to a team on {{ instance }}, a platform to perform event -ticket sales. +You've been invited to join a team on {{ instance }}, an event ticket sales platform. -Organizer: {{ organizer }} -Team: {{ team }} +- Organizer: {{ organizer }} +- Team: {{ team }} + +To accept, follow the link below: -If you want to join that team, just click on the following link: {{ url }} -If you do not want to join, you can safely ignore or delete this email. +If you don't want to join, you can safely ignore this email. -Best regards, - -Your {{ instance }} team +Thanks, +The {{ instance }} Team {% endblocktrans %} diff --git a/src/pretix/control/templates/pretixcontrol/email/login_notice.txt b/src/pretix/control/templates/pretixcontrol/email/login_notice.txt index 44b8d01d1..0bd786690 100644 --- a/src/pretix/control/templates/pretixcontrol/email/login_notice.txt +++ b/src/pretix/control/templates/pretixcontrol/email/login_notice.txt @@ -1,13 +1,19 @@ -{% load i18n %}{% blocktrans with url=url|safe os=source.os_type agent=source.agent_type %}Hello, +{% load i18n %}{% blocktrans %}Hello, -a login to your {{ instance }} account from an unusual or new location was detected. The login was performed using {{ agent }} on {{ os }} from {{ country }}. +We noticed a new sign-in to your {{ instance }} account: +{% endblocktrans %} +- {% trans "Time" %}: {{ when }} +- {% trans "Browser" %}: {{ agent }} +- {% trans "Operating system" %}: {{ os }} +{% if device %}- {% trans "Device" %}: {{ device }} +{% endif %}{% if country %}- {% trans "Country" %}: {{ country }} +{% endif %} +{% blocktrans with url=url|safe %}If it was you, no action is needed. -If this was you, you can safely ignore this email. - -If this was not you, we recommend that you change your password in your account settings: +If you don't recognise this sign-in, please change your password immediately: {{ url }} -Best regards, -Your {{ instance }} team +Thanks, +The {{ instance }} Team {% endblocktrans %} diff --git a/src/pretix/control/templates/pretixcontrol/email/security_notice.txt b/src/pretix/control/templates/pretixcontrol/email/security_notice.txt index a8f6531f3..8edcab067 100644 --- a/src/pretix/control/templates/pretixcontrol/email/security_notice.txt +++ b/src/pretix/control/templates/pretixcontrol/email/security_notice.txt @@ -1,16 +1,15 @@ {% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello, -this is to inform you that the account information of your {{ instance }} account has been -changed. In particular, the following changes have been performed: +The following changes were made to your {{ instance }} account: {{ messages }} -If this change was not performed by you, please contact us immediately. +If you didn't make these changes, please contact the {{ instance }} support team immediately. -You can review and change your account settings here: +You can review your account settings here: {{ url }} -Best regards, -Your {{ instance }} team +Thanks, +The {{ instance }} Team {% endblocktrans %} diff --git a/src/pretix/control/views/mailsetup.py b/src/pretix/control/views/mailsetup.py index 6ef1f70fb..ceaede1ee 100644 --- a/src/pretix/control/views/mailsetup.py +++ b/src/pretix/control/views/mailsetup.py @@ -28,7 +28,7 @@ from django.core.mail import get_connection from django.shortcuts import redirect from django.utils.crypto import get_random_string from django.utils.functional import cached_property -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext, gettext_lazy as _ from django.views.generic import TemplateView from pretix.base import email @@ -216,13 +216,14 @@ class MailSettingsSetupView(TemplateView): messages.error(request, _('The verification code was incorrect, please try again.')) else: self.request.session[session_key] = get_random_string(length=6, allowed_chars='1234567890') + sender_address = self.simple_form.cleaned_data.get('mail_from') mail( - self.simple_form.cleaned_data.get('mail_from'), - _('Sender address verification'), + sender_address, + gettext('Confirm %(address)s as a sender address') % {'address': sender_address}, 'pretixcontrol/email/email_setup.txt', { 'code': self.request.session[session_key], - 'address': self.simple_form.cleaned_data.get('mail_from'), + 'address': sender_address, 'instance': settings.PRETIX_INSTANCE_NAME, }, None, diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index b891349df..62f26c899 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -1032,7 +1032,9 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, def _send_invite(self, instance): mail( instance.email, - _('Account invitation'), + gettext('You\'ve been invited to join %(organizer)s') % { + 'organizer': self.request.organizer.name, + }, 'pretixcontrol/email/invitation.txt', { 'instance': settings.PRETIX_INSTANCE_NAME, diff --git a/src/pretix/helpers/security.py b/src/pretix/helpers/security.py index b9aa92456..847aed36d 100644 --- a/src/pretix/helpers/security.py +++ b/src/pretix/helpers/security.py @@ -27,6 +27,7 @@ from django.conf import settings from django.contrib.auth import login as auth_login from django.contrib.gis import geoip2 from django.core.cache import cache +from django.utils.formats import date_format from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from django_countries.fields import Country @@ -169,11 +170,15 @@ def handle_login_source(user, request): with language(user.locale): mail( user.email, - _('Login from new source detected'), + _('New sign-in to your account'), 'pretixcontrol/email/login_notice.txt', { - 'source': src, - 'country': Country(str(country)).name if country else _('Unknown country'), + 'when': date_format(src.last_seen, 'DATETIME_FORMAT'), + 'agent': src.agent_type, + 'os': src.os_type, + # ua-parser returns "Other" for unidentified desktop devices. + 'device': src.device_type if src.device_type and src.device_type != 'Other' else None, + 'country': Country(str(country)).name if country else None, 'instance': settings.PRETIX_INSTANCE_NAME, 'url': mainreverse_absolute('control:user.settings') }, diff --git a/src/tests/base/test_export.py b/src/tests/base/test_export.py index 80c5deb87..7dab7d38c 100644 --- a/src/tests/base/test_export.py +++ b/src/tests/base/test_export.py @@ -104,7 +104,7 @@ def test_event_fail_invalid_config(event, user): assert s.schedule_next_run > now() assert s.error_counter == 1 assert len(djmail.outbox) == 1 - assert djmail.outbox[0].subject == "Export failed" + assert djmail.outbox[0].subject == "Scheduled export failed" assert "Reason: Export type not found" in djmail.outbox[0].body assert djmail.outbox[0].to == [user.email] @@ -152,7 +152,7 @@ def test_event_fail_user_no_permission(event, user, team): assert s.schedule_next_run > now() assert s.error_counter == 1 assert len(djmail.outbox) == 1 - assert djmail.outbox[0].subject == "Export failed" + assert djmail.outbox[0].subject == "Scheduled export failed" assert "Reason: Export type not found or permission denied." in djmail.outbox[0].body assert djmail.outbox[0].to == [user.email] @@ -235,7 +235,7 @@ def test_organizer_fail_invalid_config(event, user): assert s.schedule_next_run > now() assert s.error_counter == 1 assert len(djmail.outbox) == 1 - assert djmail.outbox[0].subject == "Export failed" + assert djmail.outbox[0].subject == "Scheduled export failed" assert "Reason: Export type not found" in djmail.outbox[0].body assert djmail.outbox[0].to == [user.email] @@ -283,7 +283,7 @@ def test_organizer_fail_user_does_not_have_specific_permission(event, user, team assert s.schedule_next_run > now() assert s.error_counter == 1 assert len(djmail.outbox) == 1 - assert djmail.outbox[0].subject == "Export failed" + assert djmail.outbox[0].subject == "Scheduled export failed" assert "Reason: Export type not found or permission denied." in djmail.outbox[0].body assert djmail.outbox[0].to == [user.email]