Files
pretix_original/src/pretix/base/forms/widgets.py
2024-08-23 14:41:25 +02:00

263 lines
10 KiB
Python

#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-2021 rami.io GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Felix Schäfer
#
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
import os
from datetime import datetime
from django import forms
from django.utils.formats import get_format
from django.utils.functional import lazy
from django.utils.html import escape
from django.utils.timezone import get_current_timezone, now
from django.utils.translation import gettext_lazy as _
def replace_arabic_numbers(inp):
if not isinstance(inp, str):
return inp
table = {
1632: 48, # 0
1633: 49, # 1
1634: 50, # 2
1635: 51, # 3
1636: 52, # 4
1637: 53, # 5
1638: 54, # 6
1639: 55, # 7
1640: 56, # 8
1641: 57, # 9
}
return inp.translate(table)
def format_placeholders_help_text(placeholders, event=None):
placeholders = [(k, v.render_sample(event) if event else v) for k, v in placeholders.items()]
placeholders.sort(key=lambda x: x[0])
phs = [
'<button type="button" class="content-placeholder" title="%s">{%s}</button>' % (escape(_("Sample: %s") % v) if v else "", escape(k))
for k, v in placeholders
]
return _('Available placeholders: {list}').format(
list=' '.join(phs)
)
class DatePickerWidget(forms.DateInput):
def __init__(self, attrs=None, date_format=None):
attrs = attrs or {}
if 'placeholder' in attrs:
del attrs['placeholder']
date_attrs = dict(attrs)
date_attrs.setdefault('class', 'form-control')
date_attrs['class'] += ' datepickerfield'
date_attrs['autocomplete'] = 'off'
def placeholder():
df = date_format or get_format('DATE_INPUT_FORMATS')[0]
return now().replace(
year=2000, month=12, day=31, hour=18, minute=0, second=0, microsecond=0
).strftime(df)
date_attrs['placeholder'] = lazy(placeholder, str)
forms.DateInput.__init__(self, date_attrs, date_format)
def value_from_datadict(self, data, files, name):
v = super().value_from_datadict(data, files, name)
return replace_arabic_numbers(v)
class TimePickerWidget(forms.TimeInput):
def __init__(self, attrs=None, time_format=None):
attrs = attrs or {}
if 'placeholder' in attrs:
del attrs['placeholder']
time_attrs = dict(attrs)
time_attrs.setdefault('class', 'form-control')
time_attrs['class'] += ' timepickerfield'
time_attrs['autocomplete'] = 'off'
def placeholder():
tf = time_format or get_format('TIME_INPUT_FORMATS')[0]
return now().replace(
year=2000, month=1, day=1, hour=0, minute=0, second=0, microsecond=0
).strftime(tf)
time_attrs['placeholder'] = lazy(placeholder, str)
forms.TimeInput.__init__(self, time_attrs, time_format)
def value_from_datadict(self, data, files, name):
v = super().value_from_datadict(data, files, name)
return replace_arabic_numbers(v)
class UploadedFileWidget(forms.ClearableFileInput):
def __init__(self, *args, **kwargs):
self.position = kwargs.pop('position')
self.event = kwargs.pop('event')
self.answer = kwargs.pop('answer')
super().__init__(*args, **kwargs)
class FakeFile:
def __init__(self, file, position, event, answer):
self.file = file
self.position = position
self.event = event
self.answer = answer
def __str__(self):
return os.path.basename(self.file.name).split('.', 1)[-1]
@property
def url(self):
from pretix.base.models import OrderPosition
from pretix.multidomain.urlreverse import eventreverse
if isinstance(self.position, OrderPosition):
return eventreverse(self.event, 'presale:event.order.download.answer', kwargs={
'order': self.position.order.code,
'secret': self.position.order.secret,
'answer': self.answer.pk,
})
else:
return eventreverse(self.event, 'presale:event.cart.download.answer', kwargs={
'answer': self.answer.pk,
})
def get_context(self, name, value, attrs):
# Browsers can't recognize that the server already has a file uploaded
# Don't mark this input as being required if we already have an answer
# (this needs to be done via the attrs, otherwise we wouldn't get the "required" star on the field label)
ctx = super().get_context(name, value, attrs)
if ctx['widget']['is_initial']:
ctx['widget']['attrs']['required'] = False
return ctx
def format_value(self, value):
if self.is_initial(value):
return self.FakeFile(value, self.position, self.event, self.answer)
class SplitDateTimePickerWidget(forms.SplitDateTimeWidget):
template_name = 'pretixbase/forms/widgets/splitdatetime.html'
def __init__(self, attrs=None, date_format=None, time_format=None, min_date=None, max_date=None):
attrs = attrs or {}
if 'placeholder' in attrs:
del attrs['placeholder']
date_attrs = dict(attrs)
time_attrs = dict(attrs)
date_attrs.setdefault('class', 'form-control splitdatetimepart')
time_attrs.setdefault('class', 'form-control splitdatetimepart')
date_attrs.setdefault('autocomplete', 'off')
time_attrs.setdefault('autocomplete', 'off')
date_attrs['class'] += ' datepickerfield'
time_attrs['class'] += ' timepickerfield'
date_attrs['autocomplete'] = 'off'
time_attrs['autocomplete'] = 'off'
if min_date:
date_attrs['data-min'] = (
min_date if not isinstance(min_date, datetime) else min_date.astimezone(get_current_timezone()).date()
).isoformat()
if max_date:
date_attrs['data-max'] = (
max_date if not isinstance(max_date, datetime) else max_date.astimezone(get_current_timezone()).date()
).isoformat()
def date_placeholder():
df = date_format or get_format('DATE_INPUT_FORMATS')[0]
return now().replace(
year=2000, month=12, day=31, hour=18, minute=0, second=0, microsecond=0
).strftime(df)
def time_placeholder():
tf = time_format or get_format('TIME_INPUT_FORMATS')[0]
return now().replace(
year=2000, month=1, day=1, hour=0, minute=0, second=0, microsecond=0
).strftime(tf)
date_attrs['placeholder'] = lazy(date_placeholder, str)
time_attrs['placeholder'] = lazy(time_placeholder, str)
date_attrs['aria-label'] = _('Date')
time_attrs['aria-label'] = _('Time')
if 'aria-label' in attrs:
del attrs['aria-label']
widgets = (
forms.DateInput(attrs=date_attrs, format=date_format),
forms.TimeInput(attrs=time_attrs, format=time_format),
)
# Skip one hierarchy level
forms.MultiWidget.__init__(self, widgets, attrs)
def value_from_datadict(self, data, files, name):
v = super().value_from_datadict(data, files, name)
return [replace_arabic_numbers(i) for i in v]
class BusinessBooleanRadio(forms.RadioSelect):
def __init__(self, require_business=False, attrs=None):
self.require_business = require_business
if self.require_business:
choices = (
('business', _('Business or institutional customer')),
)
else:
choices = (
('individual', _('Individual customer')),
('business', _('Business or institutional customer')),
)
super().__init__(attrs, choices)
def format_value(self, value):
if self.require_business:
return 'business'
try:
return {True: 'business', False: 'individual'}[value]
except KeyError:
return 'individual'
def value_from_datadict(self, data, files, name):
value = data.get(name)
if self.require_business:
return True
return {
'business': True,
True: True,
'True': True,
'individual': False,
'False': False,
False: False,
}.get(value)