diff --git a/src/pretix/base/migrations/0016_order_guest_email.py b/src/pretix/base/migrations/0016_order_guest_email.py
new file mode 100644
index 0000000000..fef35d8b06
--- /dev/null
+++ b/src/pretix/base/migrations/0016_order_guest_email.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0015_auto_20150916_2219'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='order',
+ name='guest_email',
+ field=models.EmailField(max_length=254, verbose_name='E-mail', blank=True, null=True),
+ ),
+ ]
diff --git a/src/pretix/base/migrations/0017_order_guest_locale.py b/src/pretix/base/migrations/0017_order_guest_locale.py
new file mode 100644
index 0000000000..79a63f7e2b
--- /dev/null
+++ b/src/pretix/base/migrations/0017_order_guest_locale.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('pretixbase', '0016_order_guest_email'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='order',
+ name='guest_locale',
+ field=models.CharField(max_length=32, null=True, blank=True, verbose_name='Locale'),
+ ),
+ ]
diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py
index 52678fdf72..7478e1602c 100644
--- a/src/pretix/base/models.py
+++ b/src/pretix/base/models.py
@@ -1442,6 +1442,14 @@ class Order(Versionable):
verbose_name=_("User"),
related_name="orders"
)
+ guest_email = models.EmailField(
+ null=True, blank=True,
+ verbose_name=_('E-mail')
+ )
+ guest_locale = models.CharField(
+ null=True, blank=True, max_length=32,
+ verbose_name=_('Locale')
+ )
secret = models.CharField(max_length=32, default=generate_secret)
datetime = models.DateTimeField(
verbose_name=_("Date")
@@ -1590,6 +1598,18 @@ class Order(Versionable):
return error_messages['busy']
return True
+ @property
+ def locale(self):
+ if self.user:
+ return self.user.locale
+ return self.guest_locale
+
+ @property
+ def email(self):
+ if self.user:
+ return self.user.email
+ return self.guest_email
+
class CachedTicket(models.Model):
order = VersionedForeignKey(Order, on_delete=models.CASCADE)
diff --git a/src/pretix/base/payment.py b/src/pretix/base/payment.py
index 626e4917b1..060549bc6f 100644
--- a/src/pretix/base/payment.py
+++ b/src/pretix/base/payment.py
@@ -14,6 +14,7 @@ from pretix.base.models import CartPosition, Order
from pretix.base.services.orders import mark_order_paid
from pretix.base.settings import SettingsSandbox
from pretix.base.signals import register_payment_providers
+from pretix.presale.views import user_cart_q
class BasePaymentProvider:
@@ -441,7 +442,7 @@ class FreeOrderProvider(BasePaymentProvider):
def is_allowed(self, request: HttpRequest) -> bool:
return CartPosition.objects.current.filter(
- Q(user=request.user) & Q(event=request.event)
+ user_cart_q(request) & Q(event=request.event)
).aggregate(sum=Sum('price'))['sum'] == 0
diff --git a/src/pretix/base/services/mail.py b/src/pretix/base/services/mail.py
index 37ec41f867..8893ebff38 100644
--- a/src/pretix/base/services/mail.py
+++ b/src/pretix/base/services/mail.py
@@ -13,7 +13,7 @@ from pretix.helpers.urls import build_absolute_uri
logger = logging.getLogger('pretix.base.mail')
-def mail(user: User, subject: str, template: str, context: dict=None, event: Event=None):
+def mail(email: str, subject: str, template: str, context: dict=None, event: Event=None, locale: str=None):
"""
Sends out an email to a user.
@@ -30,11 +30,8 @@ def mail(user: User, subject: str, template: str, context: dict=None, event: Eve
the email has been sent, just that it has been queued by the e-mail
backend.
"""
- if not user.email:
- return False
-
_lng = translation.get_language()
- translation.activate(user.locale or settings.LANGUAGE_CODE)
+ translation.activate(locale or settings.LANGUAGE_CODE)
if isinstance(template, LazyI18nString):
body = str(template)
@@ -66,7 +63,7 @@ def mail(user: User, subject: str, template: str, context: dict=None, event: Eve
)
body += "\r\n"
try:
- return mail_send([user.email], subject, body, sender)
+ return mail_send([email], subject, body, sender)
finally:
translation.activate(_lng)
diff --git a/src/pretix/base/services/orders.py b/src/pretix/base/services/orders.py
index a998adfa5f..0683d56b66 100644
--- a/src/pretix/base/services/orders.py
+++ b/src/pretix/base/services/orders.py
@@ -4,13 +4,16 @@ from django.db import transaction
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
-from pretix.base.models import EventLock, Order, OrderPosition, Quota
+from pretix.base.models import (
+ Event, EventLock, Order, OrderPosition, Quota, User,
+)
from pretix.base.services.mail import mail
from pretix.base.signals import order_paid, order_placed
from pretix.helpers.urls import build_absolute_uri
-def mark_order_paid(order, provider=None, info=None, date=None, manual=None, force=False):
+def mark_order_paid(order: Order, provider: str=None, info: str=None, date: datetime=None, manual: bool=None,
+ force: bool=False):
"""
Marks an order as paid. This clones the order object, sets the payment provider,
info and date and returns the cloned order object.
@@ -44,20 +47,19 @@ def mark_order_paid(order, provider=None, info=None, date=None, manual=None, for
from pretix.base.services.mail import mail
mail(
- order.user, _('Payment received for your order: %(code)s') % {'code': order.code},
+ order.email, _('Payment received for your order: %(code)s') % {'code': order.code},
'pretixpresale/email/order_paid.txt',
{
- 'user': order.user,
'order': order,
'event': order.event,
'url': build_absolute_uri('presale:event.order', kwargs={
'event': order.event.slug,
'organizer': order.event.organizer.slug,
'order': order.code,
- }),
+ }) + '?order_secret=' + order.secret,
'downloads': order.event.settings.get('ticket_download', as_type=bool)
},
- order.event
+ order.event, locale=order.locale
)
return order
@@ -66,7 +68,7 @@ class OrderError(Exception):
pass
-def check_positions(event, dt, positions):
+def check_positions(event: Event, dt: datetime, positions: list):
error_messages = {
'unavailable': _('Some of the products you selected were no longer available. '
'Please see below for details.'),
@@ -117,7 +119,8 @@ def check_positions(event, dt, positions):
raise OrderError(err)
-def perform_order(event, user, payment_provider, positions):
+def perform_order(event: Event, payment_provider: str, positions: list, user: User=None, email: str=None,
+ locale: str=None):
error_messages = {
'busy': _('We were not able to process your request completely as the '
'server was too busy. Please try again.'),
@@ -127,21 +130,22 @@ def perform_order(event, user, payment_provider, positions):
try:
with event.lock():
check_positions(event, dt, positions)
- order = place_order(event, user, positions, dt, payment_provider)
+ order = place_order(event, user, email if user is None else None, positions, dt, payment_provider,
+ locale=locale)
mail(
- user, _('Your order: %(code)s') % {'code': order.code},
+ order.email, _('Your order: %(code)s') % {'code': order.code},
'pretixpresale/email/order_placed.txt',
{
- 'user': user, 'order': order,
+ 'order': order,
'event': event,
'url': build_absolute_uri('presale:event.order', kwargs={
'event': event.slug,
'organizer': event.organizer.slug,
'order': order.code,
- }),
+ }) + '?order_secret=' + order.secret,
'payment': payment_provider.order_pending_mail_render(order)
},
- event
+ event, locale=order.locale
)
return order
except EventLock.LockTimeoutException:
@@ -151,7 +155,8 @@ def perform_order(event, user, payment_provider, positions):
@transaction.atomic()
-def place_order(event, user, positions, dt, payment_provider):
+def place_order(event: Event, user: User, email: str, positions: list, dt: datetime, payment_provider: str,
+ locale: str=None):
total = sum([c.price for c in positions])
payment_fee = payment_provider.calculate_fee(total)
total += payment_fee
@@ -162,8 +167,10 @@ def place_order(event, user, positions, dt, payment_provider):
status=Order.STATUS_PENDING,
event=event,
user=user,
+ guest_email=email,
datetime=dt,
expires=min(expires),
+ locale=locale,
total=total,
payment_fee=payment_fee,
payment_provider=payment_provider.identifier,
diff --git a/src/pretix/control/templates/pretixcontrol/order/index.html b/src/pretix/control/templates/pretixcontrol/order/index.html
index 51ee169f44..b36f78a8df 100644
--- a/src/pretix/control/templates/pretixcontrol/order/index.html
+++ b/src/pretix/control/templates/pretixcontrol/order/index.html
@@ -66,8 +66,8 @@
{% trans "Expiry date" %}
{{ order.expires }}
{% endif %}
-
{% trans "Username" %}
-
{{ order.user }}
+
{% trans "User" %}
+
{{ order.user|default:order.guest_email }}
diff --git a/src/pretix/plugins/sendmail/views.py b/src/pretix/plugins/sendmail/views.py
index 03cc47317a..3e1c1acd37 100644
--- a/src/pretix/plugins/sendmail/views.py
+++ b/src/pretix/plugins/sendmail/views.py
@@ -31,8 +31,8 @@ class SenderView(EventPermissionRequiredMixin, FormView):
users = set([o.user for o in orders])
for u in users:
- mail(u, form.cleaned_data['subject'], form.cleaned_data['message'],
- None, self.request.event)
+ mail(u.email, form.cleaned_data['subject'], form.cleaned_data['message'],
+ None, self.request.event, locale=u.locale)
messages.success(self.request, _('Your message will be sent to the selected users.'))
diff --git a/src/pretix/presale/forms/checkout.py b/src/pretix/presale/forms/checkout.py
index 4b8660fa63..321973510f 100644
--- a/src/pretix/presale/forms/checkout.py
+++ b/src/pretix/presale/forms/checkout.py
@@ -4,6 +4,10 @@ from django.utils.translation import ugettext_lazy as _
from pretix.base.models import Question
+class GuestForm(forms.Form):
+ email = forms.EmailField(label=_('E-mail'))
+
+
class QuestionsForm(forms.Form):
"""
This form class is responsible for asking order-related questions. This includes
diff --git a/src/pretix/presale/middleware.py b/src/pretix/presale/middleware.py
index a674e39a2f..bb2e7419fc 100644
--- a/src/pretix/presale/middleware.py
+++ b/src/pretix/presale/middleware.py
@@ -5,13 +5,24 @@ from pretix.base.models import Event
class EventMiddleware:
-
def process_request(self, request):
url = resolve(request.path_info)
url_namespace = url.namespace
url_name = url.url_name
if url_namespace != 'presale':
return
+
+ if 'order_secrets' not in request.session:
+ request.session['order_secrets'] = []
+ if 'order_secret' in request.GET and request.GET.get('order_secret') not in request.session['order_secrets']:
+ # We can't use append here, because this would not trigger __setitem__
+ # on the session store and would not be saved
+ request.session['order_secrets'] = request.session['order_secrets'] + [request.GET.get('order_secret')]
+ # Removal of the secret from the URL has been disabled so people can bookmark it
+ # g = request.GET.copy()
+ # del g['order_secret']
+ # return redirect(request.path + '?' + g.urlencode())
+
if 'event.' in url_name and 'event' in url.kwargs:
try:
request.event = Event.objects.current.filter(
diff --git a/src/pretix/presale/templates/pretixpresale/email/order_paid.txt b/src/pretix/presale/templates/pretixpresale/email/order_paid.txt
index f4e4d8951e..40fafb648c 100644
--- a/src/pretix/presale/templates/pretixpresale/email/order_paid.txt
+++ b/src/pretix/presale/templates/pretixpresale/email/order_paid.txt
@@ -11,7 +11,7 @@ Your {{ event }} team
we successfully received your payment for {{ event }}. Thank you!
-You can view the status of your order at
+You can change your order details and view the status of your order at
{{ url }}
Best regards,
diff --git a/src/pretix/presale/templates/pretixpresale/email/order_placed.txt b/src/pretix/presale/templates/pretixpresale/email/order_placed.txt
index cb577edc31..64238275c9 100644
--- a/src/pretix/presale/templates/pretixpresale/email/order_placed.txt
+++ b/src/pretix/presale/templates/pretixpresale/email/order_placed.txt
@@ -4,7 +4,7 @@ we successfully received your order for {{ event }} with a total value
of {{ total }} {{ currency }}. Please complete your payment before {{ date }}.
{{ paymentinfo }}
-You can view the status of your order at
+You can change your order details and view the status of your order at
{{ url }}
diff --git a/src/pretix/presale/templates/pretixpresale/event/forgot.html b/src/pretix/presale/templates/pretixpresale/event/forgot.html
index 88b5faa7c2..87d0ac24d9 100644
--- a/src/pretix/presale/templates/pretixpresale/event/forgot.html
+++ b/src/pretix/presale/templates/pretixpresale/event/forgot.html
@@ -10,7 +10,7 @@
{% bootstrap_field form.email layout="horizontal" %}