Compare commits

...

2 Commits

Author SHA1 Message Date
Raphael Michel
83c297c0a8 Allow to transfer tickets 2018-09-20 21:11:12 +02:00
Raphael Michel
60d1f02d26 Remove deprecated template part 2018-09-20 20:25:23 +02:00
7 changed files with 166 additions and 55 deletions

View File

@@ -10,6 +10,7 @@ from typing import Any, Dict, List, Union
import dateutil import dateutil
import pytz import pytz
from django.conf import settings from django.conf import settings
from django.contrib import messages
from django.db import models, transaction from django.db import models, transaction
from django.db.models import ( from django.db.models import (
Case, Exists, F, Max, OuterRef, Q, Subquery, Sum, Value, When, Case, Exists, F, Max, OuterRef, Q, Subquery, Sum, Value, When,
@@ -17,6 +18,7 @@ from django.db.models import (
from django.db.models.functions import Coalesce from django.db.models.functions import Coalesce
from django.db.models.signals import post_delete from django.db.models.signals import post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.shortcuts import redirect
from django.urls import reverse from django.urls import reverse
from django.utils.crypto import get_random_string from django.utils.crypto import get_random_string
from django.utils.encoding import escape_uri_path from django.utils.encoding import escape_uri_path
@@ -460,6 +462,47 @@ class Order(LoggedModel):
return self._is_still_available(count_waitinglist=count_waitinglist) return self._is_still_available(count_waitinglist=count_waitinglist)
def regenerate_secrets(self, user=None):
self.secret = generate_secret()
for op in self.positions.all():
op.secret = generate_position_secret()
op.save(update_fields=['secret'])
CachedTicket.objects.filter(order_position__order=self).delete()
CachedCombinedTicket.objects.filter(order=self).delete()
self.log_action('pretix.event.order.secret.changed', user=user)
self.save(update_fields=['secret'])
def resend_link(self, user=None):
from pretix.base.services.mail import SendMailException
from pretix.multidomain.urlreverse import build_absolute_uri
with language(self.locale):
try:
try:
invoice_name = self.invoice_address.name
invoice_company = self.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
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={
'order': self.code,
'secret': self.secret
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
email_subject = _('Your order: %(code)s') % {'code': self.code}
self.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.resend', user=user
)
except SendMailException:
messages.error(self.request, _('There was an error sending the mail. Please try again later.'))
return redirect(self.get_order_url())
def _is_still_available(self, now_dt: datetime=None, count_waitinglist=True) -> Union[bool, str]: def _is_still_available(self, now_dt: datetime=None, count_waitinglist=True) -> Union[bool, str]:
error_messages = { error_messages = {
'unavailable': _('The ordered product "{item}" is no longer available.'), 'unavailable': _('The ordered product "{item}" is no longer available.'),

View File

@@ -30,7 +30,6 @@ from pretix.base.i18n import language
from pretix.base.models import ( from pretix.base.models import (
CachedCombinedTicket, CachedFile, CachedTicket, Invoice, InvoiceAddress, CachedCombinedTicket, CachedFile, CachedTicket, Invoice, InvoiceAddress,
Item, ItemVariation, LogEntry, Order, QuestionAnswer, Quota, Item, ItemVariation, LogEntry, Order, QuestionAnswer, Quota,
generate_position_secret, generate_secret,
) )
from pretix.base.models.event import SubEvent from pretix.base.models.event import SubEvent
from pretix.base.models.orders import OrderFee, OrderPayment, OrderRefund from pretix.base.models.orders import OrderFee, OrderPayment, OrderRefund
@@ -845,33 +844,7 @@ class OrderResendLink(OrderView):
permission = 'can_change_orders' permission = 'can_change_orders'
def post(self, *args, **kwargs): def post(self, *args, **kwargs):
with language(self.order.locale): self.order.resend_link(self.request.user)
try:
try:
invoice_name = self.order.invoice_address.name
invoice_company = self.order.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
email_template = self.order.event.settings.mail_text_resend_link
email_context = {
'event': self.order.event.name,
'url': build_absolute_uri(self.order.event, 'presale:event.order', kwargs={
'order': self.order.code,
'secret': self.order.secret
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
email_subject = _('Your order: %(code)s') % {'code': self.order.code}
self.order.send_mail(
email_subject, email_template, email_context,
'pretix.event.order.email.resend', user=self.request.user
)
except SendMailException:
messages.error(self.request, _('There was an error sending the mail. Please try again later.'))
return redirect(self.get_order_url())
messages.success(self.request, _('The email has been queued to be sent.')) messages.success(self.request, _('The email has been queued to be sent.'))
return redirect(self.get_order_url()) return redirect(self.get_order_url())
@@ -1166,13 +1139,7 @@ class OrderContactChange(OrderView):
) )
if self.form.cleaned_data['regenerate_secrets']: if self.form.cleaned_data['regenerate_secrets']:
changed = True changed = True
self.order.secret = generate_secret() self.order.regenerate_secrets(self.request.user)
for op in self.order.positions.all():
op.secret = generate_position_secret()
op.save()
CachedTicket.objects.filter(order_position__order=self.order).delete()
CachedCombinedTicket.objects.filter(order=self.order).delete()
self.order.log_action('pretix.event.order.secret.changed', user=self.request.user)
self.form.save() self.form.save()
if changed: if changed:

View File

@@ -0,0 +1,33 @@
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from pretix.base.validators import EmailBlacklistValidator
class ChangeContactForm(forms.Form):
email = forms.EmailField(label=_('E-mail'),
help_text=_('Make sure to enter a valid email address. We will send an email containing '
'the new link to the ticket there.'),
validators=[EmailBlacklistValidator()])
email_repeat = forms.EmailField(
label=_('E-mail address (repeated)'),
help_text=_('Please enter the same email address again to make sure you typed it correctly.')
)
check_noaccess = forms.BooleanField(
label=_('I have understood that after this operation, I will no longer have access to these tickets. The link '
'of the ticket order will be changed and the new link will be sent to the given email address.')
)
check_printed = forms.BooleanField(
label=_('I have understood that after this operation, all printed or downloaded tickets from this order will '
'be invalid and need to be downloaded again.')
)
check_data = forms.BooleanField(
label=_('I have understood that after this operation, the new owner will have access to all personal data '
'included in my ticket order, such as information given for the tickets, my invoicing address, or '
'previous invoices.')
)
def clean(self):
if self.cleaned_data.get('email').lower() != self.cleaned_data.get('email_repeat').lower():
raise ValidationError(_('Please enter the same email address twice.'))

View File

@@ -223,29 +223,24 @@
{% endif %} {% endif %}
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
{% if order.can_user_cancel %} <div class="row">
<div class="row"> <div class="col-md-12 text-right">
<div class="col-md-12 text-right"> <p>
<p> {% if order.status == "p" or order.status == "n" or order.status == "e" %}
<a href="{% eventurl event 'presale:event.order.transfer' secret=order.secret order=order.code %}"
class="btn btn-default">
<span class="fa fa-mail-forward"></span>
{% trans "Transfer order" %}
</a>
{% endif %}
{% if order.can_user_cancel %}
<a href="{% eventurl event 'presale:event.order.cancel' secret=order.secret order=order.code %}" <a href="{% eventurl event 'presale:event.order.cancel' secret=order.secret order=order.code %}"
class="btn btn-danger"> class="btn btn-danger">
<span class="fa fa-remove"></span> <span class="fa fa-remove"></span>
{% trans "Cancel order" %} {% trans "Cancel order" %}
</a> </a>
</p> {% endif %}
</div> </p>
</div> </div>
{% endif %} </div>
{% if order.status == "p" and payment %}
<div class="panel panel-success">
<div class="panel-heading">
<h3 class="panel-title">
{% trans "Payment" %}
</h3>
</div>
<div class="panel-body">
{{ payment }}
</div>
</div>
{% endif %}
{% endblock %} {% endblock %}

View File

@@ -0,0 +1,32 @@
{% extends "pretixpresale/event/base.html" %}
{% load i18n %}
{% load eventurl %}
{% load bootstrap3 %}
{% block title %}{% trans "Transfer order" %}{% endblock %}
{% block content %}
<h2>
{% blocktrans trimmed with code=order.code %}
Transfer order: {{ code }}
{% endblocktrans %}
</h2>
<form method="post" action="" class="form-horizontal">
{% csrf_token %}
{% bootstrap_form form layout='horizontal' %}
<div class="row checkout-button-row">
<div class="col-md-4">
<a class="btn btn-block btn-default btn-lg"
href="{% eventurl request.event "presale:event.order" secret=order.secret order=order.code %}">
{% trans "No, take me back" %}
</a>
</div>
<div class="col-md-4 col-md-offset-4">
<button class="btn btn-block btn-danger btn-lg" type="submit">
{% trans "Yes, transfer order" %}
</button>
</div>
<div class="clearfix"></div>
</div>
</form>
{% endblock %}

View File

@@ -59,6 +59,9 @@ event_patterns = [
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel/do$', url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/cancel/do$',
pretix.presale.views.order.OrderCancelDo.as_view(), pretix.presale.views.order.OrderCancelDo.as_view(),
name='event.order.cancel.do'), name='event.order.cancel.do'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/transfer$',
pretix.presale.views.order.OrderTransfer.as_view(),
name='event.order.transfer'),
url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/modify$', url(r'^order/(?P<order>[^/]+)/(?P<secret>[A-Za-z0-9]+)/modify$',
pretix.presale.views.order.OrderModify.as_view(), pretix.presale.views.order.OrderModify.as_view(),
name='event.order.modify'), name='event.order.modify'),

View File

@@ -13,7 +13,7 @@ from django.utils.functional import cached_property
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_exempt from django.views.decorators.clickjacking import xframe_options_exempt
from django.views.generic import TemplateView, View from django.views.generic import FormView, TemplateView, View
from pretix.base.models import CachedTicket, Invoice, Order, OrderPosition from pretix.base.models import CachedTicket, Invoice, Order, OrderPosition
from pretix.base.models.orders import ( from pretix.base.models.orders import (
@@ -34,6 +34,7 @@ from pretix.base.views.tasks import AsyncAction
from pretix.helpers.safedownload import check_token from pretix.helpers.safedownload import check_token
from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse from pretix.multidomain.urlreverse import build_absolute_uri, eventreverse
from pretix.presale.forms.checkout import InvoiceAddressForm, QuestionsForm from pretix.presale.forms.checkout import InvoiceAddressForm, QuestionsForm
from pretix.presale.forms.order import ChangeContactForm
from pretix.presale.views import CartMixin, EventViewMixin from pretix.presale.views import CartMixin, EventViewMixin
from pretix.presale.views.robots import NoSearchIndexViewMixin from pretix.presale.views.robots import NoSearchIndexViewMixin
@@ -773,3 +774,40 @@ class InvoiceDownload(EventViewMixin, OrderDetailMixin, View):
return self.get(request, *args, **kwargs) return self.get(request, *args, **kwargs)
resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(invoice.number) resp['Content-Disposition'] = 'attachment; filename="{}.pdf"'.format(invoice.number)
return resp return resp
@method_decorator(xframe_options_exempt, 'dispatch')
class OrderTransfer(EventViewMixin, OrderDetailMixin, FormView):
template_name = "pretixpresale/event/order_transfer.html"
form_class = ChangeContactForm
def dispatch(self, request, *args, **kwargs):
self.request = request
self.kwargs = kwargs
if not self.order:
raise Http404(_('Unknown order code or not authorized to access this order.'))
return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['order'] = self.order
return ctx
def form_valid(self, form):
with transaction.atomic():
self.order.email = form.cleaned_data['email']
self.order.log_action(
'pretix.event.order.contact.changed',
data={
'old_email': self.order.email,
'new_email': form.cleaned_data['email'],
},
user=self.request.user,
)
self.order.regenerate_secrets()
self.order.resend_link()
messages.success(self.request, _('The ticket order has been transfered and an email will be sent to the '
'new owner.'))
return redirect(
eventreverse(self.request.event, 'presale:event.index')
)