forked from CGM_Public/pretix_original
401 lines
14 KiB
Python
401 lines
14 KiB
Python
from django import forms
|
|
from django.conf import settings
|
|
from django.core.exceptions import ValidationError
|
|
from django.core.validators import RegexValidator
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from pytz import common_timezones
|
|
|
|
from pretix.base.forms import I18nModelForm, SettingsForm
|
|
from pretix.base.i18n import I18nFormField, I18nTextarea
|
|
from pretix.base.models import Event
|
|
from pretix.control.forms import ExtFileField
|
|
|
|
|
|
class EventCreateForm(I18nModelForm):
|
|
error_messages = {
|
|
'duplicate_slug': _("You already used this slug for a different event. Please choose a new one."),
|
|
}
|
|
|
|
class Meta:
|
|
model = Event
|
|
fields = [
|
|
'name',
|
|
'slug',
|
|
'currency',
|
|
'date_from',
|
|
'date_to',
|
|
'presale_start',
|
|
'presale_end'
|
|
]
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.organizer = kwargs.pop('organizer')
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def clean_slug(self):
|
|
slug = self.cleaned_data['slug']
|
|
if Event.objects.filter(slug=slug, organizer=self.organizer).exists():
|
|
raise forms.ValidationError(
|
|
self.error_messages['duplicate_slug'],
|
|
code='duplicate_slug'
|
|
)
|
|
return slug
|
|
|
|
|
|
class EventCreateSettingsForm(SettingsForm):
|
|
timezone = forms.ChoiceField(
|
|
choices=((a, a) for a in common_timezones),
|
|
label=_("Default timezone"),
|
|
)
|
|
locales = forms.MultipleChoiceField(
|
|
choices=settings.LANGUAGES,
|
|
label=_("Available langauges"),
|
|
)
|
|
locale = forms.ChoiceField(
|
|
choices=settings.LANGUAGES,
|
|
label=_("Default language"),
|
|
)
|
|
|
|
def clean(self):
|
|
data = super().clean()
|
|
if data['locale'] not in data['locales']:
|
|
raise ValidationError({
|
|
'locale': _('Your default locale must also be enebled for your event (see box above).')
|
|
})
|
|
return data
|
|
|
|
|
|
class EventUpdateForm(I18nModelForm):
|
|
def clean_slug(self):
|
|
return self.instance.slug
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.fields['slug'].widget.attrs['readonly'] = 'readonly'
|
|
|
|
class Meta:
|
|
model = Event
|
|
localized_fields = '__all__'
|
|
fields = [
|
|
'name',
|
|
'slug',
|
|
'currency',
|
|
'date_from',
|
|
'date_to',
|
|
'is_public',
|
|
'presale_start',
|
|
'presale_end',
|
|
]
|
|
|
|
|
|
class EventSettingsForm(SettingsForm):
|
|
show_date_to = forms.BooleanField(
|
|
label=_("Show event end date"),
|
|
help_text=_("If disabled, only event's start date will be displayed to the public."),
|
|
required=False
|
|
)
|
|
show_times = forms.BooleanField(
|
|
label=_("Show dates with time"),
|
|
help_text=_("If disabled, the event's start and end date will be displayed without the time of day."),
|
|
required=False
|
|
)
|
|
show_items_outside_presale_period = forms.BooleanField(
|
|
label=_("Show items outside presale period"),
|
|
help_text=_("Show item details before presale has started and after presale has ended"),
|
|
required=False
|
|
)
|
|
presale_start_show_date = forms.BooleanField(
|
|
label=_("Show start date"),
|
|
help_text=_("Show the presale start date before presale has started"),
|
|
required=False
|
|
)
|
|
last_order_modification_date = forms.DateTimeField(
|
|
label=_('Last date of modifications'),
|
|
help_text=_("The last date users can modify details of their orders, such as attendee names or "
|
|
"answers to questions."),
|
|
required=False
|
|
)
|
|
timezone = forms.ChoiceField(
|
|
choices=((a, a) for a in common_timezones),
|
|
label=_("Default timezone"),
|
|
)
|
|
locales = forms.MultipleChoiceField(
|
|
choices=settings.LANGUAGES,
|
|
label=_("Available langauges"),
|
|
)
|
|
locale = forms.ChoiceField(
|
|
choices=settings.LANGUAGES,
|
|
label=_("Default language"),
|
|
)
|
|
show_quota_left = forms.BooleanField(
|
|
label=_("Show number of tickets left"),
|
|
help_text=_("Publicly show how many tickets of a certain type are still available."),
|
|
required=False
|
|
)
|
|
attendee_names_asked = forms.BooleanField(
|
|
label=_("Ask for attendee names"),
|
|
help_text=_("Ask for a name for all tickets which include admission to the event."),
|
|
required=False
|
|
)
|
|
attendee_names_required = forms.BooleanField(
|
|
label=_("Require attendee names"),
|
|
help_text=_("Require customers to fill in the names of all attendees."),
|
|
required=False
|
|
)
|
|
max_items_per_order = forms.IntegerField(
|
|
min_value=1,
|
|
label=_("Maximum number of items per order")
|
|
)
|
|
reservation_time = forms.IntegerField(
|
|
min_value=0,
|
|
label=_("Reservation period"),
|
|
help_text=_("The number of minutes the items in a user's card are reserved for this user."),
|
|
)
|
|
imprint_url = forms.URLField(
|
|
label=_("Imprint URL"),
|
|
required=False,
|
|
)
|
|
contact_mail = forms.EmailField(
|
|
label=_("Contact address"),
|
|
required=False,
|
|
help_text=_("Public email address for contacting the organizer")
|
|
)
|
|
|
|
def clean(self):
|
|
data = super().clean()
|
|
if data['locale'] not in data['locales']:
|
|
raise ValidationError({
|
|
'locale': _('Your default locale must also be enebled for your event (see box above).')
|
|
})
|
|
if data['attendee_names_required'] and not data['attendee_names_asked']:
|
|
raise ValidationError({
|
|
'attendee_names_required': _('You cannot require specifying attendee names if you do not ask for them.')
|
|
})
|
|
return data
|
|
|
|
|
|
class PaymentSettingsForm(SettingsForm):
|
|
payment_term_days = forms.IntegerField(
|
|
label=_('Payment term in days'),
|
|
help_text=_("The number of days after placing an order the user has to pay to preserve his reservation."),
|
|
)
|
|
payment_term_last = forms.DateTimeField(
|
|
label=_('Last date of payments'),
|
|
help_text=_("The last date any payments are accepted. This has precedence over the number of "
|
|
"days configured above."),
|
|
required=False
|
|
)
|
|
payment_term_expire_automatically = forms.BooleanField(
|
|
label=_('Automatically expire unpaid orders'),
|
|
help_text=_("If checked, all unpaid orders will automatically go from 'pending' to 'expired' "
|
|
"after the end of their payment deadline. This means that those tickets go back to "
|
|
"the pool and can be ordered by other people."),
|
|
required=False
|
|
)
|
|
payment_term_accept_late = forms.BooleanField(
|
|
label=_('Accept late payments'),
|
|
help_text=_("Accept payments for orders even when they are in 'expired' state as long as enough "
|
|
"capacity is available. No payments will ever be accepted after the 'Last date of payments' "
|
|
"configured above."),
|
|
required=False
|
|
)
|
|
tax_rate_default = forms.DecimalField(
|
|
label=_('Tax rate for payment fees'),
|
|
help_text=_("The tax rate that applies for additional fees you configured for single payment methods "
|
|
"(in percent)."),
|
|
)
|
|
|
|
|
|
class ProviderForm(SettingsForm):
|
|
"""
|
|
This is a SettingsForm, but if fields are set to required=True, validation
|
|
errors are only raised if the payment method is enabled.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.settingspref = kwargs.pop('settingspref')
|
|
super().__init__(*args, **kwargs)
|
|
|
|
def prepare_fields(self):
|
|
for k, v in self.fields.items():
|
|
v._required = v.required
|
|
v.required = False
|
|
v.widget.is_required = False
|
|
|
|
def clean(self):
|
|
cleaned_data = super().clean()
|
|
enabled = cleaned_data.get(self.settingspref + '_enabled') == 'True'
|
|
if not enabled:
|
|
return
|
|
for k, v in self.fields.items():
|
|
val = cleaned_data.get(k)
|
|
if v._required and (val is None or val == ""):
|
|
self.add_error(k, _('This field is required.'))
|
|
|
|
|
|
class InvoiceSettingsForm(SettingsForm):
|
|
invoice_address_asked = forms.BooleanField(
|
|
label=_("Ask for invoice address"),
|
|
required=False
|
|
)
|
|
invoice_address_required = forms.BooleanField(
|
|
label=_("Require invoice address"),
|
|
required=False
|
|
)
|
|
invoice_address_vatid = forms.BooleanField(
|
|
label=_("Ask for VAT ID"),
|
|
help_text=_("Does only work if an invoice address is asked for. VAT ID is not required."),
|
|
required=False
|
|
)
|
|
invoice_generate = forms.ChoiceField(
|
|
label=_("Generate invoices"),
|
|
required=False,
|
|
choices=(
|
|
('False', _('No')),
|
|
('admin', _('Manually in admin panel')),
|
|
('user', _('Automatically on user request')),
|
|
('True', _('Automatically for all created orders'))
|
|
)
|
|
)
|
|
invoice_address_from = forms.CharField(
|
|
widget=forms.Textarea(attrs={'rows': 5}), required=False,
|
|
label=_("Your address"),
|
|
help_text=_("Will be printed as the sender on invoices. Be sure to include relevant details required in "
|
|
"your jurisdiction (e.g. your VAT ID).")
|
|
)
|
|
invoice_additional_text = forms.CharField(
|
|
widget=forms.Textarea(attrs={'rows': 5}), required=False,
|
|
label=_("Additional text"),
|
|
help_text=_("Will be printed on every invoice below the invoice total.")
|
|
)
|
|
invoice_language = forms.ChoiceField(
|
|
widget=forms.Select, required=True,
|
|
label=_("Invoice language"),
|
|
choices=[('__user__', _('The user\'s language'))] + settings.LANGUAGES,
|
|
)
|
|
|
|
|
|
class MailSettingsForm(SettingsForm):
|
|
mail_prefix = forms.CharField(
|
|
label=_("Subject prefix"),
|
|
help_text=_("This will be prepended to the subject of all outgoing emails. This could be a short form of "
|
|
"your event name."),
|
|
required=False
|
|
)
|
|
mail_from = forms.EmailField(
|
|
label=_("Sender address"),
|
|
help_text=_("Sender address for outgoing emails")
|
|
)
|
|
mail_text_order_placed = I18nFormField(
|
|
label=_("Placed order"),
|
|
required=False,
|
|
widget=I18nTextarea,
|
|
help_text=_("Available placeholders: {event}, {total}, {currency}, {date}, {paymentinfo}, {url}")
|
|
)
|
|
mail_text_order_paid = I18nFormField(
|
|
label=_("Paid order"),
|
|
required=False,
|
|
widget=I18nTextarea,
|
|
help_text=_("Available placeholders: {event}, {url}")
|
|
)
|
|
mail_text_resend_link = I18nFormField(
|
|
label=_("Resend link"),
|
|
required=False,
|
|
widget=I18nTextarea,
|
|
help_text=_("Available placeholders: {event}, {url}")
|
|
)
|
|
smtp_use_custom = forms.BooleanField(
|
|
label=_("Use custom SMTP server"),
|
|
help_text=_("All mail related to your event will be sent over the smtp server specified by you."),
|
|
required=False
|
|
)
|
|
smtp_host = forms.CharField(
|
|
label=_("Hostname"),
|
|
required=False
|
|
)
|
|
smtp_port = forms.IntegerField(
|
|
label=_("Port"),
|
|
required=False
|
|
)
|
|
smtp_username = forms.CharField(
|
|
label=_("Username"),
|
|
required=False
|
|
)
|
|
smtp_password = forms.CharField(
|
|
label=_("Password"),
|
|
required=False,
|
|
widget=forms.PasswordInput(attrs={
|
|
'autocomplete': 'new-password' # see https://bugs.chromium.org/p/chromium/issues/detail?id=370363#c7
|
|
}),
|
|
)
|
|
smtp_use_tls = forms.BooleanField(
|
|
label=_("Use STARTTLS"),
|
|
help_text=_("Commonly enabled on port 587."),
|
|
required=False
|
|
)
|
|
smtp_use_ssl = forms.BooleanField(
|
|
label=_("Use SSL"),
|
|
help_text=_("Commonly enabled on port 465."),
|
|
required=False
|
|
)
|
|
|
|
def clean(self):
|
|
data = self.cleaned_data
|
|
if not data.get('smtp_password') and data.get('smtp_username'):
|
|
# Leave password unchanged if the username is set and the password field is empty.
|
|
# This makes it impossible to set an empty password as long as a username is set, but
|
|
# Python's smtplib does not support password-less schemes anyway.
|
|
data['smtp_password'] = self.initial.get('smtp_password')
|
|
|
|
if data.get('smtp_use_tls') and data.get('smtp_use_ssl'):
|
|
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.'))
|
|
]
|
|
)
|
|
logo_image = ExtFileField(
|
|
label=_('Logo image'),
|
|
ext_whitelist=(".png", ".jpg", ".svg", ".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.')
|
|
)
|
|
|
|
|
|
class TicketSettingsForm(SettingsForm):
|
|
ticket_download = forms.BooleanField(
|
|
label=_("Use feature"),
|
|
help_text=_("Use pretix to generate tickets for the user to download and print out."),
|
|
required=False
|
|
)
|
|
ticket_download_date = forms.DateTimeField(
|
|
label=_("Download date"),
|
|
help_text=_("Ticket download will be offered after this date."),
|
|
required=True
|
|
)
|
|
|
|
def prepare_fields(self):
|
|
# See clean()
|
|
for k, v in self.fields.items():
|
|
v._required = v.required
|
|
v.required = False
|
|
v.widget.is_required = False
|
|
|
|
def clean(self):
|
|
# required=True files should only be required if the feature is enabled
|
|
cleaned_data = super().clean()
|
|
enabled = cleaned_data.get('ticket_download') == 'True'
|
|
if not enabled:
|
|
return
|
|
for k, v in self.fields.items():
|
|
val = cleaned_data.get(k)
|
|
if v._required and (val is None or val == ""):
|
|
self.add_error(k, _('This field is required.'))
|