Store whether we know email addresses are working because links have been clicked

This commit is contained in:
Raphael Michel
2019-05-15 08:21:08 +02:00
parent 00bc5f4fae
commit de0e700fec
14 changed files with 99 additions and 29 deletions

View File

@@ -482,6 +482,7 @@ class OrderViewSet(viewsets.ModelViewSet):
)
if 'email' in self.request.data and serializer.instance.email != self.request.data.get('email'):
serializer.instance.email_known_to_work = False
serializer.instance.log_action(
'pretix.event.order.contact.changed',
user=self.request.user,

View File

@@ -0,0 +1,18 @@
# Generated by Django 2.2.1 on 2019-05-15 05:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0120_auto_20190509_0736'),
]
operations = [
migrations.AddField(
model_name='order',
name='email_known_to_work',
field=models.BooleanField(default=False),
),
]

View File

@@ -1,4 +1,5 @@
import copy
import hashlib
import json
import logging
import os
@@ -180,6 +181,10 @@ class Order(LockModel, LoggedModel):
default=False
)
sales_channel = models.CharField(max_length=190, default="web")
email_known_to_work = models.BooleanField(
default=False,
verbose_name=_('E-mail address verified')
)
class Meta:
verbose_name = _("Order")
@@ -206,6 +211,9 @@ class Order(LockModel, LoggedModel):
self.event.cache.delete('complain_testmode_orders')
self.delete()
def email_confirm_hash(self):
return hashlib.sha256(settings.SECRET_KEY + self.secret.encode()).hexdigest()[:9]
@property
def fees(self):
"""
@@ -729,9 +737,10 @@ class Order(LockModel, LoggedModel):
email_template = self.event.settings.mail_text_resend_link
email_context = {
'event': self.event.name,
'url': build_absolute_uri(self.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(self.event, 'presale:event.order.open', kwargs={
'order': self.code,
'secret': self.secret
'secret': self.secret,
'hash': self.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
@@ -1259,9 +1268,10 @@ class OrderPayment(models.Model):
email_template = self.order.event.settings.mail_text_order_paid
email_context = {
'event': self.order.event.name,
'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(self.order.event, 'presale:event.order.open', kwargs={
'order': self.order.code,
'secret': self.order.secret
'secret': self.order.secret,
'hash': self.order.email_confirm_hash()
}),
'downloads': self.order.event.settings.get('ticket_download', as_type=bool),
'invoice_name': invoice_name,

View File

@@ -142,9 +142,10 @@ def mail(email: str, subject: str, template: Union[str, LazyI18nString],
"You can view your order details at the following URL:\n{orderurl}."
).replace("\n", "\r\n").format(
event=event.name, orderurl=build_absolute_uri(
order.event, 'presale:event.order', kwargs={
order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
}
)
)

View File

@@ -222,9 +222,10 @@ def approve_order(order, user=None, send_mail: bool=True, auth=None, force=False
'total_with_currency': LazyCurrencyNumber(order.total, order.event.currency),
'date': LazyDate(order.expires),
'event': order.event.name,
'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
@@ -282,9 +283,10 @@ def deny_order(order, comment='', user=None, send_mail: bool=True, auth=None):
'total_with_currency': LazyCurrencyNumber(order.total, order.event.currency),
'date': LazyDate(order.expires),
'event': order.event.name,
'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'comment': comment,
'invoice_name': invoice_name,
@@ -375,9 +377,10 @@ def _cancel_order(order, user=None, send_mail: bool=True, api_token=None, device
email_context = {
'event': order.event.name,
'code': order.code,
'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
})
}
with language(order.locale):
@@ -730,9 +733,10 @@ def _perform_order(event: str, payment_provider: str, position_ids: List[str],
'total_with_currency': LazyCurrencyNumber(order.total, event.currency),
'date': LazyDate(order.expires),
'event': event.name,
'url': build_absolute_uri(event, 'presale:event.order', kwargs={
'url': build_absolute_uri(event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'payment_info': payment_info,
'invoice_name': invoice_name,
@@ -800,9 +804,10 @@ def send_expiry_warnings(sender, **kwargs):
email_template = eventsettings.mail_text_order_expire_warning
email_context = {
'event': o.event.name,
'url': build_absolute_uri(o.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(o.event, 'presale:event.order.open', kwargs={
'order': o.code,
'secret': o.secret
'secret': o.secret,
'hash': o.email_confirm_hash()
}),
'expire_date': date_format(o.expires.astimezone(tz), 'SHORT_DATE_FORMAT'),
'invoice_name': invoice_name,
@@ -851,9 +856,10 @@ def send_download_reminders(sender, **kwargs):
email_template = e.settings.mail_text_download_reminder
email_context = {
'event': o.event.name,
'url': build_absolute_uri(o.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(o.event, 'presale:event.order.open', kwargs={
'order': o.code,
'secret': o.secret
'secret': o.secret,
'hash': o.email_confirm_hash()
}),
}
email_subject = _('Your ticket is ready for download: %(code)s') % {'code': o.code}
@@ -1350,9 +1356,10 @@ class OrderChangeManager:
email_template = order.event.settings.mail_text_order_changed
email_context = {
'event': order.event.name,
'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(self.order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,

View File

@@ -27,7 +27,7 @@
<strong>{% trans "Event:" %}</strong> {{ event.name }}<br>
<strong>{% trans "Order code:" %}</strong> {{ order.code }}<br>
<strong>{% trans "Order date:" %}</strong> {{ order.datetime|date:"SHORT_DATE_FORMAT" }}<br>
<a href="{% abseventurl event "presale:event.order" order=order.code secret=order.secret %}">
<a href="{% abseventurl event "presale:event.order.open" hash=order.email_confirm_hash order=order.code secret=order.secret %}">
{% trans "View order details" %}
</a>
</div>

View File

@@ -340,7 +340,7 @@ class OrderContactForm(forms.ModelForm):
class Meta:
model = Order
fields = ['email']
fields = ['email', 'email_known_to_work']
class OrderLocaleForm(forms.ModelForm):

View File

@@ -136,7 +136,10 @@
{% endif %}
<dt>{% trans "User" %}</dt>
<dd>
{{ order.email|default_if_none:"" }}&nbsp;&nbsp;
{{ order.email|default_if_none:"" }}
{% if order.email and order.email_known_to_work %}
<span class="fa fa-check-circle text-success" data-toggle="tooltip" title="{% trans "We know that this email address works because the user clicked a link we sent them." %}"></span>
{% endif %}&nbsp;&nbsp;
<a href="{% url "control:event.order.contact" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
<span class="fa fa-edit"></span>
</a>

View File

@@ -1365,6 +1365,7 @@ class OrderContactChange(OrderView):
},
user=self.request.user,
)
if self.form.cleaned_data['regenerate_secrets']:
changed = True
self.order.secret = generate_secret()
@@ -1474,9 +1475,10 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
'code': order.code,
'date': date_format(order.datetime.astimezone(tz), 'SHORT_DATETIME_FORMAT'),
'expire_date': date_format(order.expires, 'SHORT_DATE_FORMAT'),
'url': build_absolute_uri(order.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(order.event, 'presale:event.order.open', kwargs={
'order': order.code,
'secret': order.secret
'secret': order.secret,
'hash': order.email_confirm_hash()
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,

View File

@@ -38,9 +38,10 @@ def notify_incomplete_payment(o: Order):
email_template = o.event.settings.mail_text_order_expire_warning
email_context = {
'event': o.event.name,
'url': build_absolute_uri(o.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(o.event, 'presale:event.order.open', kwargs={
'order': o.code,
'secret': o.secret
'secret': o.secret,
'hash': o.email_confirm_hash()
}),
'expire_date': date_format(o.expires.astimezone(tz), 'SHORT_DATE_FORMAT'),
'invoice_name': invoice_name,

View File

@@ -95,9 +95,10 @@ class SenderView(EventPermissionRequiredMixin, FormView):
'event': self.request.event.name,
'date': date_format(now(), 'SHORT_DATE_FORMAT'),
'expire_date': date_format(now() + timedelta(days=7), 'SHORT_DATE_FORMAT'),
'url': build_absolute_uri(self.request.event, 'presale:event.order', kwargs={
'url': build_absolute_uri(self.request.event, 'presale:event.order.open', kwargs={
'order': 'ORDER1234',
'secret': 'longrandomsecretabcdef123456'
'secret': 'longrandomsecretabcdef123456',
'hash': 'abcdef',
}),
'invoice_name': _('John Doe'),
'invoice_company': _('Sample Company LLC')

View File

@@ -49,6 +49,8 @@ event_patterns = [
name='event.cart.add'),
url(r'resend/$', pretix.presale.views.user.ResendLinkView.as_view(), name='event.resend_link'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/open/(?P<hash>[a-z0-9]+)/$', pretix.presale.views.order.OrderOpen.as_view(),
name='event.order.open'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/$', pretix.presale.views.order.OrderDetails.as_view(),
name='event.order'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/invoice$',

View File

@@ -65,6 +65,15 @@ class OrderDetailMixin(NoSearchIndexViewMixin):
})
@method_decorator(xframe_options_exempt, 'dispatch')
class OrderOpen(EventViewMixin, OrderDetailMixin, View):
def dispatch(self, request, *args, **kwargs):
if kwargs.get('hash') == self.order.email_confirm_hash():
self.order.email_known_to_work = True
self.order.save(update_fields=['email_known_to_work'])
return redirect(self.get_order_url())
@method_decorator(xframe_options_exempt, 'dispatch')
class OrderDetails(EventViewMixin, OrderDetailMixin, CartMixin, TemplateView):
template_name = "pretixpresale/event/order.html"