Use tabs for all long settings and CRUD forms (#1352)

* First tabs

* Convert more pages

* Convert question page

* Item form

* Add item_formsets signal

* Revert "Add new signal nav_item"

This reverts commit 1ce613ff89.

* Formset is a word!
This commit is contained in:
Raphael Michel
2019-07-29 09:35:00 +02:00
committed by GitHub
parent 609f0b632c
commit c1d89284a4
41 changed files with 1526 additions and 1700 deletions

View File

@@ -325,7 +325,7 @@ class EventSettingsForm(SettingsForm):
)
timezone = forms.ChoiceField(
choices=((a, a) for a in common_timezones),
label=_("Default timezone"),
label=_("Event timezone"),
)
locales = forms.MultipleChoiceField(
choices=settings.LANGUAGES,
@@ -441,6 +441,96 @@ class EventSettingsForm(SettingsForm):
required=False,
help_text=_("We'll show this publicly to allow attendees to contact you.")
)
show_variations_expanded = forms.BooleanField(
label=_("Show variations of a product expanded by default"),
required=False
)
hide_sold_out = forms.BooleanField(
label=_("Hide all products that are sold out"),
required=False
)
meta_noindex = forms.BooleanField(
label=_('Ask search engines not to index the ticket shop'),
required=False
)
redirect_to_checkout_directly = forms.BooleanField(
label=_('Directly redirect to check-out after a product has been added to the cart.'),
required=False
)
frontpage_subevent_ordering = forms.ChoiceField(
label=pgettext('subevent', 'Date ordering'),
choices=[
('date_ascending', _('Event start time')),
('date_descending', _('Event start time (descending)')),
('name_ascending', _('Name')),
('name_descending', _('Name (descending)')),
], # When adding a new ordering, remember to also define it in the event model
)
logo_image = ExtFileField(
label=_('Logo image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
required=False,
help_text=_('If you provide a logo image, we will by default not show your events name and date '
'in the page header. We will show your logo with a maximal height of 120 pixels.')
)
frontpage_text = I18nFormField(
label=_("Frontpage text"),
required=False,
widget=I18nTextarea
)
presale_has_ended_text = I18nFormField(
label=_("End of presale text"),
required=False,
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown above the ticket shop once the designated sales timeframe for this event "
"is over. You can use it to describe other options to get a ticket, such as a box office.")
)
voucher_explanation_text = I18nFormField(
label=_("Voucher explanation"),
required=False,
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown next to the input for a voucher code. You can use it e.g. to explain "
"how to obtain a voucher code.")
)
primary_color = forms.CharField(
label=_("Primary color"),
required=False,
validators=[
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
],
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
)
theme_color_success = forms.CharField(
label=_("Accent color for success"),
help_text=_("We strongly suggest to use a shade of green."),
required=False,
validators=[
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
],
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
)
theme_color_danger = forms.CharField(
label=_("Accent color for errors"),
help_text=_("We strongly suggest to use a dark shade of red."),
required=False,
validators=[
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
],
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
)
primary_font = forms.ChoiceField(
label=_('Font'),
choices=[
('Open Sans', 'Open Sans')
],
widget=FontSelect,
help_text=_('Only respected by modern browsers.')
)
def clean(self):
data = super().clean()
@@ -459,6 +549,7 @@ class EventSettingsForm(SettingsForm):
return data
def __init__(self, *args, **kwargs):
event = kwargs['obj']
super().__init__(*args, **kwargs)
self.fields['confirm_text'].widget.attrs['rows'] = '3'
self.fields['confirm_text'].widget.attrs['placeholder'] = _(
@@ -479,6 +570,11 @@ class EventSettingsForm(SettingsForm):
))
for k, v in PERSON_NAME_TITLE_GROUPS.items()
]
if not event.has_subevents:
del self.fields['frontpage_subevent_ordering']
self.fields['primary_font'].choices += [
(a, a) for a in get_fonts()
]
class CancelSettingsForm(SettingsForm):
@@ -1137,108 +1233,6 @@ class MailSettingsForm(SettingsForm):
raise ValidationError(_('You can activate either SSL or STARTTLS security, but not both at the same time.'))
class DisplaySettingsForm(SettingsForm):
primary_color = forms.CharField(
label=_("Primary color"),
required=False,
validators=[
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
],
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
)
theme_color_success = forms.CharField(
label=_("Accent color for success"),
help_text=_("We strongly suggest to use a shade of green."),
required=False,
validators=[
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
],
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
)
theme_color_danger = forms.CharField(
label=_("Accent color for errors"),
help_text=_("We strongly suggest to use a dark shade of red."),
required=False,
validators=[
RegexValidator(regex='^#[0-9a-fA-F]{6}$',
message=_('Please enter the hexadecimal code of a color, e.g. #990000.')),
],
widget=forms.TextInput(attrs={'class': 'colorpickerfield'})
)
logo_image = ExtFileField(
label=_('Logo image'),
ext_whitelist=(".png", ".jpg", ".gif", ".jpeg"),
required=False,
help_text=_('If you provide a logo image, we will by default not show your events name and date '
'in the page header. We will show your logo with a maximal height of 120 pixels.')
)
primary_font = forms.ChoiceField(
label=_('Font'),
choices=[
('Open Sans', 'Open Sans')
],
widget=FontSelect,
help_text=_('Only respected by modern browsers.')
)
frontpage_text = I18nFormField(
label=_("Frontpage text"),
required=False,
widget=I18nTextarea
)
presale_has_ended_text = I18nFormField(
label=_("End of presale text"),
required=False,
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown above the ticket shop once the designated sales timeframe for this event "
"is over. You can use it to describe other options to get a ticket, such as a box office.")
)
voucher_explanation_text = I18nFormField(
label=_("Voucher explanation"),
required=False,
widget=I18nTextarea,
widget_kwargs={'attrs': {'rows': '2'}},
help_text=_("This text will be shown next to the input for a voucher code. You can use it e.g. to explain "
"how to obtain a voucher code.")
)
show_variations_expanded = forms.BooleanField(
label=_("Show variations of a product expanded by default"),
required=False
)
hide_sold_out = forms.BooleanField(
label=_("Hide all products that are sold out"),
required=False
)
frontpage_subevent_ordering = forms.ChoiceField(
label=pgettext('subevent', 'Date ordering'),
choices=[
('date_ascending', _('Event start time')),
('date_descending', _('Event start time (descending)')),
('name_ascending', _('Name')),
('name_descending', _('Name (descending)')),
], # When adding a new ordering, remember to also define it in the event model
)
meta_noindex = forms.BooleanField(
label=_('Ask search engines not to index the ticket shop'),
required=False
)
redirect_to_checkout_directly = forms.BooleanField(
label=_('Directly redirect to check-out after a product has been added to the cart.'),
required=False
)
def __init__(self, *args, **kwargs):
event = kwargs['obj']
super().__init__(*args, **kwargs)
self.fields['primary_font'].choices += [
(a, a) for a in get_fonts()
]
if not event.has_subevents:
del self.fields['frontpage_subevent_ordering']
class TicketSettingsForm(SettingsForm):
ticket_download = forms.BooleanField(
label=_("Use feature"),

View File

@@ -460,6 +460,9 @@ class ItemUpdateForm(I18nModelForm):
class ItemVariationsFormSet(I18nFormSet):
template = "pretixcontrol/item/include_variations.html"
title = _('Variations')
def clean(self):
super().clean()
for f in self.forms:
@@ -516,6 +519,9 @@ class ItemVariationForm(I18nModelForm):
class ItemAddOnsFormSet(I18nFormSet):
title = _('Add-ons')
template = "pretixcontrol/item/include_addons.html"
def __init__(self, *args, **kwargs):
self.event = kwargs.get('event')
super().__init__(*args, **kwargs)
@@ -581,6 +587,9 @@ class ItemAddOnForm(I18nModelForm):
class ItemBundleFormSet(I18nFormSet):
template = "pretixcontrol/item/include_bundles.html"
title = _('Bundled products')
def __init__(self, *args, **kwargs):
self.event = kwargs.get('event')
self.item = kwargs.pop('item')

View File

@@ -201,9 +201,6 @@ class OrganizerSettingsForm(SettingsForm):
widget=I18nTextarea,
help_text=_('Not displayed anywhere by default, but if you want to, you can use this e.g. in ticket templates.')
)
class OrganizerDisplaySettingsForm(SettingsForm):
primary_color = forms.CharField(
label=_("Primary color"),
required=False,

View File

@@ -48,14 +48,6 @@ def get_event_navigation(request: HttpRequest):
}),
'active': url.url_name == 'event.settings.plugins',
},
{
'label': _('Display'),
'url': reverse('control:event.settings.display', kwargs={
'event': request.event.slug,
'organizer': request.event.organizer.slug,
}),
'active': url.url_name == 'event.settings.display',
},
{
'label': _('Tickets'),
'url': reverse('control:event.settings.tickets', kwargs={
@@ -78,7 +70,7 @@ def get_event_navigation(request: HttpRequest):
'event': request.event.slug,
'organizer': request.event.organizer.slug,
}),
'active': url.url_name == 'event.settings.tax',
'active': url.url_name.startswith('event.settings.tax'),
},
{
'label': _('Invoicing'),
@@ -425,13 +417,6 @@ def get_organizer_navigation(request):
}),
'active': url.url_name == 'organizer.edit',
},
{
'label': _('Display'),
'url': reverse('control:organizer.display', kwargs={
'organizer': request.organizer.slug
}),
'active': url.url_name == 'organizer.display',
},
]
})
if 'can_change_teams' in request.orgapermset:

View File

