Fix #543 -- Allow send mail from order (#550)

- Add send email directly for order
- Add email history (from mass and custom) to each specific order
This commit is contained in:
Daniel
2017-07-18 17:45:30 +08:00
committed by Raphael Michel
parent 921834c917
commit b90894c20f
15 changed files with 367 additions and 28 deletions

View File

@@ -614,6 +614,15 @@ class MailSettingsForm(SettingsForm):
help_text=_("Available placeholders: {event}, {code}, {url}"),
validators=[PlaceholderValidator(['{event}', '{code}', '{url}'])]
)
mail_text_order_custom_mail = I18nFormField(
label=_("Text"),
required=False,
widget=I18nTextarea,
help_text=_("Available placeholders: {expire_date}, {event}, {code}, {date}, {url}, "
"{invoice_name}, {invoice_company}"),
validators=[PlaceholderValidator(['{expire_date}', '{event}', '{code}', '{date}', '{url}',
'{invoice_name}', '{invoice_company}'])]
)
smtp_use_custom = forms.BooleanField(
label=_("Use custom SMTP server"),
help_text=_("All mail related to your event will be sent over the smtp server specified by you."),

View File

@@ -6,7 +6,7 @@ from django.utils.formats import localize
from django.utils.timezone import now
from django.utils.translation import pgettext_lazy, ugettext_lazy as _
from pretix.base.forms import I18nModelForm
from pretix.base.forms import I18nModelForm, PlaceholderValidator
from pretix.base.models import Item, ItemAddOn, Order, OrderPosition
from pretix.base.models.event import SubEvent
from pretix.base.services.pricing import get_price
@@ -215,3 +215,30 @@ class OrderLocaleForm(forms.ModelForm):
super().__init__(*args, **kwargs)
locale_names = dict(settings.LANGUAGES)
self.fields['locale'].choices = [(a, locale_names[a]) for a in self.instance.event.settings.locales]
class OrderMailForm(forms.Form):
subject = forms.CharField(
label=_("Subject"),
required=True
)
def __init__(self, *args, **kwargs):
order = kwargs.pop('order')
super().__init__(*args, **kwargs)
self.fields['sendto'] = forms.EmailField(
label=_("Recipient"),
required=True,
initial=order.email
)
self.fields['sendto'].widget.attrs['readonly'] = 'readonly'
self.fields['message'] = forms.CharField(
label=_("Message"),
required=True,
widget=forms.Textarea,
initial=order.event.settings.mail_text_order_custom_mail.localize(order.locale),
help_text=_("Available placeholders: {expire_date}, {event}, {code}, {date}, {url}, "
"{invoice_name}, {invoice_company}"),
validators=[PlaceholderValidator(['{expire_date}', '{event}', '{code}', '{date}', '{url}',
'{invoice_name}', '{invoice_company}'])]
)

View File

@@ -107,6 +107,7 @@ def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
'pretix.event.order.payment.changed': _('The payment method has been changed.'),
'pretix.event.order.expire_warning_sent': _('An email has been sent with a warning that the order is about '
'to expire.'),
'pretix.event.order.mail_sent': _('A custom email has been sent.'),
'pretix.user.settings.2fa.enabled': _('Two-factor authentication has been enabled.'),
'pretix.user.settings.2fa.disabled': _('Two-factor authentication has been disabled.'),
'pretix.user.settings.2fa.regenemergency': _('Your two-factor emergency codes have been regenerated.'),

View File

@@ -39,6 +39,9 @@
{% blocktrans asvar title_order_canceled %}Order canceled{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_canceled" title=title_order_canceled items="mail_text_order_canceled" %}
{% blocktrans asvar title_order_custom_mail %}Order custom mail{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="custom_mail" title=title_order_custom_mail items="mail_text_order_custom_mail" %}
</div>
</fieldset>
<fieldset>

View File

@@ -42,6 +42,9 @@
class="btn btn-default" target="_blank">
{% trans "View order as user" %}
</a>
<a href="{% url "control:event.order.mail_history" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default">
{% trans "View email history" %}
</a>
</div>
</div>
</form>
@@ -93,6 +96,9 @@
<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>
<a href="{% url "control:event.order.sendmail" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}" class="btn btn-default btn-xs">
<span class="fa fa-envelope-o"></span>
</a>
{% if order.status != "c" %}
<form class="form-inline helper-display-inline" method="post"
action="{% url "control:event.order.resendlink" event=request.event.slug organizer=request.event.organizer.slug code=order.code %}">

View File

@@ -0,0 +1,33 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Email history" %}{% endblock %}
{% block content %}
<h1>{% trans "Email history" %}</h1>
<div class="alert alert-info">
{% trans "Includes sent custom and mass emails history only." %}
</div>
<div>
<ul class="list-group">
{% for log in logs %}
<li class="list-group-item logentry">
<p class="meta">
<span class="fa fa-clock-o"></span> {{ log.datetime|date:"SHORT_DATETIME_FORMAT" }}
{% if log.user %}
<br/><span class="fa fa-user"></span> {{ log.user.get_full_name }}
{% endif %}
{% if log.display %}
<br/><span class="fa fa-comment-o"></span> {{ log.display }}
{% endif %}
</p>
<p>
<strong>{% trans "Subject:" %}
{{ log.parsed_data.subject }}</strong>
<pre>{{ log.parsed_data.message }}</pre>
</p>
</li>
{% endfor %}
</ul>
</div>
{% include "pretixcontrol/pagination.html" %}
{% endblock %}

View File

@@ -0,0 +1,37 @@
{% extends "pretixcontrol/event/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Send email" %}{% endblock %}
{% block content %}
<h1>{% trans "Send email" %}</h1>
{% block inner %}
<form class="form-horizontal" method="post" action="">
{% csrf_token %}
{% bootstrap_field form.sendto layout='horizontal' %}
{% bootstrap_field form.subject layout='horizontal' %}
{% bootstrap_field form.message layout='horizontal' %}
{% if request.method == "POST" %}
<fieldset>
<legend>{% trans "E-mail preview" %}</legend>
<div class="tab-pane mail-preview-group">
<pre lang="" class="mail-preview">
{% for segment in preview_output %}
{% spaceless %}
{{ segment|linebreaksbr }}
{% endspaceless %}
{% endfor %}
</pre>
</div>
</fieldset>
{% endif %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-default btn-save pull-left" name="action" value="preview">
{% trans "Preview email" %}
</button>
<button type="submit" class="btn btn-primary btn-save">
{% trans "Send" %}
</button>
</div>
</form>
{% endblock %}
{% endblock %}

View File

@@ -138,6 +138,10 @@ urlpatterns = [
name='event.order.comment'),
url(r'^orders/(?P<code>[0-9A-Z]+)/change$', orders.OrderChange.as_view(),
name='event.order.change'),
url(r'^orders/(?P<code>[0-9A-Z]+)/sendmail$', orders.OrderSendMail.as_view(),
name='event.order.sendmail'),
url(r'^orders/(?P<code>[0-9A-Z]+)/mail_history$', orders.OrderEmailHistory.as_view(),
name='event.order.mail_history'),
url(r'^orders/(?P<code>[0-9A-Z]+)/$', orders.OrderDetail.as_view(), name='event.order'),
url(r'^invoice/(?P<invoice>[^/]+)$', orders.InvoiceDownload.as_view(),
name='event.invoice.download'),

View File

@@ -443,6 +443,8 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
'mail_text_order_expire_warning': ['event', 'url', 'expire_date', 'invoice_name', 'invoice_company'],
'mail_text_waiting_list': ['event', 'url', 'product', 'hours', 'code'],
'mail_text_order_canceled': ['code', 'event', 'url'],
'mail_text_order_custom_mail': ['expire_date', 'event', 'code', 'date', 'url',
'invoice_name', 'invoice_company']
}
@cached_property

View File

@@ -1,20 +1,25 @@
from datetime import timedelta
import pytz
from django.conf import settings
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import Count
from django.db.models import Count, Q
from django.http import FileResponse, Http404, HttpResponseNotAllowed
from django.shortcuts import redirect, render
from django.utils.formats import date_format
from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from django.views.generic import DetailView, ListView, TemplateView, View
from django.views.generic import (
DetailView, FormView, ListView, TemplateView, View,
)
from i18nfield.strings import LazyI18nString
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, CachedTicket, Invoice, InvoiceAddress, Item, ItemVariation,
Order, Quota, generate_position_secret, generate_secret,
LogEntry, Order, Quota, generate_position_secret, generate_secret,
)
from pretix.base.models.event import SubEvent
from pretix.base.services.export import export
@@ -33,7 +38,7 @@ from pretix.base.views.async import AsyncAction
from pretix.control.forms.filter import EventOrderFilterForm
from pretix.control.forms.orders import (
CommentForm, ExporterForm, ExtendForm, OrderContactForm, OrderLocaleForm,
OrderPositionAddForm, OrderPositionChangeForm,
OrderMailForm, OrderPositionAddForm, OrderPositionChangeForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
from pretix.multidomain.urlreverse import build_absolute_uri
@@ -604,6 +609,114 @@ class OrderLocaleChange(OrderView):
return self.get(*args, **kwargs)
class OrderSendMail(EventPermissionRequiredMixin, FormView):
template_name = 'pretixcontrol/order/sendmail.html'
permission = 'can_change_orders'
form_class = OrderMailForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['order'] = Order.objects.get(
event=self.request.event,
code=self.kwargs['code'].upper()
)
return kwargs
def form_invalid(self, form):
messages.error(self.request, _('We could not send the email. See below for details.'))
return super().form_invalid(form)
def form_valid(self, form):
tz = pytz.timezone(self.request.event.settings.timezone)
order = Order.objects.get(
event=self.request.event,
code=self.kwargs['code'].upper()
)
self.preview_output = {}
try:
invoice_name = order.invoice_address.name
invoice_company = order.invoice_address.company
except InvoiceAddress.DoesNotExist:
invoice_name = ""
invoice_company = ""
with language(order.locale):
email_context = {
'event': order.event,
'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={
'order': order.code,
'secret': order.secret
}),
'invoice_name': invoice_name,
'invoice_company': invoice_company,
}
email_content = form.cleaned_data['message'].format_map(email_context)
if self.request.POST.get('action') == 'preview':
self.preview_output = []
self.preview_output.append(
_('Subject: {subject}').format(subject=form.cleaned_data['subject']))
self.preview_output.append(email_content)
return self.get(self.request, *self.args, **self.kwargs)
else:
try:
with language(order.locale):
email_template = LazyI18nString(form.cleaned_data['message'])
mail(
order.email, form.cleaned_data['subject'],
email_template, email_context,
self.request.event, locale=order.locale,
order=order
)
order.log_action(
'pretix.event.order.mail_sent',
user=self.request.user,
data={
'subject': form.cleaned_data['subject'],
'message': email_content,
'recipient': form.cleaned_data['sendto'],
}
)
messages.success(self.request, _('Your message has been queued and will be sent to {}.'.format(order.email)))
except SendMailException:
messages.error(
self.request,
_('Failed to send mail to the following user: {}'.format(order.email))
)
return super(OrderSendMail, self).form_valid(form)
def get_success_url(self):
return reverse('control:event.order', kwargs={
'event': self.request.event.slug,
'organizer': self.request.event.organizer.slug,
'code': self.kwargs['code']}
)
def get_context_data(self, *args, **kwargs):
ctx = super().get_context_data(*args, **kwargs)
ctx['preview_output'] = getattr(self, 'preview_output', None)
return ctx
class OrderEmailHistory(EventPermissionRequiredMixin, ListView):
template_name = 'pretixcontrol/order/mail_history.html'
permission = 'can_view_orders'
model = LogEntry
context_object_name = 'logs'
paginate_by = 5
def get_queryset(self):
order = Order.objects.filter(
event=self.request.event,
code=self.kwargs['code'].upper()
).first()
qs = order.all_logentries()
qs = qs.filter(Q(action_type="pretix.plugins.sendmail.order.email.sent") | Q(action_type="pretix.event.order.mail_sent"))
return qs
class OverView(EventPermissionRequiredMixin, TemplateView):
template_name = 'pretixcontrol/orders/overview.html'
permission = 'can_view_orders'