mirror of
https://github.com/pretix/pretix.git
synced 2026-04-28 00:02:37 +00:00
Compare commits
5 Commits
release/4.
...
sticky-add
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ef22375ec | ||
|
|
177906e2ac | ||
|
|
59f6b20129 | ||
|
|
51998e820d | ||
|
|
e803b56716 |
@@ -128,6 +128,10 @@ last_modified datetime Last modificati
|
||||
|
||||
The ``custom_followup_at`` attribute has been added.
|
||||
|
||||
.. versionchanged:: 4.4
|
||||
|
||||
The ``item`` and ``variation`` query parameters have been added.
|
||||
|
||||
|
||||
.. _order-position-resource:
|
||||
|
||||
@@ -415,6 +419,8 @@ List of all orders
|
||||
:query string code: Only return orders that match the given order code
|
||||
:query string status: Only return orders in the given order status (see above)
|
||||
:query string search: Only return orders matching a given search query
|
||||
:query integer item: Only return orders with a position that contains this item ID. *Warning:* Result will also include orders if they contain mixed items, and it will even return orders where the item is only contained in a canceled position.
|
||||
:query integer variation: Only return orders with a position that contains this variation ID. *Warning:* Result will also include orders if they contain mixed items and variations, and it will even return orders where the variation is only contained in a canceled position.
|
||||
:query boolean testmode: Only return orders with ``testmode`` set to ``true`` or ``false``
|
||||
:query boolean require_approval: If set to ``true`` or ``false``, only categories with this value for the field
|
||||
``require_approval`` will be returned.
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "4.3.1"
|
||||
__version__ = "4.4.0.dev0"
|
||||
|
||||
@@ -92,6 +92,8 @@ with scopes_disabled():
|
||||
subevent_after = django_filters.IsoDateTimeFilter(method='subevent_after_qs')
|
||||
subevent_before = django_filters.IsoDateTimeFilter(method='subevent_before_qs')
|
||||
search = django_filters.CharFilter(method='search_qs')
|
||||
item = django_filters.CharFilter(field_name='all_positions', lookup_expr='item_id')
|
||||
variation = django_filters.CharFilter(field_name='all_positions', lookup_expr='variation_id')
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
|
||||
@@ -539,6 +539,22 @@ def base_placeholders(sender, **kwargs):
|
||||
'voucher_list', ['voucher_list'], lambda voucher_list: ' \n'.join(voucher_list),
|
||||
' 68CYU2H6ZTP3WLK5\n 7MB94KKPVEPSMVF2'
|
||||
),
|
||||
SimpleFunctionalMailTextPlaceholder(
|
||||
# join vouchers with two spaces at end of line so markdown-parser inserts a <br>
|
||||
'voucher_url_list', ['event', 'voucher_list'],
|
||||
lambda event, voucher_list: ' \n'.join([
|
||||
build_absolute_uri(
|
||||
event, 'presale:event.redeem'
|
||||
) + '?voucher=' + c
|
||||
for c in voucher_list
|
||||
]),
|
||||
lambda event: ' \n'.join([
|
||||
build_absolute_uri(
|
||||
event, 'presale:event.redeem'
|
||||
) + '?voucher=' + c
|
||||
for c in ['68CYU2H6ZTP3WLK5', '7MB94KKPVEPSMVF2']
|
||||
]),
|
||||
),
|
||||
SimpleFunctionalMailTextPlaceholder(
|
||||
'url', ['event', 'voucher_list'], lambda event, voucher_list: build_absolute_uri(event, 'presale:event.index', kwargs={
|
||||
'event': event.slug,
|
||||
|
||||
@@ -300,6 +300,9 @@ class Invoice(models.Model):
|
||||
def __repr__(self):
|
||||
return '<Invoice {} / {}>'.format(self.full_invoice_no, self.pk)
|
||||
|
||||
def __str__(self):
|
||||
return self.full_invoice_no
|
||||
|
||||
|
||||
class InvoiceLine(models.Model):
|
||||
"""
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
{% extends "error.html" %}
|
||||
{% load i18n %}
|
||||
{% load rich_text %}
|
||||
{% load static %}
|
||||
{% block title %}{% trans "Redirect" %}{% endblock %}
|
||||
{% block content %}
|
||||
<i class="fa fa-link fa-fw big-icon"></i>
|
||||
<div class="error-details">
|
||||
<h1>{% trans "Redirect" %}</h1>
|
||||
<h3>
|
||||
{% blocktrans trimmed with host="<strong>"|add:hostname|add:"</strong>"|safe %}
|
||||
The link you clicked on wants to redirect you to a destination on the website {{ host }}.
|
||||
{% endblocktrans %}
|
||||
{% blocktrans trimmed %}
|
||||
Please only proceed if you trust this website to be safe.
|
||||
{% endblocktrans %}
|
||||
</h3>
|
||||
<p>
|
||||
<a href="{{ url }}" class="btn btn-primary btn-lg">
|
||||
{% blocktrans trimmed with host=hostname %}
|
||||
Proceed to {{ host }}
|
||||
{% endblocktrans %}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -24,21 +24,6 @@ import urllib.parse
|
||||
from django.core import signing
|
||||
from django.http import HttpResponseBadRequest, HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
def _is_samesite_referer(request):
|
||||
referer = request.META.get('HTTP_REFERER')
|
||||
if referer is None:
|
||||
return False
|
||||
|
||||
referer = urllib.parse.urlparse(referer)
|
||||
|
||||
# Make sure we have a valid URL for Referer.
|
||||
if '' in (referer.scheme, referer.netloc):
|
||||
return False
|
||||
|
||||
return (referer.scheme, referer.netloc) == (request.scheme, request.get_host())
|
||||
|
||||
|
||||
def redir_view(request):
|
||||
@@ -47,14 +32,6 @@ def redir_view(request):
|
||||
url = signer.unsign(request.GET.get('url', ''))
|
||||
except signing.BadSignature:
|
||||
return HttpResponseBadRequest('Invalid parameter')
|
||||
|
||||
if not _is_samesite_referer(request):
|
||||
u = urllib.parse.urlparse(url)
|
||||
return render(request, 'pretixbase/redirect.html', {
|
||||
'hostname': u.hostname,
|
||||
'url': url,
|
||||
})
|
||||
|
||||
r = HttpResponseRedirect(url)
|
||||
r['X-Robots-Tag'] = 'noindex'
|
||||
return r
|
||||
|
||||
@@ -57,7 +57,8 @@ from pretix.base.forms.widgets import (
|
||||
DatePickerWidget, SplitDateTimePickerWidget,
|
||||
)
|
||||
from pretix.base.models import (
|
||||
InvoiceAddress, ItemAddOn, Order, OrderFee, OrderPosition, TaxRule,
|
||||
Invoice, InvoiceAddress, ItemAddOn, Order, OrderFee, OrderPosition,
|
||||
TaxRule,
|
||||
)
|
||||
from pretix.base.models.event import SubEvent
|
||||
from pretix.base.services.pricing import get_price
|
||||
@@ -609,6 +610,17 @@ class OrderMailForm(forms.Form):
|
||||
label=_("Subject"),
|
||||
required=True
|
||||
)
|
||||
attach_tickets = forms.BooleanField(
|
||||
label=_("Attach tickets"),
|
||||
help_text=_("Will be ignored if all tickets in this order exceed a given size limit to ensure email deliverability."),
|
||||
required=False
|
||||
)
|
||||
attach_invoices = forms.ModelMultipleChoiceField(
|
||||
label=_("Attach invoices"),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
required=False,
|
||||
queryset=Invoice.objects.none()
|
||||
)
|
||||
|
||||
def _set_field_placeholders(self, fn, base_parameters):
|
||||
phs = [
|
||||
@@ -641,6 +653,7 @@ class OrderMailForm(forms.Form):
|
||||
widget=forms.Textarea,
|
||||
initial=order.event.settings.mail_text_order_custom_mail.localize(order.locale),
|
||||
)
|
||||
self.fields['attach_invoices'].queryset = order.invoices.all()
|
||||
self._set_field_placeholders('message', ['event', 'order'])
|
||||
|
||||
|
||||
@@ -648,6 +661,7 @@ class OrderPositionMailForm(OrderMailForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
position = self.position = kwargs.pop('position')
|
||||
super().__init__(*args, **kwargs)
|
||||
del self.fields['attach_invoices']
|
||||
self.fields['sendto'].initial = position.attendee_email
|
||||
self.fields['message'] = forms.CharField(
|
||||
label=_("Message"),
|
||||
|
||||
@@ -74,17 +74,17 @@
|
||||
{{ c.datetime|date:"SHORT_DATETIME_FORMAT" }}
|
||||
{% if c.type == "exit" %}
|
||||
{% if c.auto_checked_in %}
|
||||
<span class="fa fa-fw fa-hourglass-end" data-toggle="tooltip"
|
||||
<span class="fa fa-fw fa-hourglass-end" data-toggle="tooltip_html"
|
||||
title="{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically marked not present: {{ date }}{% endblocktrans %}"></span>
|
||||
{% endif %}
|
||||
{% elif c.forced and c.successful %}
|
||||
<span class="fa fa-fw fa-warning" data-toggle="tooltip"
|
||||
<span class="fa fa-fw fa-warning" data-toggle="tooltip_html"
|
||||
title="{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Additional entry scan: {{ date }}{% endblocktrans %}"></span>
|
||||
{% elif c.forced and not c.successful %}
|
||||
<br>
|
||||
<small class="text-muted">{% trans "Failed in offline mode" %}</small>
|
||||
{% elif c.auto_checked_in %}
|
||||
<span class="fa fa-fw fa-magic" data-toggle="tooltip"
|
||||
<span class="fa fa-fw fa-magic" data-toggle="tooltip_html"
|
||||
title="{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically checked in: {{ date }}{% endblocktrans %}"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
<div class="quotabox availability" data-toggle="tooltip_html" data-placement="top"
|
||||
title="{% trans "Quota:" %} {{ q.name|force_escape|force_escape }}<br>{% blocktrans with date=q.cached_availability_time|date:"SHORT_DATETIME_FORMAT" %}Numbers as of {{ date }}{% endblocktrans %}">
|
||||
title="{% trans "Quota:" %} {{ q.name }}<br>{% blocktrans with date=q.cached_availability_time|date:"SHORT_DATETIME_FORMAT" %}Numbers as of {{ date }}{% endblocktrans %}">
|
||||
{% if q.size|default_if_none:"NONE" == "NONE" %}
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-success progress-bar-100">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% load i18n %}
|
||||
<a class="quotabox" data-toggle="tooltip_html" data-placement="top"
|
||||
title="{% trans "Quota:" %} {{ q.name|force_escape|force_escape }}{% if q.cached_avail.1 is not None %}<br>{% blocktrans with num=q.cached_avail.1 %}Currently available: {{ num }}{% endblocktrans %}{% endif %}"
|
||||
title="{% trans "Quota:" %} {{ q.name }}{% if q.cached_avail.1 is not None %}<br>{% blocktrans with num=q.cached_avail.1 %}Currently available: {{ num }}{% endblocktrans %}{% endif %}"
|
||||
href="{% url "control:event.items.quotas.show" event=q.event.slug organizer=q.event.organizer.slug quota=q.pk %}">
|
||||
{% if q.size|default_if_none:"NONE" == "NONE" %}
|
||||
<div class="progress">
|
||||
|
||||
@@ -328,19 +328,19 @@
|
||||
{% if line.checkins.all %}
|
||||
{% for c in line.all_checkins.all %}
|
||||
{% if not c.successful %}
|
||||
<span class="fa fa-fw fa-exclamation-circle text-danger" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Denied scan: {{ date }}{% endblocktrans %}<br>{{ c.get_error_reason_display }}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
<span class="fa fa-fw fa-exclamation-circle text-danger" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Denied scan: {{ date }}{% endblocktrans %}<br>{{ c.get_error_reason_display }}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% elif c.type == "exit" %}
|
||||
{% if c.auto_checked_in %}
|
||||
<span class="fa fa-fw text-success fa-hourglass-end" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically marked not present: {{ date }}{% endblocktrans %}"></span>
|
||||
<span class="fa fa-fw text-success fa-hourglass-end" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically marked not present: {{ date }}{% endblocktrans %}"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-fw text-success fa-sign-out" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Exit scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
<span class="fa fa-fw text-success fa-sign-out" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Exit scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% endif %}
|
||||
{% elif c.forced %}
|
||||
<span class="fa fa-fw fa-warning text-warning" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Additional entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
<span class="fa fa-fw fa-warning text-warning" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Additional entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% elif c.auto_checked_in %}
|
||||
<span class="fa fa-fw fa-magic text-success" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically checked in: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
<span class="fa fa-fw fa-magic text-success" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Automatically checked in: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% else %}
|
||||
<span class="fa fa-fw fa-check text-success" data-toggle="tooltip_html" title="{{ c.list.name|force_escape|force_escape }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
<span class="fa fa-fw fa-check text-success" data-toggle="tooltip_html" title="{{ c.list.name }}<br>{% blocktrans trimmed with date=c.datetime|date:'SHORT_DATETIME_FORMAT' %}Entry scan: {{ date }}{% endblocktrans %}{% if c.gate %}<br>{{ c.gate }}{% endif %}"></span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
{% bootstrap_field form.sendto layout='horizontal' %}
|
||||
{% bootstrap_field form.subject layout='horizontal' %}
|
||||
{% bootstrap_field form.message layout='horizontal' %}
|
||||
{% bootstrap_field form.attach_tickets layout='horizontal' %}
|
||||
{% if form.attach_invoices %}
|
||||
{% bootstrap_field form.attach_invoices layout='horizontal' %}
|
||||
{% endif %}
|
||||
{% if request.method == "POST" %}
|
||||
<fieldset>
|
||||
<legend>{% trans "E-mail preview" %}</legend>
|
||||
|
||||
@@ -1982,7 +1982,9 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
|
||||
order.send_mail(
|
||||
form.cleaned_data['subject'], email_template,
|
||||
email_context, 'pretix.event.order.email.custom_sent',
|
||||
self.request.user, auto_email=False
|
||||
self.request.user, auto_email=False,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
invoices=form.cleaned_data.get('attach_invoices', []),
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(order.email)))
|
||||
@@ -2047,7 +2049,8 @@ class OrderPositionSendMail(OrderSendMail):
|
||||
email_template,
|
||||
email_context,
|
||||
'pretix.event.order.position.email.custom_sent',
|
||||
self.request.user
|
||||
self.request.user,
|
||||
attach_tickets=form.cleaned_data.get('attach_tickets', False),
|
||||
)
|
||||
messages.success(self.request,
|
||||
_('Your message has been queued and will be sent to {}.'.format(position.attendee_email)))
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="form-horizontal stripe-container">
|
||||
{% if is_moto %}
|
||||
<h1>
|
||||
<span class="label label-info pull-right flip" data-toggle="tooltip" title="{% trans "This transaction will be marked as Mail Order/Telephone Order, exempting it from Strong Customer Authentication (SCA) whenever possible" %}">MOTO</span>
|
||||
<span class="label label-info pull-right flip" data-toggle="tooltip_html" title="{% trans "This transaction will be marked as Mail Order/Telephone Order, exempting it from Strong Customer Authentication (SCA) whenever possible" %}">MOTO</span>
|
||||
</h1>
|
||||
<div class="clearfix"></div>
|
||||
{% endif %}
|
||||
|
||||
@@ -675,21 +675,7 @@ $(function () {
|
||||
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$('[data-toggle="tooltip_html"]').tooltip({
|
||||
'html': true,
|
||||
'whiteList': {
|
||||
// Global attributes allowed on any supplied element below.
|
||||
'*': ['class', 'dir', 'id', 'lang', 'role'],
|
||||
b: [],
|
||||
br: [],
|
||||
code: [],
|
||||
div: [], // required for template
|
||||
h3: ['class', 'role'], // required for template
|
||||
i: [],
|
||||
small: [],
|
||||
span: [],
|
||||
strong: [],
|
||||
u: [],
|
||||
}
|
||||
'html': true
|
||||
});
|
||||
|
||||
var url = document.location.toString();
|
||||
|
||||
@@ -172,6 +172,12 @@ article.item-with-variations:last-child, .product-row:last-child {
|
||||
}
|
||||
section.front-page {
|
||||
margin-top: 30px;
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
top: 100px;
|
||||
padding: 10px 0;
|
||||
background: rgba(255,255,255,.8);
|
||||
z-index: 10;
|
||||
}
|
||||
.offline-banner {
|
||||
background: $brand-danger;
|
||||
|
||||
Reference in New Issue
Block a user