@@ -237,24 +237,6 @@ As with all plugin signals, the ``sender`` keyword argument will contain the eve
A second keyword argument ``request`` will contain the request object.
"""
nav_item = EventPluginSignal(
providing_args=['request', 'item']
)
"""
This signal is sent out to include tab links on the settings page of an item.
Receivers are expected to return a list of dictionaries. The dictionaries
should contain at least the keys ``label`` and ``url``. You should also return
an ``active`` key with a boolean set to ``True``, when this item should be marked
as active.
If your linked view should stay in the tab-like context of this page, we recommend
that you use ``pretix.control.views.item.ItemDetailMixin`` for your view
and your template inherits from ``pretixcontrol/item/base.html``.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
A second keyword argument ``request`` will contain the request object.
"""
event_settings_widget = EventPluginSignal(
providing_args=['request']
)
@@ -279,6 +261,24 @@ styles. It is advisable to set a prefix for your form to avoid clashes with othe
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
item_formsets = EventPluginSignal(
providing_args=['request', 'item']
)
"""
This signal allows you to return additional formsets that should be rendered on the product
modification page. You are passed ``request`` and ``item`` arguments and are expected to return
an instance of a formset class that you bind yourself when appropriate. Your formset will be
executed as part of the standard validation and rendering cycle and rendered using default
bootstrap styles. It is advisable to set a prefix for your formset to avoid clashes with other
plugins.
Your formset needs to have two special properties: ``template`` with a template that will be
included to render the formset and ``title`` that will be used as a headline. Your template
will be passed a ``formset`` variable with your formset.
As with all plugin signals, the ``sender`` keyword argument will contain the event.
"""
subevent_forms = EventPluginSignal(
providing_args=['request', 'subevent']
)

View File

@@ -47,6 +47,7 @@
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/orderchange.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/typeahead.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/quicksetup.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/tabs.js" %}"></script>
<script type="text/javascript" src="{% static "pretixbase/js/details.js" %}"></script>
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
<script type="text/javascript" src="{% static "colorpicker/bootstrap-colorpicker.js" %}"></script>

View File

@@ -6,31 +6,33 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "Cancellation of unpaid or free orders" %}</legend>
{% bootstrap_field form.cancel_allow_user layout="control" %}
{% bootstrap_field form.cancel_allow_user_until layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Cancellation of paid orders" %}</legend>
{% bootstrap_field form.cancel_allow_user_paid layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_keep layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_keep_percentage layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_keep_fees layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_until layout="control" %}
{% if not gets_notification %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
If a user requests cancels a paid order and the money can not be refunded automatically, e.g.
due to the selected payment method, you will need to take manual action. However, you have
currently turned off notifications for this event.
{% endblocktrans %}
<a href="{% url "control:user.settings.notifications" %}" class="btn btn-default">
{% trans "Change notification settings" %}
</a>
</div>
{% endif %}
</fieldset>
<div class="tabbed-form">
<fieldset>
<legend>{% trans "Unpaid or free orders" %}</legend>
{% bootstrap_field form.cancel_allow_user layout="control" %}
{% bootstrap_field form.cancel_allow_user_until layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Paid orders" %}</legend>
{% bootstrap_field form.cancel_allow_user_paid layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_keep layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_keep_percentage layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_keep_fees layout="control" %}
{% bootstrap_field form.cancel_allow_user_paid_until layout="control" %}
{% if not gets_notification %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
If a user requests cancels a paid order and the money can not be refunded automatically, e.g.
due to the selected payment method, you will need to take manual action. However, you have
currently turned off notifications for this event.
{% endblocktrans %}
<a href="{% url "control:user.settings.notifications" %}" class="btn btn-default">
{% trans "Change notification settings" %}
</a>
</div>
{% endif %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -1,44 +0,0 @@
{% extends "pretixcontrol/event/settings_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load hierarkey_form %}
{% block custom_header %}
{{ block.super }}
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
{% endblock %}
{% block inside %}
<h1>{% trans "Display settings" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "Event page" %}</legend>
{% bootstrap_field form.logo_image layout="control" %}
{% bootstrap_field form.frontpage_text layout="control" %}
{% bootstrap_field form.presale_has_ended_text layout="control" %}
{% bootstrap_field form.voucher_explanation_text layout="control" %}
{% bootstrap_field form.show_variations_expanded layout="control" %}
{% bootstrap_field form.hide_sold_out layout="control" %}
{% bootstrap_field form.meta_noindex layout="control" %}
{% if form.frontpage_subevent_ordering %}
{% bootstrap_field form.frontpage_subevent_ordering layout="control" %}
{% endif %}
{% bootstrap_field form.redirect_to_checkout_directly layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Shop design" %}</legend>
{% url "control:organizer.display" organizer=request.organizer.slug as org_url %}
{% propagated request.event org_url "primary_color" "primary_font" "theme_color_success" "theme_color_danger" %}
{% bootstrap_field form.primary_color layout="control" %}
{% bootstrap_field form.theme_color_success layout="control" %}
{% bootstrap_field form.theme_color_danger layout="control" %}
{% bootstrap_field form.primary_font layout="control" %}
{% endpropagated %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -4,48 +4,50 @@
{% block inside %}
<h1>{% trans "Invoice settings" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "General settings" %}</legend>
{% bootstrap_field form.invoice_generate layout="control" %}
{% bootstrap_field form.invoice_generate_sales_channels layout="control" %}
{% bootstrap_field form.invoice_email_attachment layout="control" %}
{% bootstrap_field form.invoice_numbers_prefix layout="control" %}
{% bootstrap_field form.invoice_numbers_consecutive layout="control" %}
{% bootstrap_field form.invoice_language layout="control" %}
{% bootstrap_field form.invoice_include_free layout="control" %}
{% bootstrap_field form.invoice_attendee_name layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Invoice address form" %}</legend>
{% bootstrap_field form.invoice_address_asked layout="control" %}
{% bootstrap_field form.invoice_address_required layout="control" %}
{% bootstrap_field form.invoice_name_required layout="control" %}
{% bootstrap_field form.invoice_address_company_required layout="control" %}
{% bootstrap_field form.invoice_address_vatid layout="control" %}
{% bootstrap_field form.invoice_address_beneficiary layout="control" %}
{% bootstrap_field form.invoice_address_not_asked_free layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Your invoice details" %}</legend>
{% bootstrap_field form.invoice_address_from_name layout="control" %}
{% bootstrap_field form.invoice_address_from layout="control" %}
{% bootstrap_field form.invoice_address_from_zipcode layout="control" %}
{% bootstrap_field form.invoice_address_from_city layout="control" %}
{% bootstrap_field form.invoice_address_from_country layout="control" %}
{% bootstrap_field form.invoice_address_from_tax_id layout="control" %}
{% bootstrap_field form.invoice_address_from_vat_id layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Invoice customization" %}</legend>
{% bootstrap_field form.invoice_renderer layout="control" %}
{% bootstrap_field form.invoice_introductory_text layout="control" %}
{% bootstrap_field form.invoice_additional_text layout="control" %}
{% bootstrap_field form.invoice_footer_text layout="control" %}
{% bootstrap_field form.invoice_logo_image layout="control" %}
{% bootstrap_field form.invoice_address_explanation_text layout="control" %}
</fieldset>
<div class="tabbed-form">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "Invoice generation" %}</legend>
{% bootstrap_field form.invoice_generate layout="control" %}
{% bootstrap_field form.invoice_generate_sales_channels layout="control" %}
{% bootstrap_field form.invoice_email_attachment layout="control" %}
{% bootstrap_field form.invoice_numbers_prefix layout="control" %}
{% bootstrap_field form.invoice_numbers_consecutive layout="control" %}
{% bootstrap_field form.invoice_language layout="control" %}
{% bootstrap_field form.invoice_include_free layout="control" %}
{% bootstrap_field form.invoice_attendee_name layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Address form" %}</legend>
{% bootstrap_field form.invoice_address_asked layout="control" %}
{% bootstrap_field form.invoice_address_required layout="control" %}
{% bootstrap_field form.invoice_name_required layout="control" %}
{% bootstrap_field form.invoice_address_company_required layout="control" %}
{% bootstrap_field form.invoice_address_vatid layout="control" %}
{% bootstrap_field form.invoice_address_beneficiary layout="control" %}
{% bootstrap_field form.invoice_address_not_asked_free layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Issuer details" %}</legend>
{% bootstrap_field form.invoice_address_from_name layout="control" %}
{% bootstrap_field form.invoice_address_from layout="control" %}
{% bootstrap_field form.invoice_address_from_zipcode layout="control" %}
{% bootstrap_field form.invoice_address_from_city layout="control" %}
{% bootstrap_field form.invoice_address_from_country layout="control" %}
{% bootstrap_field form.invoice_address_from_tax_id layout="control" %}
{% bootstrap_field form.invoice_address_from_vat_id layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Invoice customization" %}</legend>
{% bootstrap_field form.invoice_renderer layout="control" %}
{% bootstrap_field form.invoice_introductory_text layout="control" %}
{% bootstrap_field form.invoice_additional_text layout="control" %}
{% bootstrap_field form.invoice_footer_text layout="control" %}
{% bootstrap_field form.invoice_logo_image layout="control" %}
{% bootstrap_field form.invoice_address_explanation_text layout="control" %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-default btn-lg" name="preview" value="preview" formtarget="_blank">
{% trans "Save and show preview" %}

View File

@@ -5,85 +5,87 @@
{% block inside %}
<h1>{% trans "E-mail settings" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data"
mail-preview-url="{% url "control:event.settings.mail.preview" event=request.event.slug organizer=request.event.organizer.slug %}">
mail-preview-url="{% url "control:event.settings.mail.preview" event=request.event.slug organizer=request.event.organizer.slug %}">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "General settings" %}</legend>
{% bootstrap_field form.mail_prefix layout="control" %}
{% bootstrap_field form.mail_from layout="control" %}
{% bootstrap_field form.mail_from_name layout="control" %}
{% bootstrap_field form.mail_text_signature layout="control" %}
{% bootstrap_field form.mail_bcc layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "E-mail design" %}</legend>
<div class="row">
{% for r in renderers.values %}
<div class="col-md-3">
<div class="well maildesignpreview text-center">
<label class="radio">
<input type="radio" name="mail_html_renderer" value="{{ r.identifier }}"
{% if request.event.settings.mail_html_renderer == r.identifier %}checked{% endif %}>
{{ r.verbose_name }}
</label>
<img src="{% static r.thumbnail_filename %}">
<a class="btn btn-default btn-sm" target="_blank"
href="{% url "control:event.settings.mail.preview.layout" event=request.event.slug organizer=request.event.organizer.slug %}?renderer={{ r.identifier }}">
{% trans "Preview" %}
</a>
<div class="tabbed-form">
<fieldset>
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.mail_prefix layout="control" %}
{% bootstrap_field form.mail_from layout="control" %}
{% bootstrap_field form.mail_from_name layout="control" %}
{% bootstrap_field form.mail_text_signature layout="control" %}
{% bootstrap_field form.mail_bcc layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "E-mail design" %}</legend>
<div class="row">
{% for r in renderers.values %}
<div class="col-md-3">
<div class="well maildesignpreview text-center">
<label class="radio">
<input type="radio" name="mail_html_renderer" value="{{ r.identifier }}"
{% if request.event.settings.mail_html_renderer == r.identifier %}checked{% endif %}>
{{ r.verbose_name }}
</label>
<img src="{% static r.thumbnail_filename %}">
<a class="btn btn-default btn-sm" target="_blank"
href="{% url "control:event.settings.mail.preview.layout" event=request.event.slug organizer=request.event.organizer.slug %}?renderer={{ r.identifier }}">
{% trans "Preview" %}
</a>
</div>
</div>
</div>
{% endfor %}
</div>
</fieldset>
<fieldset>
<legend>{% trans "E-mail content" %}</legend>
<div class="panel-group" id="questions_group">
{% blocktrans asvar title_placed_order %}Placed order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_placed" title=title_placed_order items="mail_text_order_placed,mail_send_order_placed_attendee,mail_text_order_placed_attendee" exclude="mail_send_order_placed_attendee" %}
{% endfor %}
</div>
</fieldset>
<fieldset>
<legend>{% trans "E-mail content" %}</legend>
<div class="panel-group" id="questions_group">
{% blocktrans asvar title_placed_order %}Placed order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_placed" title=title_placed_order items="mail_text_order_placed,mail_send_order_placed_attendee,mail_text_order_placed_attendee" exclude="mail_send_order_placed_attendee" %}
{% blocktrans asvar title_paid_order %}Paid order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_paid" title=title_paid_order items="mail_text_order_paid,mail_send_order_paid_attendee,mail_text_order_paid_attendee" exclude="mail_send_order_paid_attendee" %}
{% blocktrans asvar title_paid_order %}Paid order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_paid" title=title_paid_order items="mail_text_order_paid,mail_send_order_paid_attendee,mail_text_order_paid_attendee" exclude="mail_send_order_paid_attendee" %}
{% blocktrans asvar title_free_order %}Free order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_free" title=title_free_order items="mail_text_order_free,mail_send_order_free_attendee,mail_text_order_free_attendee" exclude="mail_send_order_free_attendee" %}
{% blocktrans asvar title_free_order %}Free order{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_free" title=title_free_order items="mail_text_order_free,mail_send_order_free_attendee,mail_text_order_free_attendee" exclude="mail_send_order_free_attendee" %}
{% blocktrans asvar title_resend_link %}Resend link{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="resend_link" title=title_resend_link items="mail_text_resend_link,mail_text_resend_all_links" %}
{% blocktrans asvar title_resend_link %}Resend link{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="resend_link" title=title_resend_link items="mail_text_resend_link,mail_text_resend_all_links" %}
{% blocktrans asvar title_order_changed %}Order changed{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_changed" title=title_order_changed items="mail_text_order_changed" %}
{% blocktrans asvar title_order_changed %}Order changed{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_changed" title=title_order_changed items="mail_text_order_changed" %}
{% blocktrans asvar title_payment_reminder %}Payment reminder{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_expirew" title=title_payment_reminder items="mail_days_order_expire_warning,mail_text_order_expire_warning" exclude="mail_days_order_expire_warning" %}
{% blocktrans asvar title_payment_reminder %}Payment reminder{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="order_expirew" title=title_payment_reminder items="mail_days_order_expire_warning,mail_text_order_expire_warning" exclude="mail_days_order_expire_warning" %}
{% blocktrans asvar title_waiting_list_notification %}Waiting list notification{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="waiting_list" title=title_waiting_list_notification items="mail_text_waiting_list" %}
{% blocktrans asvar title_waiting_list_notification %}Waiting list notification{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="waiting_list" title=title_waiting_list_notification items="mail_text_waiting_list" %}
{% 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_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" %}
{% 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" %}
{% blocktrans asvar title_download_tickets_reminder %}Reminder to download tickets{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_download_tickets_reminder items="mail_days_download_reminder,mail_text_download_reminder,mail_send_download_reminder_attendee,mail_text_download_reminder_attendee" exclude="mail_days_download_reminder,mail_send_download_reminder_attendee" %}
{% blocktrans asvar title_download_tickets_reminder %}Reminder to download tickets{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_download_tickets_reminder items="mail_days_download_reminder,mail_text_download_reminder,mail_send_download_reminder_attendee,mail_text_download_reminder_attendee" exclude="mail_days_download_reminder,mail_send_download_reminder_attendee" %}
{% blocktrans asvar title_require_approval %}Order approval process{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_require_approval items="mail_text_order_placed_require_approval,mail_text_order_approved,mail_text_order_denied" %}
</div>
</fieldset>
<fieldset>
<legend>{% trans "SMTP settings" %}</legend>
{% bootstrap_field form.smtp_use_custom layout="control" %}
{% bootstrap_field form.smtp_host layout="control" %}
{% bootstrap_field form.smtp_port layout="control" %}
{% bootstrap_field form.smtp_username layout="control" %}
{% bootstrap_field form.smtp_password layout="control" %}
{% bootstrap_field form.smtp_use_tls layout="control" %}
{% bootstrap_field form.smtp_use_ssl layout="control" %}
</fieldset>
{% blocktrans asvar title_require_approval %}Order approval process{% endblocktrans %}
{% include "pretixcontrol/event/mail_settings_fragment.html" with pid="ticket_reminder" title=title_require_approval items="mail_text_order_placed_require_approval,mail_text_order_approved,mail_text_order_denied" %}
</div>
</fieldset>
<fieldset>
<legend>{% trans "SMTP settings" %}</legend>
{% bootstrap_field form.smtp_use_custom layout="control" %}
{% bootstrap_field form.smtp_host layout="control" %}
{% bootstrap_field form.smtp_port layout="control" %}
{% bootstrap_field form.smtp_username layout="control" %}
{% bootstrap_field form.smtp_password layout="control" %}
{% bootstrap_field form.smtp_use_tls layout="control" %}
{% bootstrap_field form.smtp_use_ssl layout="control" %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -5,60 +5,67 @@
<h1>{% trans "Payment settings" %}</h1>
<form action="" method="post" class="form-horizontal form-plugins">
{% csrf_token %}
<fieldset>
<legend>{% trans "Payment providers" %}</legend>
<table class="table table-payment-providers">
<tbody>
{% for provider in providers %}
<tr>
<td>
<strong>{{ provider.verbose_name }}</strong>
</td>
<td>
{% if provider.show_enabled %}
<span class="text-success">
<div class="tabbed-form">
<fieldset>
<legend>{% trans "Payment providers" %}</legend>
<table class="table table-payment-providers">
<tbody>
{% for provider in providers %}
<tr>
<td>
<strong>{{ provider.verbose_name }}</strong>
</td>
<td>
{% if provider.show_enabled %}
<span class="text-success">
<span class="fa fa-check"></span>
{% trans "Enabled" %}
</span>
{% else %}
<span class="text-danger">
{% else %}
<span class="text-danger">
<span class="fa fa-times"></span>
{% trans "Disabled" %}
</span>
{% endif %}
</td>
<td class="text-right">
<a href="{% url 'control:event.settings.payment.provider' event=request.event.slug organizer=request.organizer.slug provider=provider.identifier %}"
class="btn btn-default">
<span class="fa fa-cog"></span>
{% trans "Settings" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
There are no payment providers available. Please go to the <a {{ plugin_settings_href }}>plugin settings</a> and activate one or more payment plugins.
{% endblocktrans %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<fieldset>
<legend>{% trans "General payment settings" %}</legend>
{% bootstrap_form_errors form layout="control" %}
{% bootstrap_field form.payment_term_days layout="control" %}
{% bootstrap_field form.payment_term_last layout="control" %}
{% bootstrap_field form.payment_term_weekdays layout="control" %}
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
{% bootstrap_field form.payment_term_accept_late layout="control" %}
{% bootstrap_field form.tax_rate_default layout="control" %}
{% bootstrap_field form.payment_explanation layout="control" %}
</fieldset>
{% endif %}
</td>
<td class="text-right">
<a href="{% url 'control:event.settings.payment.provider' event=request.event.slug organizer=request.organizer.slug provider=provider.identifier %}"
class="btn btn-default">
<span class="fa fa-cog"></span>
{% trans "Settings" %}
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="3">
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
There are no payment providers available. Please go to the
<a {{ plugin_settings_href }}>plugin settings</a> and activate one or more payment plugins.
{% endblocktrans %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</fieldset>
<fieldset>
<legend>{% trans "Deadlines" %}</legend>
{% bootstrap_form_errors form layout="control" %}
{% bootstrap_field form.payment_term_days layout="control" %}
{% bootstrap_field form.payment_term_last layout="control" %}
{% bootstrap_field form.payment_term_weekdays layout="control" %}
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
{% bootstrap_field form.payment_term_accept_late layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced" %}</legend>
{% bootstrap_form_errors form layout="control" %}
{% bootstrap_field form.tax_rate_default layout="control" %}
{% bootstrap_field form.payment_explanation layout="control" %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -3,77 +3,69 @@
{% load bootstrap3 %}
{% block inside %}
<h1>{% trans "Installed plugins" %}</h1>
<form action="" method="post" class="form-horizontal form-plugins">
{% csrf_token %}
<fieldset>
{% if "success" in request.GET %}
<div class="alert alert-success">
{% trans "Your changes have been saved." %}
</div>
{% endif %}
<div class="row row-plugins">
{% for plugin in plugins %}
<div class="col-md-6 col-sm-12">
<div class="panel panel-{% if plugin.app.compatibility_errors %}warning{% elif plugin.module in plugins_active %}success{% else %}default{% endif %}">
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{{ plugin.name }}</h3>
</div>
<div class="col-sm-4">
{% if plugin.app.compatibility_errors %}
<button class="btn disabled btn-block btn-default" disabled="disabled">{% trans "Incompatible" %}</button>
{% elif plugin.restricted and not staff_session %}
<button class="btn disabled btn-block btn-default" disabled="disabled">{% trans "Not available" %}</button>
{% elif plugin.module in plugins_active %}
<button class="btn btn-default btn-block" name="plugin:{{ plugin.module }}" value="disable">{% trans "Disable" %}</button>
{% else %}
<button class="btn btn-default btn-block" name="plugin:{{ plugin.module }}" value="enable">{% trans "Enable" %}</button>
{% endif %}
</div>
</div>
</div>
<div class="panel-body">
{% if plugin.author %}
<p class="meta">{% blocktrans trimmed with v=plugin.version a=plugin.author %}
Version {{ v }} by <em>{{ a }}</em>
{% endblocktrans %}</p>
{% else %}
<p class="meta">{% blocktrans trimmed with v=plugin.version a=plugin.author %}
Version {{ v }}
{% endblocktrans %}</p>
{% endif %}
<p>{{ plugin.description }}</p>
{% if plugin.restricted and not request.user.is_staff %}
<div class="alert alert-warning">
{% trans "This plugin needs to be enabled by a system administrator for your event." %}
</div>
{% endif %}
{% if plugin.app.compatibility_errors %}
<div class="alert alert-warning">
{% trans "This plugin cannot be enabled for the following reasons:" %}
<ul>
{% for e in plugin.app.compatibility_errors %}
<li>{{ e }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if plugin.app.compatibility_warnings %}
<div class="alert alert-warning">
{% trans "This plugin reports the following problems:" %}
<ul>
{% for e in plugin.app.compatibility_warnings %}
<li>{{ e }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
<form action="" method="post" class="form-horizontal form-plugins">
{% csrf_token %}
{% if "success" in request.GET %}
<div class="alert alert-success">
{% trans "Your changes have been saved." %}
</div>
</fieldset>
</form>
{% endif %}
<div class="table-responsive">
<table class="table">
{% for plugin in plugins %}
<tr class="{% if plugin.app.compatibility_errors %}warning{% elif plugin.module in plugins_active %}success{% else %}default{% endif %}">
<td>
<strong>{{ plugin.name }}</strong>
{% if plugin.author %}
<p class="meta text-muted">{% blocktrans trimmed with v=plugin.version a=plugin.author %}
Version {{ v }} by <em>{{ a }}</em>
{% endblocktrans %}</p>
{% else %}
<p class="meta text-muted">{% blocktrans trimmed with v=plugin.version a=plugin.author %}
Version {{ v }}
{% endblocktrans %}</p>
{% endif %}
<p>{{ plugin.description }}</p>
{% if plugin.restricted and not request.user.is_staff %}
<span class="text-muted">
{% trans "This plugin needs to be enabled by a system administrator for your event." %}
</span>
{% endif %}
{% if plugin.app.compatibility_errors %}
<div class="alert alert-warning">
{% trans "This plugin cannot be enabled for the following reasons:" %}
<ul>
{% for e in plugin.app.compatibility_errors %}
<li>{{ e }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% if plugin.app.compatibility_warnings %}
<div class="alert alert-warning">
{% trans "This plugin reports the following problems:" %}
<ul>
{% for e in plugin.app.compatibility_warnings %}
<li>{{ e }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
</td>
<td class="text-right">
{% if plugin.app.compatibility_errors %}
<button class="btn disabled btn-block btn-default" disabled="disabled">{% trans "Incompatible" %}</button>
{% elif plugin.restricted and not staff_session %}
<button class="btn disabled btn-block btn-default" disabled="disabled">{% trans "Not available" %}</button>
{% elif plugin.module in plugins_active %}
<button class="btn btn-default btn-block" name="plugin:{{ plugin.module }}" value="disable">{% trans "Disable" %}</button>
{% else %}
<button class="btn btn-default btn-block" name="plugin:{{ plugin.module }}" value="enable">{% trans "Enable" %}</button>
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
</form>
{% endblock %}

View File

@@ -1,81 +1,119 @@
{% extends "pretixcontrol/event/settings_base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load hierarkey_form %}
{% block custom_header %}
{{ block.super }}
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
{% endblock %}
{% block inside %}
<h1>{% trans "General settings" %}</h1>
<form action="" method="post" class="form-horizontal">
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.slug layout="control" %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
{% bootstrap_field form.location layout="control" %}
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field form.is_public layout="control" %}
<div class="tabbed-form">
<fieldset>
<legend>{% trans "Basics" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.slug layout="control" %}
{% bootstrap_field form.date_from layout="control" %}
{% bootstrap_field form.date_to layout="control" %}
{% bootstrap_field form.location layout="control" %}
{% bootstrap_field form.date_admission layout="control" %}
{% bootstrap_field form.currency layout="control" %}
{% bootstrap_field sform.contact_mail layout="control" %}
{% bootstrap_field sform.imprint_url layout="control" %}
{% bootstrap_field form.is_public layout="control" %}
{% if meta_forms %}
<div class="form-group metadata-group">
<label class="col-md-3 control-label">{% trans "Meta data" %}</label>
<div class="col-md-9">
{% for form in meta_forms %}
<div class="row">
<div class="col-md-4">
<label for="{{ form.value.id_for_label }}">
{{ form.property.name }}
</label>
{% if meta_forms %}
<div class="form-group metadata-group">
<label class="col-md-3 control-label">{% trans "Meta data" %}</label>
<div class="col-md-9">
{% for form in meta_forms %}
<div class="row">
<div class="col-md-4">
<label for="{{ form.value.id_for_label }}">
{{ form.property.name }}
</label>
</div>
<div class="col-md-8">
{% bootstrap_form form layout="inline" %}
</div>
</div>
<div class="col-md-8">
{% bootstrap_form form layout="inline" %}
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</div>
</div>
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Display settings" %}</legend>
{% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.locale layout="control" %}
{% bootstrap_field sform.timezone layout="control" %}
{% bootstrap_field sform.show_date_to layout="control" %}
{% bootstrap_field sform.show_times layout="control" %}
{% bootstrap_field sform.contact_mail layout="control" %}
{% bootstrap_field sform.imprint_url layout="control" %}
{% bootstrap_field sform.confirm_text layout="control" %}
{% bootstrap_field sform.show_quota_left layout="control" %}
{% bootstrap_field sform.display_net_prices layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Timeline" %}</legend>
{% bootstrap_field form.presale_start layout="control" %}
{% bootstrap_field sform.presale_start_show_date layout="control" %}
{% bootstrap_field form.presale_end layout="control" %}
{% bootstrap_field sform.show_items_outside_presale_period layout="control" %}
{% bootstrap_field sform.last_order_modification_date layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Orders" %}</legend>
{% bootstrap_field sform.reservation_time layout="control" %}
{% bootstrap_field sform.max_items_per_order layout="control" %}
{% bootstrap_field sform.attendee_names_asked layout="control" %}
{% bootstrap_field sform.attendee_names_required layout="control" %}
{% bootstrap_field sform.name_scheme layout="control" %}
{% bootstrap_field sform.name_scheme_titles layout="control" %}
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
{% bootstrap_field sform.attendee_emails_asked layout="control" %}
{% bootstrap_field sform.attendee_emails_required layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Waiting list" %}</legend>
{% bootstrap_field sform.waiting_list_enabled layout="control" %}
{% bootstrap_field sform.waiting_list_auto layout="control" %}
{% bootstrap_field sform.waiting_list_hours layout="control" %}
</fieldset>
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Localization" %}</legend>
{% bootstrap_field sform.locales layout="control" %}
{% bootstrap_field sform.locale layout="control" %}
{% bootstrap_field sform.timezone layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Attendee data" %}</legend>
{% bootstrap_field sform.attendee_names_asked layout="control" %}
{% bootstrap_field sform.attendee_names_required layout="control" %}
{% bootstrap_field sform.name_scheme layout="control" %}
{% bootstrap_field sform.name_scheme_titles layout="control" %}
{% bootstrap_field sform.order_email_asked_twice layout="control" %}
{% bootstrap_field sform.attendee_emails_asked layout="control" %}
{% bootstrap_field sform.attendee_emails_required layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Texts" %}</legend>
{% bootstrap_field sform.frontpage_text layout="control" %}
{% bootstrap_field sform.presale_has_ended_text layout="control" %}
{% bootstrap_field sform.voucher_explanation_text layout="control" %}
{% bootstrap_field sform.confirm_text layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Shop design" %}</legend>
{% bootstrap_field sform.logo_image layout="control" %}
{% url "control:organizer.edit" organizer=request.organizer.slug as org_url %}
{% propagated request.event org_url "primary_color" "primary_font" "theme_color_success" "theme_color_danger" %}
{% bootstrap_field sform.primary_color layout="control" %}
{% bootstrap_field sform.theme_color_success layout="control" %}
{% bootstrap_field sform.theme_color_danger layout="control" %}
{% bootstrap_field sform.primary_font layout="control" %}
{% endpropagated %}
</fieldset>
<fieldset>
<legend>{% trans "Timeline" %}</legend>
{% bootstrap_field form.presale_start layout="control" %}
{% bootstrap_field sform.presale_start_show_date layout="control" %}
{% bootstrap_field form.presale_end layout="control" %}
{% bootstrap_field sform.show_items_outside_presale_period layout="control" %}
{% bootstrap_field sform.last_order_modification_date layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Display" %}</legend>
{% bootstrap_field sform.show_date_to layout="control" %}
{% bootstrap_field sform.show_times layout="control" %}
{% bootstrap_field sform.show_quota_left layout="control" %}
{% bootstrap_field sform.display_net_prices layout="control" %}
{% bootstrap_field sform.show_variations_expanded layout="control" %}
{% bootstrap_field sform.hide_sold_out layout="control" %}
{% bootstrap_field sform.meta_noindex layout="control" %}
{% if sform.frontpage_subevent_ordering %}
{% bootstrap_field sform.frontpage_subevent_ordering layout="control" %}
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Cart" %}</legend>
{% bootstrap_field sform.reservation_time layout="control" %}
{% bootstrap_field sform.max_items_per_order layout="control" %}
{% bootstrap_field sform.redirect_to_checkout_directly layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Waiting list" %}</legend>
{% bootstrap_field sform.waiting_list_enabled layout="control" %}
{% bootstrap_field sform.waiting_list_auto layout="control" %}
{% bootstrap_field sform.waiting_list_hours layout="control" %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -18,102 +18,93 @@
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.rate addon_after="%" layout="control" %}
<details class="panel panel-default"
{% if rule.eu_reverse_charge or rule.has_custom_rules or form.errors %}open{% endif %}>
<summary class="panel-heading">
<h4 class="panel-title">
<strong>{% trans "Advanced settings" %}</strong>
<i class="fa fa-angle-down collapse-indicator"></i>
</h4>
</summary>
<div id="advanced">
<div class="panel-body">
<legend>{% trans "Advanced settings" %}</legend>
<div class="alert alert-legal">
{% blocktrans trimmed with docs="https://docs.pretix.eu/en/latest/user/events/taxes.html" %}
These settings are intended for advanced users. See the
<a href="{{ docs }}">documentation</a>
for more information. Note that we are not responsible for the correct handling
of taxes in your ticket shop. If in doubt, please contact a lawyer or tax consultant.
{% endblocktrans %}
</div>
{% bootstrap_field form.price_includes_tax layout="control" %}
{% bootstrap_field form.eu_reverse_charge layout="control" %}
{% bootstrap_field form.home_country layout="control" %}
<legend>{% trans "Custom taxation rules" %}</legend>
<div class="alert alert-warning">
{% blocktrans trimmed %}
These settings are intended for professional users with very specific taxation situations.
If you create any rule here, the reverse charge settings above will be ignored. The rules will be
checked in order and once the first rule matches the order, it will be used and all further rules will
be ignored. If no rule matches, tax will be charged.
{% endblocktrans %}
{% trans "All of these rules will only apply if an invoice address is set." %}
</div>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
{% bootstrap_form_errors form %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-4">
{% bootstrap_field form.country layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.address_type layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.action layout='inline' form_group_class="" %}
</div>
<div class="col-sm-2 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-4">
{% bootstrap_field formset.empty_form.country layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field formset.empty_form.address_type layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field formset.empty_form.action layout='inline' form_group_class="" %}
</div>
<div class="col-sm-2 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new rule" %}</button>
</p>
</div>
<div class="tabbed-form">
<fieldset>
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.rate addon_after="%" layout="control" %}
{% bootstrap_field form.price_includes_tax layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Advanced" %}</legend>
<div class="alert alert-legal">
{% blocktrans trimmed with docs="https://docs.pretix.eu/en/latest/user/events/taxes.html" %}
These settings are intended for advanced users. See the
<a href="{{ docs }}">documentation</a>
for more information. Note that we are not responsible for the correct handling
of taxes in your ticket shop. If in doubt, please contact a lawyer or tax consultant.
{% endblocktrans %}
</div>
{% bootstrap_field form.eu_reverse_charge layout="control" %}
{% bootstrap_field form.home_country layout="control" %}
<h3>{% trans "Custom taxation rules" %}</h3>
<div class="alert alert-warning">
{% blocktrans trimmed %}
These settings are intended for professional users with very specific taxation situations.
If you create any rule here, the reverse charge settings above will be ignored. The rules will be
checked in order and once the first rule matches the order, it will be used and all further rules will
be ignored. If no rule matches, tax will be charged.
{% endblocktrans %}
{% trans "All of these rules will only apply if an invoice address is set." %}
</div>
</div>
</details>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
{% bootstrap_form_errors form %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-4">
{% bootstrap_field form.country layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.address_type layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field form.action layout='inline' form_group_class="" %}
</div>
<div class="col-sm-2 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-sm-4">
{% bootstrap_field formset.empty_form.country layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field formset.empty_form.address_type layout='inline' form_group_class="" %}
</div>
<div class="col-sm-3">
{% bootstrap_field formset.empty_form.action layout='inline' form_group_class="" %}
</div>
<div class="col-sm-2 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new rule" %}</button>
</p>
</div>
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -5,48 +5,55 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
<h1>{% trans "Ticket download" %}</h1>
<fieldset>
{% if request.event.settings.ticket_download and not any_enabled %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
You activated ticket downloads but no output provider is enabled. Be sure to enable a plugin and
activate an output provider.
{% endblocktrans %}
</div>
{% endif %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.ticket_download layout="control" %}
{% bootstrap_field form.ticket_download_date layout="control" %}
{% bootstrap_field form.ticket_download_addons layout="control" %}
{% bootstrap_field form.ticket_download_nonadm layout="control" %}
{% bootstrap_field form.ticket_download_pending layout="control" %}
{% for provider in providers %}
<div class="panel panel-default ticketoutput-panel">
<div class="panel-heading">
<a href="{% url "control:event.settings.tickets.preview" event=request.event.slug organizer=request.organizer.slug output=provider.identifier %}"
class="btn btn-default btn-sm pull-right {% if not provider.preview_allowed %}disabled{% endif %}"
target="_blank">
{% trans "Preview" %}
</a>
<h3 class="panel-title">{{ provider.verbose_name }}</h3>
<div class="clear"></div>
<div class="tabbed-form">
<fieldset>
<legend>{% trans "Download settings" %}</legend>
{% if request.event.settings.ticket_download and not any_enabled %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
You activated ticket downloads but no output provider is enabled. Be sure to enable a plugin and
activate an output provider.
{% endblocktrans %}
</div>
<div class="panel-body">
{% bootstrap_form provider.form layout='horizontal' %}
{% with c=provider.settings_content %}
{% if c %}{{ c|safe }}{% endif %}
{% endwith %}
{% endif %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.ticket_download layout="control" %}
{% bootstrap_field form.ticket_download_date layout="control" %}
{% bootstrap_field form.ticket_download_addons layout="control" %}
{% bootstrap_field form.ticket_download_nonadm layout="control" %}
{% bootstrap_field form.ticket_download_pending layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Download formats" %}</legend>
{% for provider in providers %}
<div class="panel panel-default ticketoutput-panel">
<div class="panel-heading">
<a href="{% url "control:event.settings.tickets.preview" event=request.event.slug organizer=request.organizer.slug output=provider.identifier %}"
class="btn btn-default btn-sm pull-right {% if not provider.preview_allowed %}disabled{% endif %}"
target="_blank">
{% trans "Preview" %}
</a>
<h3 class="panel-title">{{ provider.verbose_name }}</h3>
<div class="clear"></div>
</div>
<div class="panel-body">
{% bootstrap_form provider.form layout='horizontal' %}
{% with c=provider.settings_content %}
{% if c %}{{ c|safe }}{% endif %}
{% endwith %}
</div>
</div>
</div>
{% empty %}
<div class="alert alert-warning">
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
There are no ticket outputs available. Please go to the <a {{ plugin_settings_href }}>plugin settings</a> and activate one or more ticket output plugins.
{% endblocktrans %}
</div>
{% endfor %}
</fieldset>
{% empty %}
<div class="alert alert-warning">
{% url "control:event.settings.plugins" event=request.event.slug organizer=request.organizer.slug as plugin_settings_url %}
{% blocktrans trimmed with plugin_settings_href='href="'|add:plugin_settings_url|add:'"'|safe %}
There are no ticket outputs available. Please go to the
<a {{ plugin_settings_href }}>plugin settings</a> and activate one or more ticket output plugins.
{% endblocktrans %}
</div>
{% endfor %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -1,98 +0,0 @@
{% extends "pretixcontrol/item/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block inside %}
<p>
{% blocktrans trimmed %}
With add-ons, you can specify products that can be bought as an addition to this product. For example, if
you host a conference with a base conference ticket and a number of workshops, you could define the
workshops as add-ons to the conference ticket. With this configuration, the workshops cannot be bought
on their own but only in combination with a conference ticket. You can here specify categories of products
that can be used as add-ons to this product. You can also specify the minimum and maximum number of
add-ons of the given category that can or need to be chosen. The user can buy every add-on from the
category at most once. If an add-on product has multiple variations, only one of them can be bought.
{% endblocktrans %}
</p>
<form class="form-horizontal branches" method="post" action="">
{% csrf_token %}
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Add-On" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-xs btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_form_errors form %}
{% bootstrap_field form.addon_category layout="control" %}
{% bootstrap_field form.min_count layout="control" %}
{% bootstrap_field form.max_count layout="control" %}
{% bootstrap_field form.price_included layout="control" %}
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Add-On" %}</h3>
</div>
<div class="col-sm-4">
<button type="button" class="btn btn-xs btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-xs btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.addon_category layout="control" %}
{% bootstrap_field formset.empty_form.min_count layout="control" %}
{% bootstrap_field formset.empty_form.max_count layout="control" %}
{% bootstrap_field formset.empty_form.price_included layout="control" %}
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new add-on" %}</button>
</p>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -4,37 +4,6 @@
{% block content %}
{% if object.id %}
<h1>{% trans "Modify product:" %} {{ object.name }}</h1>
<ul class="nav nav-pills">
<li {% if "event.item" == url_name %}class="active"{% endif %}>
<a href="{% url 'control:event.item' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">
{% trans "General information" %}
</a>
</li>
{% if object.has_variations %}
<li {% if "event.item.variations" == url_name %}class="active"{% endif %}>
<a href="{% url 'control:event.item.variations' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">
{% trans "Variations" %}
</a>
</li>
{% endif %}
<li {% if "event.item.addons" == url_name %}class="active"{% endif %}>
<a href="{% url 'control:event.item.addons' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">
{% trans "Add-Ons" %}
</a>
</li>
<li {% if "event.item.bundles" == url_name %}class="active"{% endif %}>
<a href="{% url 'control:event.item.bundles' organizer=request.event.organizer.slug event=request.event.slug item=object.id %}">
{% trans "Bundled products" %}
</a>
</li>
{% for n in extra_nav %}
<li {% if n.active %}class="active"{% endif %}>
<a href="{{ n.url }}">
{{ n.label }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<h1>{% trans "Create product" %}</h1>
<p>{% blocktrans trimmed %}

View File

@@ -1,80 +0,0 @@
{% extends "pretixcontrol/item/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block inside %}
<p>
{% blocktrans trimmed %}
With bundles, you can specify products that are always automatically added as add-ons in the cart for this product.
{% endblocktrans %}
</p>
<form class="form-horizontal branches" method="post" action="">
{% csrf_token %}
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Bundled product" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_form_errors form %}
{% bootstrap_field form.itemvar layout="control" %}
{% bootstrap_field form.count layout="control" %}
{% bootstrap_field form.designated_price layout="control" %}
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Bundled product" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.itemvar layout="control" %}
{% bootstrap_field formset.empty_form.count layout="control" %}
{% bootstrap_field formset.empty_form.designated_price layout="control" %}
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new bundled product" %}</button>
</p>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,87 @@
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
<p>
{% blocktrans trimmed %}
With add-ons, you can specify products that can be bought as an addition to this product. For example, if
you host a conference with a base conference ticket and a number of workshops, you could define the
workshops as add-ons to the conference ticket. With this configuration, the workshops cannot be bought
on their own but only in combination with a conference ticket. You can here specify categories of products
that can be used as add-ons to this product. You can also specify the minimum and maximum number of
add-ons of the given category that can or need to be chosen. The user can buy every add-on from the
category at most once. If an add-on product has multiple variations, only one of them can be bought.
{% endblocktrans %}
</p>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Add-On" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-xs btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_form_errors form %}
{% bootstrap_field form.addon_category layout="control" %}
{% bootstrap_field form.min_count layout="control" %}
{% bootstrap_field form.max_count layout="control" %}
{% bootstrap_field form.price_included layout="control" %}
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Add-On" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-xs btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.addon_category layout="control" %}
{% bootstrap_field formset.empty_form.min_count layout="control" %}
{% bootstrap_field formset.empty_form.max_count layout="control" %}
{% bootstrap_field formset.empty_form.price_included layout="control" %}
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new add-on" %}</button>
</p>
</div>

View File

@@ -0,0 +1,70 @@
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
<p>
{% blocktrans trimmed %}
With bundles, you can specify products that are always automatically added as add-ons in the cart for this product.
{% endblocktrans %}
</p>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Bundled product" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_form_errors form %}
{% bootstrap_field form.itemvar layout="control" %}
{% bootstrap_field form.count layout="control" %}
{% bootstrap_field form.designated_price layout="control" %}
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<div class="row">
<div class="col-sm-8">
<h3 class="panel-title">{% trans "Bundled product" %}</h3>
</div>
<div class="col-sm-4 text-right">
<button type="button" class="btn btn-xs btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.itemvar layout="control" %}
{% bootstrap_field formset.empty_form.count layout="control" %}
{% bootstrap_field formset.empty_form.designated_price layout="control" %}
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new bundled product" %}</button>
</p>
</div>

View File

@@ -0,0 +1,88 @@
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
<div class="row">
<div class="col-md-9">
{% bootstrap_field form.value layout='inline' form_group_class="" %}
</div>
<div class="col-md-3 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</h4>
</div>
<div class="panel-body form-horizontal">
{% if form.instance.pk and not form.instance.quotas.exists %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
Please note that your variation will <strong>not</strong> be available for sale
until you have added it to an existing or newly created quota.
{% endblocktrans %}
</div>
{% endif %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.description layout="control" %}
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
<div class="row">
<div class="col-md-9">
{% bootstrap_field formset.empty_form.value layout='inline' form_group_class="" %}
</div>
<div class="col-md-3 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</h4>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.active layout="control" %}
{% bootstrap_field formset.empty_form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.description layout="control" %}
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new variation" %}</button>
</p>
</div>

View File

@@ -1,56 +1,70 @@
{% extends "pretixcontrol/item/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block inside %}
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
<div class="row">
<div class="col-xs-12 col-lg-10">
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.name layout="control" %}
<div class="internal-name-wrapper">
{% bootstrap_field form.internal_name layout="control" %}
</div>
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.category layout="control" %}
{% bootstrap_field form.admission layout="control" %}
{% bootstrap_field form.description layout="control" %}
{% bootstrap_field form.picture layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Price settings" %}</legend>
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.tax_rule layout="control" %}
{% bootstrap_field form.free_price layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Availability" %}</legend>
{% bootstrap_field form.sales_channels layout="control" %}
{% bootstrap_field form.available_from layout="control" %}
{% bootstrap_field form.available_until layout="control" %}
{% bootstrap_field form.max_per_order layout="control" %}
{% bootstrap_field form.min_per_order layout="control" %}
{% bootstrap_field form.hidden_if_available layout="control" %}
{% bootstrap_field form.require_voucher layout="control" %}
{% bootstrap_field form.hide_without_voucher layout="control" %}
{% bootstrap_field form.require_bundling layout="control" %}
{% bootstrap_field form.allow_cancel layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Check-in" %}</legend>
{% bootstrap_field form.checkin_attention layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Additional settings" %}</legend>
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.require_approval layout="control" %}
{% bootstrap_field form.generate_tickets layout="control" %}
{% bootstrap_field form.show_quota_left layout="control" %}
{% for f in plugin_forms %}
{% bootstrap_form f layout="control" %}
<div class="tabbed-form">
<fieldset>
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.name layout="control" %}
<div class="internal-name-wrapper">
{% bootstrap_field form.internal_name layout="control" %}
</div>
{% bootstrap_field form.category layout="control" %}
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.admission layout="control" %}
{% bootstrap_field form.description layout="control" %}
{% bootstrap_field form.picture layout="control" %}
{% bootstrap_field form.require_approval layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Price" %}</legend>
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.tax_rule layout="control" %}
{% bootstrap_field form.free_price layout="control" %}
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Availability" %}</legend>
{% bootstrap_field form.sales_channels layout="control" %}
{% bootstrap_field form.available_from layout="control" %}
{% bootstrap_field form.available_until layout="control" %}
{% bootstrap_field form.max_per_order layout="control" %}
{% bootstrap_field form.min_per_order layout="control" %}
{% bootstrap_field form.hidden_if_available layout="control" %}
{% bootstrap_field form.require_voucher layout="control" %}
{% bootstrap_field form.hide_without_voucher layout="control" %}
{% bootstrap_field form.require_bundling layout="control" %}
{% bootstrap_field form.allow_cancel layout="control" %}
</fieldset>
{% for v in formsets.values %}
<fieldset>
<legend>{{ v.title }}</legend>
{% include v.template with formset=v %}
</fieldset>
{% endfor %}
</fieldset>
<fieldset>
<legend>{% trans "Tickets & check-in" %}</legend>
{% bootstrap_field form.generate_tickets layout="control" %}
{% bootstrap_field form.checkin_attention layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Additional settings" %}</legend>
{% bootstrap_field form.show_quota_left layout="control" %}
{% for f in plugin_forms %}
{% bootstrap_form f layout="control" %}
{% endfor %}
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</div>
<div class="col-xs-12 col-lg-2">
<div class="panel panel-default">
@@ -63,10 +77,5 @@
</div>
</div>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -1,99 +0,0 @@
{% extends "pretixcontrol/item/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load formset_tags %}
{% block inside %}
<form class="form-horizontal branches" method="post" action="">
{% csrf_token %}
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
<div class="row">
<div class="col-md-9">
{% bootstrap_field form.value layout='inline' form_group_class="" %}
</div>
<div class="col-md-3 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</h4>
</div>
<div class="panel-body form-horizontal">
{% if form.instance.pk and not form.instance.quotas.exists %}
<div class="alert alert-warning">
{% blocktrans trimmed %}
Please note that your variation will <strong>not</strong> be available for sale
until you have added it to an existing or newly created quota.
{% endblocktrans %}
</div>
{% endif %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.active layout="control" %}
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field form.description layout="control" %}
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="panel panel-default" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="panel-heading">
<h4 class="panel-title">
<div class="row">
<div class="col-md-9">
{% bootstrap_field formset.empty_form.value layout='inline' form_group_class="" %}
</div>
<div class="col-md-3 text-right">
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</h4>
</div>
<div class="panel-body form-horizontal">
{% bootstrap_field formset.empty_form.active layout="control" %}
{% bootstrap_field formset.empty_form.default_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.original_price addon_after=request.event.currency layout="control" %}
{% bootstrap_field formset.empty_form.description layout="control" %}
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new variation" %}</button>
</p>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -19,113 +19,115 @@
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_form_errors form %}
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_field form.question layout="control" %}
{% bootstrap_field form.type layout="control" %}
{% bootstrap_field form.items layout="control" %}
{% bootstrap_field form.required layout="control" %}
</fieldset>
<div class="alert alert-info alert-required-boolean">
{% blocktrans trimmed %}
If you mark a Yes/No question as required, it means that the user has to select Yes and No is not
accepted. If you want to allow both options, do not make this field required.
{% endblocktrans %}
</div>
<fieldset id="answer-options">
<legend>{% trans "Answer options" %}</legend>
<noscript>
<p>{% trans "Only applicable if you choose 'Choose one/multiple from a list' above." %}</p>
</noscript>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<div class="tabbed-form">
<fieldset>
<legend>{% trans "General " %}</legend>
{% bootstrap_field form.question layout="control" %}
{% bootstrap_field form.type layout="control" %}
{% bootstrap_field form.items layout="control" %}
{% bootstrap_field form.required layout="control" %}
<div class="alert alert-info alert-required-boolean">
{% blocktrans trimmed %}
If you mark a Yes/No question as required, it means that the user has to select Yes and No is not
accepted. If you want to allow both options, do not make this field required.
{% endblocktrans %}
</div>
<div id="answer-options">
<h3>{% trans "Answer options" %}</h3>
<noscript>
<p>{% trans "Only applicable if you choose 'Choose one/multiple from a list' above." %}</p>
</noscript>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<span class="text-muted">
{% blocktrans trimmed with id=form.instance.identifier %}
Answer option {{ id }}
{% endblocktrans %}
</span>
{% bootstrap_form_errors form %}
{% bootstrap_field form.answer layout='inline' form_group_class="" %}
{% bootstrap_form_errors form %}
{% bootstrap_field form.answer layout='inline' form_group_class="" %}
</div>
<div class="col-xs-2 text-right">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="col-xs-2 text-right">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
{% bootstrap_field formset.empty_form.ORDER form_group_class="" layout="inline" %}
</div>
<div class="row question-option-row">
<div class="col-xs-10">
<span class="text-muted">
{% trans "New answer option" %}
</span>
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
{% bootstrap_field formset.empty_form.answer layout='inline' form_group_class="" %}
</div>
<div class="col-xs-2 text-right">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
<div class="col-xs-2 text-right">
<span>&nbsp;</span><br>
<button type="button" class="btn btn-default" data-formset-move-up-button>
<i class="fa fa-arrow-up"></i></button>
<button type="button" class="btn btn-default" data-formset-move-down-button>
<i class="fa fa-arrow-down"></i></button>
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new option" %}</button>
</p>
</div>
</fieldset>
<fieldset>
<legend>{% trans "Advanced settings" %}</legend>
{% bootstrap_field form.help_text layout="control" %}
{% bootstrap_field form.identifier layout="control" %}
{% bootstrap_field form.ask_during_checkin layout="control" %}
{% bootstrap_field form.hidden layout="control" %}
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add a new option" %}</button>
</p>
</div>
</div>
</fieldset>
<fieldset>
<legend>{% trans "Advanced" %}</legend>
{% bootstrap_field form.help_text layout="control" %}
{% bootstrap_field form.identifier layout="control" %}
{% bootstrap_field form.ask_during_checkin layout="control" %}
{% bootstrap_field form.hidden layout="control" %}
<div class="form-group">
<label class="col-md-3 control-label" for="id_dependency_question">
{% trans "Question dependency" %}
<br><span class="optional">{% trans "Optional" context "form" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.dependency_question layout="inline" form_group_class="inner" %}
<div class="form-group">
<label class="col-md-3 control-label" for="id_dependency_question">
{% trans "Question dependency" %}
<br><span class="optional">{% trans "Optional" context "form" %}</span>
</label>
<div class="col-md-4">
{% bootstrap_field form.dependency_question layout="inline" form_group_class="inner" %}
</div>
<div class="col-md-5">
<script type="text/plain" id="dependency_value_val">{{ form.instance.dependency_values|escapejson_dumps }}</script>
{% bootstrap_field form.dependency_values layout="inline" form_group_class="inner" %}
</div>
</div>
<div class="col-md-5">
<script type="text/plain" id="dependency_value_val">{{ form.instance.dependency_values|escapejson_dumps }}</script>
{% bootstrap_field form.dependency_values layout="inline" form_group_class="inner" %}
</div>
</div>
</fieldset>
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -1,42 +0,0 @@
{% extends "pretixcontrol/organizers/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block custom_header %}
{{ block.super }}
<link type="text/css" rel="stylesheet" href="{% url "control:pdf.css" %}">
{% endblock %}
{% block inner %}
<h1>{% trans "Display settings" %}</h1>
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend>{% trans "Organizer page" %}</legend>
{% bootstrap_form_errors form %}
{% bootstrap_field form.locales layout="control" %}
{% bootstrap_field form.organizer_logo_image layout="control" %}
{% bootstrap_field form.organizer_homepage_text layout="control" %}
{% bootstrap_field form.event_list_type layout="control" %}
{% bootstrap_field form.event_list_availability layout="control" %}
{% bootstrap_field form.organizer_link_back layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Shop design" %}</legend>
<p class="help-block">
{% blocktrans trimmed %}
These settings will be used for the organizer page as well as for the default settings
for all events in this account that do not have their own design settings.
{% endblocktrans %}
</p>
{% bootstrap_field form.primary_color layout="control" %}
{% bootstrap_field form.theme_color_success layout="control" %}
{% bootstrap_field form.theme_color_danger layout="control" %}
{% bootstrap_field form.primary_font layout="control" %}
{% bootstrap_field form.favicon layout="control" %}
</fieldset>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -16,79 +16,104 @@
<form action="" method="post" class="form-horizontal" enctype="multipart/form-data">
{% csrf_token %}
<fieldset>
<legend>{% trans "General information" %}</legend>
{% bootstrap_form_errors form %}
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.slug layout="control" %}
{% if form.domain %}
{% bootstrap_field form.domain layout="control" %}
{% endif %}
</fieldset>
<fieldset>
<legend>{% trans "Other" %}</legend>
{% bootstrap_form_errors sform %}
{% bootstrap_field sform.organizer_info_text layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Event metadata (advanced)" %}</legend>
<p>
{% blocktrans trimmed %}
You can here define a set of metadata properties (i.e. variables) that you can later set for your
events and re-use in places like ticket layouts. This is an useful timesaver if you create lots and
lots of events.
{% endblocktrans %}
</p>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-md-5">
{% bootstrap_form_errors form %}
{% bootstrap_field form.name layout='inline' form_group_class="" %}
</div>
<div class="col-md-5 col-lg-6">
{% bootstrap_field form.default layout='inline' form_group_class="" %}
</div>
<div class="col-md-2 col-lg-1 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-md-5">
{% bootstrap_field formset.empty_form.name layout='inline' form_group_class="" %}
</div>
<div class="col-md-5 col-lg-6">
{% bootstrap_field formset.empty_form.default layout='inline' form_group_class="" %}
</div>
<div class="col-md-2 col-lg-1 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add property" %}</button>
{% bootstrap_form_errors sform %}
{% bootstrap_form_errors form %}
<div class="tabbed-form">
<fieldset>
<legend>{% trans "General" %}</legend>
{% bootstrap_field form.name layout="control" %}
{% bootstrap_field form.slug layout="control" %}
{% if form.domain %}
{% bootstrap_field form.domain layout="control" %}
{% endif %}
{% bootstrap_field sform.organizer_info_text layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Organizer page" %}</legend>
{% bootstrap_field sform.organizer_logo_image layout="control" %}
{% bootstrap_field sform.organizer_homepage_text layout="control" %}
{% bootstrap_field sform.event_list_type layout="control" %}
{% bootstrap_field sform.event_list_availability layout="control" %}
{% bootstrap_field sform.organizer_link_back layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Localization" %}</legend>
{% bootstrap_field sform.locales layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Shop design" %}</legend>
<p class="help-block">
{% blocktrans trimmed %}
These settings will be used for the organizer page as well as for the default settings
for all events in this account that do not have their own design settings.
{% endblocktrans %}
</p>
</div>
</fieldset>
{% bootstrap_field sform.primary_color layout="control" %}
{% bootstrap_field sform.theme_color_success layout="control" %}
{% bootstrap_field sform.theme_color_danger layout="control" %}
{% bootstrap_field sform.primary_font layout="control" %}
{% bootstrap_field sform.favicon layout="control" %}
</fieldset>
<fieldset>
<legend>{% trans "Event metadata" %}</legend>
<p>
{% blocktrans trimmed %}
You can here define a set of metadata properties (i.e. variables) that you can later set for your
events and re-use in places like ticket layouts. This is an useful timesaver if you create lots and
lots of events.
{% endblocktrans %}
</p>
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}">
{{ formset.management_form }}
{% bootstrap_formset_errors formset %}
<div data-formset-body>
{% for form in formset %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ form.id }}
{% bootstrap_field form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-md-5">
{% bootstrap_form_errors form %}
{% bootstrap_field form.name layout='inline' form_group_class="" %}
</div>
<div class="col-md-5 col-lg-6">
{% bootstrap_field form.default layout='inline' form_group_class="" %}
</div>
<div class="col-md-2 col-lg-1 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endfor %}
</div>
<script type="form-template" data-formset-empty-form>
{% escapescript %}
<div class="row" data-formset-form>
<div class="sr-only">
{{ formset.empty_form.id }}
{% bootstrap_field formset.empty_form.DELETE form_group_class="" layout="inline" %}
</div>
<div class="col-md-5">
{% bootstrap_field formset.empty_form.name layout='inline' form_group_class="" %}
</div>
<div class="col-md-5 col-lg-6">
{% bootstrap_field formset.empty_form.default layout='inline' form_group_class="" %}
</div>
<div class="col-md-2 col-lg-1 text-right">
<button type="button" class="btn btn-danger" data-formset-delete-button>
<i class="fa fa-trash"></i></button>
</div>
</div>
{% endescapescript %}
</script>
<p>
<button type="button" class="btn btn-default" data-formset-add>
<i class="fa fa-plus"></i> {% trans "Add property" %}</button>
</p>
</div>
</fieldset>
</div>
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}

View File

@@ -150,12 +150,6 @@ urlpatterns = [
url(r'^items/$', item.ItemList.as_view(), name='event.items'),
url(r'^items/add$', item.ItemCreate.as_view(), name='event.items.add'),
url(r'^items/(?P<item>\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'),
url(r'^items/(?P<item>\d+)/variations$', item.ItemVariations.as_view(),
name='event.item.variations'),
url(r'^items/(?P<item>\d+)/addons', item.ItemAddOns.as_view(),
name='event.item.addons'),
url(r'^items/(?P<item>\d+)/bundles', item.ItemBundles.as_view(),
name='event.item.bundles'),
url(r'^items/(?P<item>\d+)/up$', item.item_move_up, name='event.items.up'),
url(r'^items/(?P<item>\d+)/down$', item.item_move_down, name='event.items.down'),
url(r'^items/(?P<item>\d+)/delete$', item.ItemDelete.as_view(), name='event.items.delete'),

View File

@@ -40,10 +40,10 @@ from pretix.base.signals import register_ticket_outputs
from pretix.base.templatetags.money import money_filter
from pretix.base.templatetags.rich_text import markdown_compile_email
from pretix.control.forms.event import (
CancelSettingsForm, CommentForm, DisplaySettingsForm, EventDeleteForm,
EventMetaValueForm, EventSettingsForm, EventUpdateForm,
InvoiceSettingsForm, MailSettingsForm, PaymentSettingsForm, ProviderForm,
QuickSetupForm, QuickSetupProductFormSet, TaxRuleForm, TaxRuleLineFormSet,
CancelSettingsForm, CommentForm, EventDeleteForm, EventMetaValueForm,
EventSettingsForm, EventUpdateForm, InvoiceSettingsForm, MailSettingsForm,
PaymentSettingsForm, ProviderForm, QuickSetupForm,
QuickSetupProductFormSet, TaxRuleForm, TaxRuleLineFormSet,
TicketSettingsForm, WidgetCodeForm,
)
from pretix.control.permissions import EventPermissionRequiredMixin
@@ -63,6 +63,17 @@ class EventSettingsViewMixin:
ctx['is_event_settings'] = True
return ctx
def _save_decoupled(self, form):
# Save fields that are currently only set via the organizer but should be decoupled
fields = set()
for f in self.request.POST.getlist("decouple"):
fields |= set(f.split(","))
for f in fields:
if f not in form.fields:
continue
if f not in self.request.event.settings._cache():
self.request.event.settings.set(f, self.request.event.settings.get(f))
class MetaDataEditorMixin:
meta_form = EventMetaValueForm
@@ -117,7 +128,8 @@ class EventUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, MetaData
return EventSettingsForm(
obj=self.object,
prefix='settings',
data=self.request.POST if self.request.method == 'POST' else None
data=self.request.POST if self.request.method == 'POST' else None,
files=self.request.FILES if self.request.method == 'POST' else None,
)
def get_context_data(self, *args, **kwargs) -> dict:
@@ -128,18 +140,32 @@ class EventUpdate(EventSettingsViewMixin, EventPermissionRequiredMixin, MetaData
@transaction.atomic
def form_valid(self, form):
self._save_decoupled(self.sform)
self.sform.save()
self.save_meta()
change_css = False
if self.sform.has_changed():
self.request.event.log_action('pretix.event.settings', user=self.request.user, data={
k: self.request.event.settings.get(k) for k in self.sform.changed_data
})
display_properties = (
'primary_color', 'theme_color_success', 'theme_color_danger', 'primary_font',
)
if any(p in self.sform.changed_data for p in display_properties):
change_css = True
if form.has_changed():
self.request.event.log_action('pretix.event.changed', user=self.request.user, data={
k: getattr(self.request.event, k) for k in form.changed_data
})
messages.success(self.request, _('Your changes have been saved.'))
if change_css:
regenerate_css.apply_async(args=(self.request.event.pk,))
messages.success(self.request, _('Your changes have been saved. Please note that it can '
'take a short period of time until your changes become '
'active.'))
else:
messages.success(self.request, _('Your changes have been saved.'))
return super().form_valid(form)
def get_success_url(self) -> str:
@@ -325,17 +351,6 @@ class EventSettingsFormView(EventPermissionRequiredMixin, FormView):
kwargs['obj'] = self.request.event
return kwargs
def _save_decoupled(self, form):
# Save fields that are currently only set via the organizer but should be decoupled
fields = set()
for f in self.request.POST.getlist("decouple"):
fields |= set(f.split(","))
for f in fields:
if f not in form.fields:
continue
if f not in self.request.event.settings._cache():
self.request.event.settings.set(f, self.request.event.settings.get(f))
def form_success(self):
pass
@@ -453,41 +468,12 @@ class InvoicePreview(EventPermissionRequiredMixin, View):
return resp
class DisplaySettings(EventSettingsViewMixin, EventSettingsFormView):
model = Event
form_class = DisplaySettingsForm
template_name = 'pretixcontrol/event/display.html'
permission = 'can_change_event_settings'
def get_success_url(self) -> str:
return reverse('control:event.settings.display', kwargs={
class DisplaySettings(View):
def get(self, request, *wargs, **kwargs):
return redirect(reverse('control:event.settings', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug
})
@transaction.atomic
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
form.save()
self._save_decoupled(form)
if form.has_changed():
self.request.event.log_action(
'pretix.event.settings', user=self.request.user, data={
k: (form.cleaned_data.get(k).name
if isinstance(form.cleaned_data.get(k), File)
else form.cleaned_data.get(k))
for k in form.changed_data
}
)
regenerate_css.apply_async(args=(self.request.event.pk,))
messages.success(self.request, _('Your changes have been saved. Please note that it can '
'take a short period of time until your changes become '
'active.'))
return redirect(self.get_success_url())
else:
messages.error(self.request, _('We could not save your changes. See below for details.'))
return self.get(request)
}) + '#tab-0-3-open')
class MailSettings(EventSettingsViewMixin, EventSettingsFormView):

View File

@@ -1,4 +1,5 @@
import json
from collections import OrderedDict
from django.contrib import messages
from django.core.exceptions import PermissionDenied
@@ -13,11 +14,13 @@ from django.utils.functional import cached_property
from django.utils.timezone import now
from django.utils.translation import ugettext, ugettext_lazy as _
from django.views.generic import ListView
from django.views.generic.base import TemplateView
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.generic.edit import DeleteView
from django_countries.fields import Country
from pretix.api.serializers.item import (
ItemAddOnSerializer, ItemBundleSerializer, ItemVariationSerializer,
)
from pretix.base.forms import I18nFormSet
from pretix.base.models import (
CartPosition, Item, ItemCategory, ItemVariation, Order, Question,
@@ -35,7 +38,7 @@ from pretix.control.forms.item import (
from pretix.control.permissions import (
EventPermissionRequiredMixin, event_permission_required,
)
from pretix.control.signals import item_forms, nav_item
from pretix.control.signals import item_forms, item_formsets
from . import ChartContainingView, CreateView, PaginationMixin, UpdateView
@@ -824,18 +827,6 @@ class ItemDetailMixin(SingleObjectMixin):
model = Item
context_object_name = 'item'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
nav = sorted(
sum(
(list(a[1]) for a in nav_item.send(self.request.event, request=self.request, item=self.get_object())),
[]
),
key=lambda r: str(r['label'])
)
ctx['extra_nav'] = nav
return ctx
def get_object(self, queryset=None) -> Item:
try:
if not hasattr(self, 'object') or not self.object:
@@ -916,14 +907,85 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
'item': self.get_object().id,
})
def is_valid(self, form):
v = (
form.is_valid()
and all(f.is_valid() for f in self.plugin_forms)
and all(f.is_valid() for f in self.formsets.values())
)
if v and form.cleaned_data['category'] and form.cleaned_data['category'].is_addon:
addons = self.formsets['addons'].ordered_forms + [
ef for ef in self.formsets['addons'].extra_forms
if ef not in self.formsets['addons'].ordered_forms and ef not in self.formsets['addons'].deleted_forms
]
if addons:
messages.error(self.request,
_('You cannot add add-ons to a product that is only available as an add-on '
'itself.'))
v = False
bundles = [
ef for ef in self.formsets['bundles'].forms
if ef not in self.formsets['bundles'].deleted_forms
]
if bundles:
messages.error(self.request,
_('You cannot add bundles to a product that is only available as an add-on '
'itself.'))
v = False
return v
def post(self, request, *args, **kwargs):
self.get_object()
form = self.get_form()
if form.is_valid() and all(f.is_valid() for f in self.plugin_forms):
if self.is_valid(form):
return self.form_valid(form)
else:
return self.form_invalid(form)
def save_formset(self, key, log_base, attr='item', order=True, serializer=None,
rm_verb='removed'):
for form in self.formsets[key].deleted_forms:
if not form.instance.pk:
continue
d = {
'id': form.instance.pk
}
if serializer:
d.update(serializer(form.instance).data)
self.get_object().log_action(
'pretix.event.item.{}.{}'.format(log_base, rm_verb), user=self.request.user, data=d
)
form.instance.delete()
form.instance.pk = None
if order:
forms = self.formsets[key].ordered_forms + [
ef for ef in self.formsets[key].extra_forms
if ef not in self.formsets[key].ordered_forms and ef not in self.formsets[key].deleted_forms
]
else:
forms = [
ef for ef in self.formsets[key].forms
if ef not in self.formsets[key].deleted_forms
]
for i, form in enumerate(forms):
if order:
form.instance.position = i
setattr(form.instance, attr, self.get_object())
created = not form.instance.pk
form.save()
if form.has_changed() and any(a for a in form.changed_data if a != 'ORDER'):
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
if key == 'variations':
change_data['value'] = form.instance.value
change_data['id'] = form.instance.pk
self.get_object().log_action(
'pretix.event.item.{}.changed'.format(log_base) if not created else
'pretix.event.item.{}.added'.format(log_base),
user=self.request.user, data=change_data
)
@transaction.atomic
def form_valid(self, form):
messages.success(self.request, _('Your changes have been saved.'))
@@ -945,6 +1007,27 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
invalidate_cache.apply_async(kwargs={'event': self.request.event.pk, 'item': self.object.pk})
for f in self.plugin_forms:
f.save()
for k, v in self.formsets.items():
if k == 'variations':
self.save_formset(
'variations', 'variation',
serializer=ItemVariationSerializer,
rm_verb='deleted'
)
elif k == 'addons':
self.save_formset(
'addons', 'addons', 'base_item',
serializer=ItemAddOnSerializer
)
elif k == 'bundles':
self.save_formset(
'bundles', 'bundles', 'base_item', order=False,
serializer=ItemBundleSerializer
)
else:
v.save()
return super().form_valid(form)
def form_invalid(self, form):
@@ -954,6 +1037,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
ctx['plugin_forms'] = self.plugin_forms
ctx['formsets'] = self.formsets
if not ctx['item'].active and ctx['item'].bundled_with.count() > 0:
messages.info(self.request, _("You disabled this item, but it is still part of a product bundle. "
@@ -962,246 +1046,51 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, UpdateVie
return ctx
class ItemVariations(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_items'
template_name = 'pretixcontrol/item/variations.html'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.item = None
@cached_property
def formset(self):
formsetclass = inlineformset_factory(
Item, ItemVariation,
form=ItemVariationForm, formset=ItemVariationsFormSet,
can_order=True, can_delete=True, extra=0
)
return formsetclass(self.request.POST if self.request.method == "POST" else None,
queryset=ItemVariation.objects.filter(item=self.get_object()),
event=self.request.event)
def formsets(self):
f = OrderedDict([
('variations', inlineformset_factory(
Item, ItemVariation,
form=ItemVariationForm, formset=ItemVariationsFormSet,
can_order=True, can_delete=True, extra=0
)(
self.request.POST if self.request.method == "POST" else None,
queryset=ItemVariation.objects.filter(item=self.get_object()),
event=self.request.event, prefix="variations"
)),
('addons', inlineformset_factory(
Item, ItemAddOn,
form=ItemAddOnForm, formset=ItemAddOnsFormSet,
can_order=True, can_delete=True, extra=0
)(
self.request.POST if self.request.method == "POST" else None,
queryset=ItemAddOn.objects.filter(base_item=self.get_object()),
event=self.request.event, prefix="addons"
)),
('bundles', inlineformset_factory(
Item, ItemBundle,
form=ItemBundleForm, formset=ItemBundleFormSet,
fk_name='base_item',
can_order=False, can_delete=True, extra=0
)(
self.request.POST if self.request.method == "POST" else None,
queryset=ItemBundle.objects.filter(base_item=self.get_object()),
event=self.request.event, item=self.item, prefix="bundles"
)),
])
if not self.object.has_variations:
del f['variations']
def post(self, request, *args, **kwargs):
with transaction.atomic():
if self.formset.is_valid():
for form in self.formset.deleted_forms:
if not form.instance.pk:
continue
self.get_object().log_action(
'pretix.event.item.variation.deleted', user=self.request.user, data={
'value': form.instance.value,
'id': form.instance.pk
}
)
form.instance.delete()
form.instance.pk = None
forms = self.formset.ordered_forms + [
ef for ef in self.formset.extra_forms
if ef not in self.formset.ordered_forms and ef not in self.formset.deleted_forms
]
for i, form in enumerate(forms):
form.instance.position = i
form.instance.item = self.get_object()
created = not form.instance.pk
form.save()
if form.has_changed():
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
change_data['value'] = form.instance.value
change_data['id'] = form.instance.pk
self.get_object().log_action(
'pretix.event.item.variation.changed' if not created else
'pretix.event.item.variation.added',
user=self.request.user, data=change_data
)
messages.success(self.request, _('Your changes have been saved.'))
return redirect(self.get_success_url())
messages.error(self.request, _('We could not save your changes. See below for details.'))
return self.get(request, *args, **kwargs)
def get_success_url(self) -> str:
return reverse('control:event.item.variations', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().id,
})
def get_context_data(self, **kwargs) -> dict:
self.object = self.get_object()
context = super().get_context_data(**kwargs)
context['formset'] = self.formset
return context
class ItemAddOns(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_items'
template_name = 'pretixcontrol/item/addons.html'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.item = None
@cached_property
def formset(self):
formsetclass = inlineformset_factory(
Item, ItemAddOn,
form=ItemAddOnForm, formset=ItemAddOnsFormSet,
can_order=True, can_delete=True, extra=0
)
return formsetclass(self.request.POST if self.request.method == "POST" else None,
queryset=ItemAddOn.objects.filter(base_item=self.get_object()),
event=self.request.event)
def post(self, request, *args, **kwargs):
with transaction.atomic():
if self.formset.is_valid():
for form in self.formset.deleted_forms:
if not form.instance.pk:
continue
self.get_object().log_action(
'pretix.event.item.addons.removed', user=self.request.user, data={
'category': form.instance.addon_category.pk
}
)
form.instance.delete()
form.instance.pk = None
forms = self.formset.ordered_forms + [
ef for ef in self.formset.extra_forms
if ef not in self.formset.ordered_forms and ef not in self.formset.deleted_forms
]
for i, form in enumerate(forms):
form.instance.base_item = self.get_object()
form.instance.position = i
created = not form.instance.pk
form.save()
if form.has_changed():
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
change_data['id'] = form.instance.pk
self.get_object().log_action(
'pretix.event.item.addons.changed' if not created else
'pretix.event.item.addons.added',
user=self.request.user, data=change_data
)
messages.success(self.request, _('Your changes have been saved.'))
return redirect(self.get_success_url())
return self.get(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
if self.get_object().category and self.get_object().category.is_addon:
messages.error(self.request, _('You cannot add add-ons to a product that is only available as an add-on '
'itself.'))
return redirect(self.get_previous_url())
return super().get(request, *args, **kwargs)
def get_previous_url(self) -> str:
return reverse('control:event.item', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().id,
})
def get_success_url(self) -> str:
return reverse('control:event.item.addons', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().id,
})
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
context['formset'] = self.formset
return context
class ItemBundles(ItemDetailMixin, EventPermissionRequiredMixin, TemplateView):
permission = 'can_change_items'
template_name = 'pretixcontrol/item/bundles.html'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.item = None
@cached_property
def formset(self):
formsetclass = inlineformset_factory(
Item, ItemBundle,
form=ItemBundleForm, formset=ItemBundleFormSet,
fk_name='base_item',
can_order=False, can_delete=True, extra=0
)
return formsetclass(self.request.POST if self.request.method == "POST" else None,
queryset=ItemBundle.objects.filter(base_item=self.get_object()),
event=self.request.event, item=self.item)
def post(self, request, *args, **kwargs):
with transaction.atomic():
if self.formset.is_valid():
for form in self.formset.deleted_forms:
if not form.instance.pk:
continue
self.get_object().log_action(
'pretix.event.item.bundles.removed', user=self.request.user, data={
'bundled_item': form.instance.bundled_item.pk,
'bundled_variation': (form.instance.bundled_variation.pk if form.instance.bundled_variation else None),
'count': form.instance.count,
'designated_price': str(form.instance.designated_price),
}
)
form.instance.delete()
form.instance.pk = None
forms = [
ef for ef in self.formset.forms
if ef not in self.formset.deleted_forms
]
for i, form in enumerate(forms):
form.instance.base_item = self.get_object()
created = not form.instance.pk
form.save()
if form.has_changed():
change_data = {k: form.cleaned_data.get(k) for k in form.changed_data}
change_data['id'] = form.instance.pk
self.get_object().log_action(
'pretix.event.item.bundles.changed' if not created else
'pretix.event.item.bundles.added',
user=self.request.user, data=change_data
)
messages.success(self.request, _('Your changes have been saved.'))
return redirect(self.get_success_url())
return self.get(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
if self.get_object().category and self.get_object().category.is_addon:
messages.error(self.request, _('You cannot add bundles to a product that is only available as an add-on '
'itself.'))
return redirect(self.get_previous_url())
return super().get(request, *args, **kwargs)
def get_previous_url(self) -> str:
return reverse('control:event.item', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().id,
})
def get_success_url(self) -> str:
return reverse('control:event.item.bundles', kwargs={
'organizer': self.request.event.organizer.slug,
'event': self.request.event.slug,
'item': self.get_object().id,
})
def get_context_data(self, **kwargs) -> dict:
context = super().get_context_data(**kwargs)
context['formset'] = self.formset
return context
i = 0
for rec, resp in item_formsets.send(sender=self.request.event, item=self.item, request=self.request):
if isinstance(resp, (list, tuple)):
for k in resp:
f['p-{}'.format(i)] = k
i += 1
else:
f['p-{}'.format(i)] = resp
i += 1
return f
class ItemDelete(EventPermissionRequiredMixin, DeleteView):

View File

@@ -14,6 +14,7 @@ from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from django.utils.functional import cached_property
from django.utils.translation import ugettext_lazy as _
from django.views import View
from django.views.generic import (
CreateView, DeleteView, DetailView, FormView, ListView, UpdateView,
)
@@ -25,9 +26,8 @@ from pretix.base.models.organizer import TeamAPIToken
from pretix.base.services.mail import SendMailException, mail
from pretix.control.forms.filter import OrganizerFilterForm
from pretix.control.forms.organizer import (
DeviceForm, EventMetaPropertyForm, OrganizerDeleteForm,
OrganizerDisplaySettingsForm, OrganizerForm, OrganizerSettingsForm,
OrganizerUpdateForm, TeamForm, WebHookForm,
DeviceForm, EventMetaPropertyForm, OrganizerDeleteForm, OrganizerForm,
OrganizerSettingsForm, OrganizerUpdateForm, TeamForm, WebHookForm,
)
from pretix.control.permissions import (
AdministratorPermissionRequiredMixin, OrganizerPermissionRequiredMixin,
@@ -154,39 +154,13 @@ class OrganizerSettingsFormView(OrganizerDetailViewMixin, OrganizerPermissionReq
return self.get(request)
class OrganizerDisplaySettings(OrganizerSettingsFormView):
model = Organizer
form_class = OrganizerDisplaySettingsForm
template_name = 'pretixcontrol/organizers/display.html'
permission = 'can_change_organizer_settings'
class OrganizerDisplaySettings(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, View):
permission = None
def get_success_url(self) -> str:
return reverse('control:organizer.display', kwargs={
def get(self, request, *wargs, **kwargs):
return redirect(reverse('control:organizer.edit', kwargs={
'organizer': self.request.organizer.slug,
})
@transaction.atomic
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid():
form.save()
if form.has_changed():
self.request.organizer.log_action(
'pretix.organizer.settings', user=self.request.user, data={
k: (form.cleaned_data.get(k).name
if isinstance(form.cleaned_data.get(k), File)
else form.cleaned_data.get(k))
for k in form.changed_data
}
)
regenerate_organizer_css.apply_async(args=(self.request.organizer.pk,))
messages.success(self.request, _('Your changes have been saved. Please note that it can '
'take a short period of time until your changes become '
'active.'))
return redirect(self.get_success_url())
else:
messages.error(self.request, _('We could not save your changes. See below for details.'))
return self.get(request)
}) + '#tab-0-3-open')
class OrganizerDelete(AdministratorPermissionRequiredMixin, FormView):
@@ -263,6 +237,7 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
def form_valid(self, form):
self.save_formset(self.object)
self.sform.save()
change_css = False
if self.sform.has_changed():
self.request.organizer.log_action(
'pretix.organizer.settings',
@@ -274,13 +249,25 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView):
for k in self.sform.changed_data
}
)
display_properties = (
'primary_color', 'theme_color_success', 'theme_color_danger', 'primary_font',
)
if any(p in self.sform.changed_data for p in display_properties):
change_css = True
if form.has_changed():
self.request.organizer.log_action(
'pretix.organizer.changed',
user=self.request.user,
data={k: form.cleaned_data.get(k) for k in form.changed_data}
)
messages.success(self.request, _('Your changes have been saved.'))
if change_css:
regenerate_organizer_css.apply_async(args=(self.request.organizer.pk,))
messages.success(self.request, _('Your changes have been saved. Please note that it can '
'take a short period of time until your changes become '
'active.'))
else:
messages.success(self.request, _('Your changes have been saved.'))
return super().form_valid(form)
def get_form_kwargs(self):

View File

@@ -0,0 +1,41 @@
/*globals $*/
$(function () {
var j = 0;
$(".tabbed-form").each(function () {
var $form = $(this);
var $tabs = $("<ul>").addClass("nav nav-tabs").insertBefore($form);
$form.addClass("tab-content");
var i = 0;
var preselect = null;
$form.find("fieldset").each(function () {
var $fieldset = $(this);
var tid = "tab-" + j + "-" + i;
var $tabli = $("<li>").appendTo($tabs);
var $tablink = $("<a>").attr("role", "tab")
.attr("data-toggle", "tab")
.attr("href", "#" + tid)
.text($fieldset.find("legend").text())
.appendTo($tabli);
if ($fieldset.find(".has-error, .alert-danger").length > 0) {
$tablink.append(" ");
$tablink.append($("<span>").addClass("fa fa-warning text-danger"));
if (preselect === null) {
preselect = i;
}
}
$fieldset.find("legend").remove();
$fieldset.addClass("tab-pane").attr("id", tid);
if (location.hash && ($fieldset.find(location.hash).length || location.hash === "#" + tid + "-open") && preselect === null) {
preselect = i;
}
i++;
});
$tabs.find("a").get(preselect != null ? preselect : 0).click();
$tabs.find("a").on('shown.bs.tab', function (e) {
history.replaceState(null, null, e.target.getAttribute("href") + "-open");
});
j++;
});
});

View File

@@ -74,7 +74,11 @@ div[data-formset-body], div[data-formset-form], div[data-nested-formset-form], d
margin-bottom: 0;
}
.preview-panel .nav-tabs {
.tabbed-form > .tab-pane {
padding: 10px;
}
.nav-tabs {
border-bottom: 0px solid #ddd;
}