forked from CGM_Public/pretix_original
Compare commits
3 Commits
walletdete
...
v2023.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
669f622ce6 | ||
|
|
d166c7ee68 | ||
|
|
0282cfbdd5 |
@@ -32,8 +32,6 @@ as well as the type of underlying hardware. Example:
|
||||
"token": "kpp4jn8g2ynzonp6",
|
||||
"hardware_brand": "Samsung",
|
||||
"hardware_model": "Galaxy S",
|
||||
"os_name": "Android",
|
||||
"os_version": "2.3.6",
|
||||
"software_brand": "pretixdroid",
|
||||
"software_version": "4.0.0"
|
||||
}
|
||||
@@ -100,8 +98,6 @@ following endpoint:
|
||||
{
|
||||
"hardware_brand": "Samsung",
|
||||
"hardware_model": "Galaxy S",
|
||||
"os_name": "Android",
|
||||
"os_version": "2.3.6",
|
||||
"software_brand": "pretixdroid",
|
||||
"software_version": "4.1.0",
|
||||
"info": {"arbitrary": "data"}
|
||||
|
||||
@@ -24,8 +24,6 @@ all_events boolean Whether this de
|
||||
limit_events list List of event slugs this device has access to
|
||||
hardware_brand string Device hardware manufacturer (read-only)
|
||||
hardware_model string Device hardware model (read-only)
|
||||
os_name string Device operating system name (read-only)
|
||||
os_version string Device operating system version (read-only)
|
||||
software_brand string Device software product (read-only)
|
||||
software_version string Device software version (read-only)
|
||||
created datetime Creation time
|
||||
@@ -78,8 +76,6 @@ Device endpoints
|
||||
"security_profile": "full",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"os_name": "Android",
|
||||
"os_version": "8.1.0",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1"
|
||||
}
|
||||
@@ -127,8 +123,6 @@ Device endpoints
|
||||
"security_profile": "full",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"os_name": "Android",
|
||||
"os_version": "8.1.0",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1"
|
||||
}
|
||||
@@ -179,8 +173,6 @@ Device endpoints
|
||||
"initialized": null
|
||||
"hardware_brand": null,
|
||||
"hardware_model": null,
|
||||
"os_name": null,
|
||||
"os_version": null,
|
||||
"software_brand": null,
|
||||
"software_version": null
|
||||
}
|
||||
|
||||
@@ -70,8 +70,6 @@ The provider class
|
||||
|
||||
.. autoattribute:: settings_form_fields
|
||||
|
||||
.. autoattribute:: walletqueries
|
||||
|
||||
.. automethod:: settings_form_clean
|
||||
|
||||
.. automethod:: settings_content_render
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "2023.7.0.dev0"
|
||||
__version__ = "2023.6.1"
|
||||
|
||||
@@ -728,7 +728,6 @@ class EventSettingsSerializer(SettingsSerializer):
|
||||
'payment_term_minutes',
|
||||
'payment_term_last',
|
||||
'payment_term_expire_automatically',
|
||||
'payment_term_expire_delay_days',
|
||||
'payment_term_accept_late',
|
||||
'payment_explanation',
|
||||
'payment_pending_hidden',
|
||||
|
||||
@@ -251,8 +251,6 @@ class DeviceSerializer(serializers.ModelSerializer):
|
||||
unique_serial = serializers.CharField(read_only=True)
|
||||
hardware_brand = serializers.CharField(read_only=True)
|
||||
hardware_model = serializers.CharField(read_only=True)
|
||||
os_name = serializers.CharField(read_only=True)
|
||||
os_version = serializers.CharField(read_only=True)
|
||||
software_brand = serializers.CharField(read_only=True)
|
||||
software_version = serializers.CharField(read_only=True)
|
||||
created = serializers.DateTimeField(read_only=True)
|
||||
@@ -265,7 +263,7 @@ class DeviceSerializer(serializers.ModelSerializer):
|
||||
fields = (
|
||||
'device_id', 'unique_serial', 'initialization_token', 'all_events', 'limit_events',
|
||||
'revoked', 'name', 'created', 'initialized', 'hardware_brand', 'hardware_model',
|
||||
'os_name', 'os_version', 'software_brand', 'software_version', 'security_profile'
|
||||
'software_brand', 'software_version', 'security_profile'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ class InitializationRequestSerializer(serializers.Serializer):
|
||||
token = serializers.CharField(max_length=190)
|
||||
hardware_brand = serializers.CharField(max_length=190)
|
||||
hardware_model = serializers.CharField(max_length=190)
|
||||
os_name = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
os_version = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
software_brand = serializers.CharField(max_length=190)
|
||||
software_version = serializers.CharField(max_length=190)
|
||||
info = serializers.JSONField(required=False, allow_null=True)
|
||||
@@ -52,8 +50,6 @@ class InitializationRequestSerializer(serializers.Serializer):
|
||||
class UpdateRequestSerializer(serializers.Serializer):
|
||||
hardware_brand = serializers.CharField(max_length=190)
|
||||
hardware_model = serializers.CharField(max_length=190)
|
||||
os_name = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
os_version = serializers.CharField(max_length=190, required=False, allow_null=True)
|
||||
software_brand = serializers.CharField(max_length=190)
|
||||
software_version = serializers.CharField(max_length=190)
|
||||
info = serializers.JSONField(required=False, allow_null=True)
|
||||
@@ -103,8 +99,6 @@ class InitializeView(APIView):
|
||||
device.initialized = now()
|
||||
device.hardware_brand = serializer.validated_data.get('hardware_brand')
|
||||
device.hardware_model = serializer.validated_data.get('hardware_model')
|
||||
device.os_name = serializer.validated_data.get('os_name')
|
||||
device.os_version = serializer.validated_data.get('os_version')
|
||||
device.software_brand = serializer.validated_data.get('software_brand')
|
||||
device.software_version = serializer.validated_data.get('software_version')
|
||||
device.info = serializer.validated_data.get('info')
|
||||
@@ -126,8 +120,6 @@ class UpdateView(APIView):
|
||||
device = request.auth
|
||||
device.hardware_brand = serializer.validated_data.get('hardware_brand')
|
||||
device.hardware_model = serializer.validated_data.get('hardware_model')
|
||||
device.os_name = serializer.validated_data.get('os_name')
|
||||
device.os_version = serializer.validated_data.get('os_version')
|
||||
device.software_brand = serializer.validated_data.get('software_brand')
|
||||
device.software_version = serializer.validated_data.get('software_version')
|
||||
device.info = serializer.validated_data.get('info')
|
||||
|
||||
@@ -249,7 +249,7 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
|
||||
h = {
|
||||
'default-src': ["{static}"],
|
||||
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com', 'https://pay.google.com'],
|
||||
'script-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
||||
'object-src': ["'none'"],
|
||||
'frame-src': ['{static}', 'https://checkout.stripe.com', 'https://js.stripe.com'],
|
||||
'style-src': ["{static}", "{media}"],
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# Generated by Django 4.1.9 on 2023-06-26 10:59
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0242_auto_20230512_1008'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='os_name',
|
||||
field=models.CharField(max_length=190, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='device',
|
||||
name='os_version',
|
||||
field=models.CharField(max_length=190, null=True),
|
||||
),
|
||||
]
|
||||
@@ -143,14 +143,6 @@ class Device(LoggedModel):
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
os_name = models.CharField(
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
os_version = models.CharField(
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
)
|
||||
software_brand = models.CharField(
|
||||
max_length=190,
|
||||
null=True, blank=True
|
||||
|
||||
@@ -896,28 +896,6 @@ class Order(LockModel, LoggedModel):
|
||||
), tz)
|
||||
return term_last
|
||||
|
||||
@property
|
||||
def payment_term_expire_date(self):
|
||||
delay = self.event.settings.get('payment_term_expire_delay_days', as_type=int)
|
||||
if not delay: # performance saver + backwards compatibility
|
||||
return self.expires
|
||||
|
||||
term_last = self.payment_term_last
|
||||
if term_last and self.expires > term_last: # backwards compatibility
|
||||
return self.expires
|
||||
|
||||
expires = self.expires.date() + timedelta(days=delay)
|
||||
|
||||
tz = ZoneInfo(self.event.settings.timezone)
|
||||
expires = make_aware(datetime.combine(
|
||||
expires,
|
||||
time(hour=23, minute=59, second=59)
|
||||
), tz)
|
||||
if term_last:
|
||||
return min(expires, term_last)
|
||||
else:
|
||||
return expires
|
||||
|
||||
def _can_be_paid(self, count_waitinglist=True, ignore_date=False, force=False) -> Union[bool, str]:
|
||||
error_messages = {
|
||||
'late_lastdate': _("The payment can not be accepted as the last date of payments configured in the "
|
||||
|
||||
@@ -830,12 +830,13 @@ class QuestionColumn(ImportColumn):
|
||||
class CustomerColumn(ImportColumn):
|
||||
identifier = 'customer'
|
||||
verbose_name = gettext_lazy('Customer')
|
||||
default_value = None
|
||||
|
||||
def clean(self, value, previous_values):
|
||||
if value:
|
||||
try:
|
||||
value = self.event.organizer.customers.get(
|
||||
Q(identifier=value) | Q(email__iexact=value) | Q(external_identifier=value)
|
||||
Q(identifier=value) | Q(email=value) | Q(external_identifier=value)
|
||||
)
|
||||
except Customer.MultipleObjectsReturned:
|
||||
value = self.event.organizer.customers.get(
|
||||
|
||||
@@ -78,16 +78,6 @@ from pretix.presale.views.cart import cart_session, get_or_create_cart_id
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class WalletQueries:
|
||||
APPLEPAY = 'applepay'
|
||||
GOOGLEPAY = 'googlepay'
|
||||
|
||||
WALLETS = (
|
||||
(APPLEPAY, pgettext_lazy('payment', 'Apple Pay')),
|
||||
(GOOGLEPAY, pgettext_lazy('payment', 'Google Pay')),
|
||||
)
|
||||
|
||||
|
||||
class PaymentProviderForm(Form):
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
@@ -446,19 +436,6 @@ class BasePaymentProvider:
|
||||
d['_restrict_to_sales_channels']._as_type = list
|
||||
return d
|
||||
|
||||
@property
|
||||
def walletqueries(self):
|
||||
"""
|
||||
.. warning:: This property is considered **experimental**. It might change or get removed at any time without
|
||||
prior notice.
|
||||
|
||||
A list of wallet payment methods that should be dynamically joined to the public name of the payment method,
|
||||
if they are available to the user.
|
||||
The detection is made on a best effort basis with no guarantees of correctness and actual availability.
|
||||
Wallets that pretix can check for are exposed through ``pretix.base.payment.WalletQueries``.
|
||||
"""
|
||||
return []
|
||||
|
||||
def settings_form_clean(self, cleaned_data):
|
||||
"""
|
||||
Overriding this method allows you to inject custom validation into the settings form.
|
||||
|
||||
@@ -939,7 +939,7 @@ class Renderer:
|
||||
|
||||
# reportlab does not support unicode combination characters
|
||||
# It's important we do this before we use ArabicReshaper
|
||||
text = unicodedata.normalize("NFC", text)
|
||||
text = unicodedata.normalize("NFKC", text)
|
||||
|
||||
# reportlab does not support RTL, ligature-heavy scripts like Arabic. Therefore, we use ArabicReshaper
|
||||
# to resolve all ligatures and python-bidi to switch RTL texts.
|
||||
|
||||
@@ -1270,12 +1270,12 @@ def expire_orders(sender, **kwargs):
|
||||
Exists(
|
||||
OrderFee.objects.filter(order_id=OuterRef('pk'), fee_type=OrderFee.FEE_TYPE_CANCELLATION)
|
||||
)
|
||||
).prefetch_related('event').order_by('event_id')
|
||||
).select_related('event').order_by('event_id')
|
||||
for o in qs:
|
||||
if o.event_id != event_id:
|
||||
expire = o.event.settings.get('payment_term_expire_automatically', as_type=bool)
|
||||
event_id = o.event_id
|
||||
if expire and now() >= o.payment_term_expire_date:
|
||||
if expire:
|
||||
mark_order_expired(o)
|
||||
|
||||
|
||||
|
||||
@@ -893,28 +893,6 @@ DEFAULTS = {
|
||||
"the pool and can be ordered by other people."),
|
||||
)
|
||||
},
|
||||
'payment_term_expire_delay_days': {
|
||||
'default': '0',
|
||||
'type': int,
|
||||
'form_class': forms.IntegerField,
|
||||
'serializer_class': serializers.IntegerField,
|
||||
'form_kwargs': dict(
|
||||
label=_('Expiration delay'),
|
||||
help_text=_("The order will only actually expire this many days after the expiration date communicated "
|
||||
"to the customer. However, this will not delay beyond the \"last date of payments\" "
|
||||
"configured above, which is always enforced. The delay may also end on a weekend regardless "
|
||||
"of the other settings above."),
|
||||
# Every order in between the official expiry date and the delayed expiry date has a performance penalty
|
||||
# for the cron job, so we limit this feature to 30 days to prevent arbitrary numbers of orders needing
|
||||
# to be checked.
|
||||
min_value=0,
|
||||
max_value=30,
|
||||
),
|
||||
'serializer_kwargs': dict(
|
||||
min_value=0,
|
||||
max_value=30,
|
||||
),
|
||||
},
|
||||
'payment_pending_hidden': {
|
||||
'default': 'False',
|
||||
'type': bool,
|
||||
|
||||
@@ -48,8 +48,6 @@ from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.safestring import mark_safe
|
||||
from markdown import Extension
|
||||
from markdown.inlinepatterns import SubstituteTagInlineProcessor
|
||||
from markdown.postprocessors import Postprocessor
|
||||
from markdown.treeprocessors import UnescapeTreeprocessor
|
||||
from tlds import tld_set
|
||||
|
||||
register = template.Library()
|
||||
@@ -110,8 +108,6 @@ URL_RE = SimpleLazyObject(lambda: build_url_re(tlds=sorted(tld_set, key=len, rev
|
||||
|
||||
EMAIL_RE = SimpleLazyObject(lambda: build_email_re(tlds=sorted(tld_set, key=len, reverse=True)))
|
||||
|
||||
DOT_ESCAPE = "|escaped-dot-sGnY9LMK|"
|
||||
|
||||
|
||||
def safelink_callback(attrs, new=False):
|
||||
"""
|
||||
@@ -189,109 +185,6 @@ class EmailNl2BrExtension(Extension):
|
||||
md.inlinePatterns.register(br_tag, 'nl', 5)
|
||||
|
||||
|
||||
class LinkifyPostprocessor(Postprocessor):
|
||||
def __init__(self, linker):
|
||||
self.linker = linker
|
||||
super().__init__()
|
||||
|
||||
def run(self, text):
|
||||
return self.linker.linkify(text)
|
||||
|
||||
|
||||
class CleanPostprocessor(Postprocessor):
|
||||
def __init__(self, tags, attributes, protocols, strip):
|
||||
self.tags = tags
|
||||
self.attributes = attributes
|
||||
self.protocols = protocols
|
||||
self.strip = strip
|
||||
super().__init__()
|
||||
|
||||
def run(self, text):
|
||||
return bleach.clean(
|
||||
text,
|
||||
tags=self.tags,
|
||||
attributes=self.attributes,
|
||||
protocols=self.protocols,
|
||||
strip=self.strip
|
||||
)
|
||||
|
||||
|
||||
class CustomUnescapeTreeprocessor(UnescapeTreeprocessor):
|
||||
"""
|
||||
This un-escapes everything except \\.
|
||||
"""
|
||||
|
||||
def _unescape(self, m):
|
||||
if m.group(1) == "46": # 46 is the ASCII position of .
|
||||
return DOT_ESCAPE
|
||||
return chr(int(m.group(1)))
|
||||
|
||||
|
||||
class CustomUnescapePostprocessor(Postprocessor):
|
||||
"""
|
||||
Restore escaped .
|
||||
"""
|
||||
|
||||
def run(self, text):
|
||||
return text.replace(DOT_ESCAPE, ".")
|
||||
|
||||
|
||||
class LinkifyAndCleanExtension(Extension):
|
||||
r"""
|
||||
We want to do:
|
||||
|
||||
input --> markdown --> bleach clean --> linkify --> output
|
||||
|
||||
Internally, the markdown library does:
|
||||
|
||||
source --> parse --> (tree|inline)processors --> serializing --> postprocessors
|
||||
|
||||
All escaped characters such as \. will be turned to something like <STX>46<ETX> in the processors
|
||||
step and then will be converted to . back again in the last tree processor, before serialization.
|
||||
Therefore, linkify does not see the escaped character anymore. This is annoying for the one case
|
||||
where you want to type "rich_text.py" and *not* have it turned into a link, since you can't type
|
||||
"rich_text\.py" either.
|
||||
|
||||
A simple solution would be to run linkify before markdown, but that may cause other issues when
|
||||
linkify messes with the markdown syntax and it makes handling our attributes etc. harder.
|
||||
|
||||
So we do a weird hack where we modify the unescape processor to unescape everything EXCEPT for the
|
||||
dot and then unescape that one manually after linkify. However, to make things even harder, the bleach
|
||||
clean step removes any invisible characters, so we need to cheat a bit more.
|
||||
"""
|
||||
|
||||
def __init__(self, linker, tags, attributes, protocols, strip):
|
||||
self.linker = linker
|
||||
self.tags = tags
|
||||
self.attributes = attributes
|
||||
self.protocols = protocols
|
||||
self.strip = strip
|
||||
super().__init__()
|
||||
|
||||
def extendMarkdown(self, md):
|
||||
md.treeprocessors.deregister('unescape')
|
||||
md.treeprocessors.register(
|
||||
CustomUnescapeTreeprocessor(md),
|
||||
'unescape',
|
||||
0
|
||||
)
|
||||
md.postprocessors.register(
|
||||
CleanPostprocessor(self.tags, self.attributes, self.protocols, self.strip),
|
||||
'clean',
|
||||
2
|
||||
)
|
||||
md.postprocessors.register(
|
||||
LinkifyPostprocessor(self.linker),
|
||||
'linkify',
|
||||
1
|
||||
)
|
||||
md.postprocessors.register(
|
||||
CustomUnescapePostprocessor(self.linker),
|
||||
'unescape_dot',
|
||||
0
|
||||
)
|
||||
|
||||
|
||||
def markdown_compile_email(source):
|
||||
linker = bleach.Linker(
|
||||
url_re=URL_RE,
|
||||
@@ -299,20 +192,18 @@ def markdown_compile_email(source):
|
||||
callbacks=DEFAULT_CALLBACKS + [truelink_callback, abslink_callback],
|
||||
parse_email=True
|
||||
)
|
||||
return markdown.markdown(
|
||||
source,
|
||||
extensions=[
|
||||
'markdown.extensions.sane_lists',
|
||||
EmailNl2BrExtension(),
|
||||
LinkifyAndCleanExtension(
|
||||
linker,
|
||||
tags=ALLOWED_TAGS,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
strip=False,
|
||||
)
|
||||
]
|
||||
)
|
||||
return linker.linkify(bleach.clean(
|
||||
markdown.markdown(
|
||||
source,
|
||||
extensions=[
|
||||
'markdown.extensions.sane_lists',
|
||||
EmailNl2BrExtension(),
|
||||
]
|
||||
),
|
||||
tags=ALLOWED_TAGS,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
))
|
||||
|
||||
|
||||
class SnippetExtension(markdown.extensions.Extension):
|
||||
@@ -322,24 +213,23 @@ class SnippetExtension(markdown.extensions.Extension):
|
||||
md.parser.blockprocessors.deregister('quote')
|
||||
|
||||
|
||||
def markdown_compile(source, linker, snippet=False):
|
||||
def markdown_compile(source, snippet=False):
|
||||
tags = ALLOWED_TAGS_SNIPPET if snippet else ALLOWED_TAGS
|
||||
exts = [
|
||||
'markdown.extensions.sane_lists',
|
||||
'markdown.extensions.nl2br',
|
||||
LinkifyAndCleanExtension(
|
||||
linker,
|
||||
tags=tags,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
strip=snippet,
|
||||
)
|
||||
'markdown.extensions.nl2br'
|
||||
]
|
||||
if snippet:
|
||||
exts.append(SnippetExtension())
|
||||
return markdown.markdown(
|
||||
source,
|
||||
extensions=exts
|
||||
return bleach.clean(
|
||||
markdown.markdown(
|
||||
source,
|
||||
extensions=exts
|
||||
),
|
||||
strip=snippet,
|
||||
tags=tags,
|
||||
attributes=ALLOWED_ATTRIBUTES,
|
||||
protocols=ALLOWED_PROTOCOLS,
|
||||
)
|
||||
|
||||
|
||||
@@ -355,7 +245,7 @@ def rich_text(text: str, **kwargs):
|
||||
callbacks=DEFAULT_CALLBACKS + ([truelink_callback, safelink_callback] if kwargs.get('safelinks', True) else [truelink_callback, abslink_callback]),
|
||||
parse_email=True
|
||||
)
|
||||
body_md = markdown_compile(text, linker)
|
||||
body_md = linker.linkify(markdown_compile(text))
|
||||
return mark_safe(body_md)
|
||||
|
||||
|
||||
@@ -371,5 +261,5 @@ def rich_text_snippet(text: str, **kwargs):
|
||||
callbacks=DEFAULT_CALLBACKS + ([truelink_callback, safelink_callback] if kwargs.get('safelinks', True) else [truelink_callback, abslink_callback]),
|
||||
parse_email=True
|
||||
)
|
||||
body_md = markdown_compile(text, linker, snippet=True)
|
||||
body_md = linker.linkify(markdown_compile(text, snippet=True))
|
||||
return mark_safe(body_md)
|
||||
|
||||
@@ -749,7 +749,6 @@ class PaymentSettingsForm(EventSettingsValidationMixin, SettingsForm):
|
||||
'payment_term_minutes',
|
||||
'payment_term_last',
|
||||
'payment_term_expire_automatically',
|
||||
'payment_term_expire_delay_days',
|
||||
'payment_term_accept_late',
|
||||
'payment_pending_hidden',
|
||||
'payment_explanation',
|
||||
|
||||
@@ -65,8 +65,6 @@
|
||||
{% bootstrap_field form.payment_term_minutes layout="control" %}
|
||||
{% bootstrap_field form.payment_term_last layout="control" %}
|
||||
{% bootstrap_field form.payment_term_expire_automatically layout="control" %}
|
||||
{% trans "days" context "unit" as days %}
|
||||
{% bootstrap_field form.payment_term_expire_delay_days layout="control" addon_after=days %}
|
||||
{% bootstrap_field form.payment_term_accept_late layout="control" %}
|
||||
{% bootstrap_field form.payment_pending_hidden layout="control" %}
|
||||
</fieldset>
|
||||
|
||||
@@ -1918,7 +1918,7 @@ class OrderContactChange(OrderView):
|
||||
'pretix.event.order.contact.changed',
|
||||
data={
|
||||
'old_email': old_email,
|
||||
'new_email': self.form.cleaned_data.get('email'),
|
||||
'new_email': self.form.cleaned_data['email'],
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
@@ -1930,7 +1930,7 @@ class OrderContactChange(OrderView):
|
||||
'pretix.event.order.phone.changed',
|
||||
data={
|
||||
'old_phone': old_phone,
|
||||
'new_phone': self.form.cleaned_data.get('phone'),
|
||||
'new_phone': self.form.cleaned_data['phone'],
|
||||
},
|
||||
user=self.request.user,
|
||||
)
|
||||
|
||||
@@ -5,8 +5,8 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
||||
"PO-Revision-Date: 2023-06-29 05:00+0000\n"
|
||||
"Last-Translator: Moritz Lerch <dev@moritz-lerch.de>\n"
|
||||
"PO-Revision-Date: 2023-06-27 14:53+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix/de/"
|
||||
">\n"
|
||||
"Language: de\n"
|
||||
@@ -16352,7 +16352,7 @@ msgstr "Terminal-ID"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:96
|
||||
msgid "Card holder"
|
||||
msgstr "Karteninhaber*in"
|
||||
msgstr "Karteninhaber"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:100
|
||||
msgid "Card expiration"
|
||||
@@ -21206,7 +21206,7 @@ msgstr "Import durchführen"
|
||||
#: pretix/control/templates/pretixcontrol/orders/import_start.html:10
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_form.html:16
|
||||
msgid "Upload a new file"
|
||||
msgstr "Neue Datei hochladen"
|
||||
msgstr "Neuen Datei hochladen"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/import_start.html:16
|
||||
msgid ""
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
||||
"PO-Revision-Date: 2023-06-29 05:00+0000\n"
|
||||
"Last-Translator: Moritz Lerch <dev@moritz-lerch.de>\n"
|
||||
"PO-Revision-Date: 2023-06-27 14:53+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/de_Informal/>\n"
|
||||
"Language: de_Informal\n"
|
||||
@@ -16324,7 +16324,7 @@ msgstr "Terminal-ID"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:96
|
||||
msgid "Card holder"
|
||||
msgstr "Karteninhaber*in"
|
||||
msgstr "Karteninhaber"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/boxoffice/payment.html:100
|
||||
msgid "Card expiration"
|
||||
@@ -21172,7 +21172,7 @@ msgstr "Import durchführen"
|
||||
#: pretix/control/templates/pretixcontrol/orders/import_start.html:10
|
||||
#: pretix/plugins/banktransfer/templates/pretixplugins/banktransfer/import_form.html:16
|
||||
msgid "Upload a new file"
|
||||
msgstr "Neue Datei hochladen"
|
||||
msgstr "Neuen Datei hochladen"
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/orders/import_start.html:16
|
||||
msgid ""
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-06-27 12:51+0000\n"
|
||||
"PO-Revision-Date: 2023-06-28 06:00+0000\n"
|
||||
"Last-Translator: Yucheng Lin <yuchenglinedu@gmail.com>\n"
|
||||
"PO-Revision-Date: 2023-06-27 07:40+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: Chinese (Traditional) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/zh_Hant/>\n"
|
||||
"Language: zh_Hant\n"
|
||||
@@ -522,20 +522,24 @@ msgid "Test-Mode of shop has been deactivated"
|
||||
msgstr "商店的測試模式已啟動"
|
||||
|
||||
#: pretix/api/webhooks.py:339
|
||||
#, fuzzy
|
||||
msgid "Waiting list entry added"
|
||||
msgstr "候補名單條目已添加"
|
||||
msgstr "候補名單條目"
|
||||
|
||||
#: pretix/api/webhooks.py:343
|
||||
#, fuzzy
|
||||
msgid "Waiting list entry changed"
|
||||
msgstr "候補名單條目已更改"
|
||||
msgstr "候補名單條目"
|
||||
|
||||
#: pretix/api/webhooks.py:347
|
||||
#, fuzzy
|
||||
msgid "Waiting list entry deleted"
|
||||
msgstr "候補名單條目已刪除"
|
||||
msgstr "候補名單條目"
|
||||
|
||||
#: pretix/api/webhooks.py:351
|
||||
#, fuzzy
|
||||
msgid "Waiting list entry received voucher"
|
||||
msgstr "候補名單條目收到憑證"
|
||||
msgstr "候補名單條目"
|
||||
|
||||
#: pretix/base/addressvalidation.py:100 pretix/base/addressvalidation.py:103
|
||||
#: pretix/base/addressvalidation.py:108 pretix/base/forms/questions.py:938
|
||||
@@ -5343,8 +5347,9 @@ msgid "Seat {number}"
|
||||
msgstr "座位 {number}"
|
||||
|
||||
#: pretix/base/models/tax.py:157
|
||||
#, fuzzy
|
||||
msgid "Your set of rules is not valid. Error message: {}"
|
||||
msgstr "你的條例規則並非有效。錯誤訊息:{}"
|
||||
msgstr "你的樣式檔案並非有效樣式。錯誤訊息:{}"
|
||||
|
||||
#: pretix/base/models/tax.py:168 pretix/base/models/tax.py:146
|
||||
msgid "Official name"
|
||||
@@ -7637,8 +7642,9 @@ msgid "Something happened in your event after the export, please try again."
|
||||
msgstr "匯出後你的活動中發生一些事情,請重試。"
|
||||
|
||||
#: pretix/base/services/shredder.py:177
|
||||
#, fuzzy
|
||||
msgid "Data shredding completed"
|
||||
msgstr "數據粉碎完成"
|
||||
msgstr "完成抓取。"
|
||||
|
||||
#: pretix/base/services/stats.py:210
|
||||
msgid "Uncategorized"
|
||||
@@ -10714,20 +10720,6 @@ msgid ""
|
||||
"\n"
|
||||
"Your pretix team\n"
|
||||
msgstr ""
|
||||
"你好\n"
|
||||
"\n"
|
||||
"我們特此確認以下數據粉碎工作已完成:\n"
|
||||
"\n"
|
||||
"召集人:%(organizer)s\n"
|
||||
"\n"
|
||||
"事件: %(event)s\n"
|
||||
"\n"
|
||||
"數據選擇: %(shredders)s\n"
|
||||
"開始時間:%(start_time)s(在此時間之後添加的新資料可能尚未刪除)\n"
|
||||
"\n"
|
||||
"敬上\n"
|
||||
"\n"
|
||||
"你的卓越團隊\n"
|
||||
|
||||
#: pretix/base/templates/pretixbase/forms/widgets/portrait_image.html:10
|
||||
msgid "Upload photo"
|
||||
@@ -13921,11 +13913,11 @@ msgstr "活動已被刪除。"
|
||||
|
||||
#: pretix/control/logdisplay.py:375
|
||||
msgid "A removal process for personal data has been started."
|
||||
msgstr "個人數據的刪除過程已啟動。"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/control/logdisplay.py:376
|
||||
msgid "A removal process for personal data has been completed."
|
||||
msgstr "個人數據的刪除過程已完成。"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/control/logdisplay.py:377 pretix/control/logdisplay.py:375
|
||||
msgid "The order details have been changed."
|
||||
@@ -21596,8 +21588,7 @@ msgstr "確認碼"
|
||||
msgid ""
|
||||
"Depending on the amount of data in your event, the following step may take a "
|
||||
"while to complete. We will inform you via email once it has been completed."
|
||||
msgstr "根據事件中的數據量,以下步驟可能需要一段時間才能完成。完成後,我們將通過電子"
|
||||
"郵件通知你。"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/control/templates/pretixcontrol/shredder/index.html:11
|
||||
msgid ""
|
||||
|
||||
@@ -59,9 +59,7 @@ from pretix import __version__
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.forms import SecretKeySettingsField
|
||||
from pretix.base.models import Event, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.payment import (
|
||||
BasePaymentProvider, PaymentException, WalletQueries,
|
||||
)
|
||||
from pretix.base.payment import BasePaymentProvider, PaymentException
|
||||
from pretix.base.plugins import get_all_plugins
|
||||
from pretix.base.services.mail import SendMailException
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
@@ -221,20 +219,6 @@ class StripeSettingsHolder(BasePaymentProvider):
|
||||
]
|
||||
|
||||
extra_fields = [
|
||||
('walletdetection',
|
||||
forms.BooleanField(
|
||||
label=mark_safe(
|
||||
_('Check for Apple Pay/Google Pay') +
|
||||
' ' +
|
||||
'<span class="label label-info">{}</span>'.format(_('experimental'))
|
||||
),
|
||||
help_text=_("pretix will attempt to check if the customer's webbrowser supports wallet-based payment "
|
||||
"methods like Apple Pay or Google Pay and display them prominently with the credit card"
|
||||
"payment method. This detection does not take into consideration if Google Pay/Apple Pay "
|
||||
"has been disabled in the Stripe Dashboard."),
|
||||
initial=True,
|
||||
required=False,
|
||||
)),
|
||||
('postfix',
|
||||
forms.CharField(
|
||||
label=_('Statement descriptor postfix'),
|
||||
@@ -763,15 +747,6 @@ class StripeCC(StripeMethod):
|
||||
public_name = _('Credit card')
|
||||
method = 'cc'
|
||||
|
||||
@property
|
||||
def walletqueries(self):
|
||||
# ToDo: Check against Stripe API, if ApplePay and GooglePay are even activated/available
|
||||
# This is probably only really feasable once the Payment Methods Configuration API is out of beta
|
||||
# https://stripe.com/docs/connect/payment-method-configurations
|
||||
if self.settings.get("walletdetection", True, as_type=bool):
|
||||
return [WalletQueries.APPLEPAY, WalletQueries.GOOGLEPAY]
|
||||
return []
|
||||
|
||||
def payment_form_render(self, request, total) -> str:
|
||||
account = get_stripe_account_key(self)
|
||||
if not RegisteredApplePayDomain.objects.filter(account=account, domain=request.host).exists():
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
{% load money %}
|
||||
{% load bootstrap3 %}
|
||||
{% load rich_text %}
|
||||
{% block custom_header %}
|
||||
{{ block.super }}
|
||||
{% include "pretixpresale/event/fragment_walletdetection_head.html" %}
|
||||
{% endblock %}
|
||||
{% block inner %}
|
||||
{% if current_payments %}
|
||||
<p>{% trans "You already selected the following payment methods:" %}</p>
|
||||
@@ -75,8 +71,7 @@
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
id="input_payment_{{ p.provider.identifier }}"
|
||||
aria-describedby="payment_{{ p.provider.identifier }}"
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"
|
||||
data-wallets="{{ p.provider.walletqueries|join:"|" }}" />
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"/>
|
||||
<label for="input_payment_{{ p.provider.identifier }}"><strong>{{ p.provider.public_name }}</strong></label>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
{% load static %}
|
||||
{% load compress %}
|
||||
|
||||
{% compress js %}
|
||||
<script type="text/javascript" src="{% static "pretixpresale/js/walletdetection.js" %}"></script>
|
||||
{% endcompress %}
|
||||
@@ -3,10 +3,6 @@
|
||||
{% load eventurl %}
|
||||
{% load money %}
|
||||
{% block title %}{% trans "Change payment method" %}{% endblock %}
|
||||
{% block custom_header %}
|
||||
{{ block.super }}
|
||||
{% include "pretixpresale/event/fragment_walletdetection_head.html" %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<h2>
|
||||
{% blocktrans trimmed with code=order.code %}
|
||||
@@ -33,8 +29,7 @@
|
||||
<input type="radio" name="payment" value="{{ p.provider.identifier }}"
|
||||
data-parent="#payment_accordion"
|
||||
{% if selected == p.provider.identifier %}checked="checked"{% endif %}
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}"
|
||||
data-wallets="{{ p.provider.walletqueries|join:"|" }}"/>
|
||||
data-toggle="radiocollapse" data-target="#payment_{{ p.provider.identifier }}" />
|
||||
<strong>{{ p.provider.public_name }}</strong>
|
||||
</label>
|
||||
</h4>
|
||||
|
||||
@@ -350,9 +350,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
)
|
||||
pathparts = request.get_full_path().split('/')
|
||||
pathparts[1] = event.slug
|
||||
r = redirect('/'.join(pathparts))
|
||||
r['Access-Control-Allow-Origin'] = '*'
|
||||
return r
|
||||
return redirect('/'.join(pathparts))
|
||||
else:
|
||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||
event = Event.objects.select_related('organizer').get(
|
||||
@@ -362,9 +360,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
pathparts = request.get_full_path().split('/')
|
||||
pathparts[1] = event.organizer.slug
|
||||
pathparts[2] = event.slug
|
||||
r = redirect('/'.join(pathparts))
|
||||
r['Access-Control-Allow-Origin'] = '*'
|
||||
return r
|
||||
return redirect('/'.join(pathparts))
|
||||
except Event.DoesNotExist:
|
||||
raise Http404(_('The selected event was not found.'))
|
||||
raise Http404(_('The selected event was not found.'))
|
||||
@@ -378,9 +374,7 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
||||
raise Http404(_('The selected organizer was not found.'))
|
||||
pathparts = request.get_full_path().split('/')
|
||||
pathparts[1] = organizer.slug
|
||||
r = redirect('/'.join(pathparts))
|
||||
r['Access-Control-Allow-Origin'] = '*'
|
||||
return r
|
||||
return redirect('/'.join(pathparts))
|
||||
raise Http404(_('The selected organizer was not found.'))
|
||||
|
||||
request._event_detected = True
|
||||
|
||||
@@ -212,14 +212,11 @@ def price_dict(item, price):
|
||||
}
|
||||
|
||||
|
||||
def get_picture(event, picture, size=None):
|
||||
thumb = None
|
||||
if size:
|
||||
try:
|
||||
thumb = get_thumbnail(picture.name, size).thumb.url
|
||||
except:
|
||||
logger.exception(f'Failed to create thumbnail of {picture.name}')
|
||||
if not thumb:
|
||||
def get_picture(event, picture):
|
||||
try:
|
||||
thumb = get_thumbnail(picture.name, '60x60^').thumb.url
|
||||
except:
|
||||
logger.exception(f'Failed to create thumbnail of {picture.name}')
|
||||
thumb = default_storage.url(picture.name)
|
||||
return urljoin(build_absolute_uri(event, 'presale:event.index'), thumb)
|
||||
|
||||
@@ -267,8 +264,7 @@ class WidgetAPIProductList(EventListMixin, View):
|
||||
{
|
||||
'id': item.pk,
|
||||
'name': str(item.name),
|
||||
'picture': get_picture(self.request.event, item.picture, '60x60^') if item.picture else None,
|
||||
'picture_fullsize': get_picture(self.request.event, item.picture) if item.picture else None,
|
||||
'picture': get_picture(self.request.event, item.picture) if item.picture else None,
|
||||
'description': str(rich_text(item.description, safelinks=False)) if item.description else None,
|
||||
'has_variations': item.has_variations,
|
||||
'require_voucher': item.require_voucher,
|
||||
|
||||
@@ -165,13 +165,13 @@ if SITE_URL.endswith('/'):
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = [urlparse(SITE_URL).scheme + '://' + urlparse(SITE_URL).hostname]
|
||||
|
||||
TRUST_X_FORWARDED_FOR = config.get('pretix', 'trust_x_forwarded_for', fallback=False)
|
||||
USE_X_FORWARDED_HOST = config.get('pretix', 'trust_x_forwarded_host', fallback=False)
|
||||
TRUST_X_FORWARDED_FOR = config.getboolean('pretix', 'trust_x_forwarded_for', fallback=False)
|
||||
USE_X_FORWARDED_HOST = config.getboolean('pretix', 'trust_x_forwarded_host', fallback=False)
|
||||
|
||||
|
||||
REQUEST_ID_HEADER = config.get('pretix', 'request_id_header', fallback=False)
|
||||
|
||||
if config.get('pretix', 'trust_x_forwarded_proto', fallback=False):
|
||||
if config.getboolean('pretix', 'trust_x_forwarded_proto', fallback=False):
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
|
||||
PRETIX_PLUGINS_DEFAULT = config.get('pretix', 'plugins_default',
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var walletdetection = {
|
||||
applepay: async function () {
|
||||
// This is a weak check for Apple Pay - in order to do a proper check, we would need to also call
|
||||
// canMakePaymentsWithActiveCard(merchantIdentifier)
|
||||
|
||||
return !!(window.ApplePaySession && window.ApplePaySession.canMakePayments());
|
||||
},
|
||||
googlepay: async function () {
|
||||
// Checking for Google Pay is a little bit more involved, since it requires including the Google Pay JS SDK, and
|
||||
// providing a lot of information.
|
||||
// So for the time being, we only check if Google Pay is available in TEST-mode, which should hopefully give us a
|
||||
// good enough idea if Google Pay could be present on this device; even though there are still a lot of other
|
||||
// factors that could inhibit Google Pay from actually being offered to the customer.
|
||||
|
||||
return $.ajax({
|
||||
url: 'https://pay.google.com/gp/p/js/pay.js',
|
||||
dataType: 'script',
|
||||
}).then(function() {
|
||||
const paymentsClient = new google.payments.api.PaymentsClient({environment: 'TEST'});
|
||||
return paymentsClient.isReadyToPay({
|
||||
apiVersion: 2,
|
||||
apiVersionMinor: 0,
|
||||
allowedPaymentMethods: [{
|
||||
type: 'CARD',
|
||||
parameters: {
|
||||
allowedAuthMethods: ["PAN_ONLY", "CRYPTOGRAM_3DS"],
|
||||
allowedCardNetworks: ["AMEX", "DISCOVER", "INTERAC", "JCB", "MASTERCARD", "VISA"]
|
||||
}
|
||||
}],
|
||||
})
|
||||
}).then(function (response) {
|
||||
return !!response.result;
|
||||
});
|
||||
},
|
||||
name_map: {
|
||||
applepay: gettext('Apple Pay'),
|
||||
googlepay: gettext('Google Pay'),
|
||||
}
|
||||
}
|
||||
|
||||
$(function () {
|
||||
const wallets = $('[data-wallets]')
|
||||
.map(function(index, pm) {
|
||||
return pm.getAttribute("data-wallets").split("|");
|
||||
})
|
||||
.get()
|
||||
.flat()
|
||||
.filter(function(item, pos, self) {
|
||||
// filter out empty or duplicate values
|
||||
return item && self.indexOf(item) == pos;
|
||||
});
|
||||
|
||||
wallets.forEach(function(wallet) {
|
||||
const labels = $('[data-wallets*='+wallet+'] + label strong, [data-wallets*='+wallet+'] + strong')
|
||||
.append('<span class="wallet wallet-loading"> <i aria-hidden="true" class="fa fa-cog fa-spin"></i></span>')
|
||||
walletdetection[wallet]()
|
||||
.then(function(result) {
|
||||
const spans = labels.find(".wallet-loading:nth-of-type(1)");
|
||||
if (result) {
|
||||
spans.removeClass('wallet-loading').hide().text(', ' + walletdetection.name_map[wallet]).fadeIn(300);
|
||||
} else {
|
||||
spans.remove();
|
||||
}
|
||||
})
|
||||
.catch(function(result) {
|
||||
labels.find(".wallet-loading:nth-of-type(1)").remove();
|
||||
})
|
||||
});
|
||||
});
|
||||
@@ -450,7 +450,7 @@ Vue.component('item', {
|
||||
|
||||
// Product description
|
||||
+ '<div class="pretix-widget-item-info-col">'
|
||||
+ '<a :href="item.picture_fullsize" v-if="item.picture" class="pretix-widget-item-picture-link" @click.prevent.stop="lightbox"><img :src="item.picture" class="pretix-widget-item-picture"></a>'
|
||||
+ '<img :src="item.picture" v-if="item.picture" class="pretix-widget-item-picture">'
|
||||
+ '<div class="pretix-widget-item-title-and-description">'
|
||||
+ '<a v-if="item.has_variations && show_toggle" class="pretix-widget-item-title" :href="\'#\' + item.id + \'-variants\'"'
|
||||
+ ' @click.prevent.stop="expand" role="button" tabindex="0"'
|
||||
@@ -530,12 +530,6 @@ Vue.component('item', {
|
||||
methods: {
|
||||
expand: function () {
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
lightbox: function () {
|
||||
this.$root.overlay.lightbox = {
|
||||
image: this.item.picture_fullsize,
|
||||
description: this.item.name,
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -790,44 +784,12 @@ var shared_alert_fragment = (
|
||||
+ '</div>'
|
||||
);
|
||||
|
||||
var shared_lightbox_fragment = (
|
||||
'<div :class="lightboxClasses" role="dialog" aria-modal="true" v-if="$root.lightbox" @click="lightboxClose">'
|
||||
+ '<div class="pretix-widget-lightbox-loading" v-if="$root.lightbox?.loading">'
|
||||
+ '<svg width="256" height="256" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path class="pretix-widget-primary-color" d="M1152 896q0-106-75-181t-181-75-181 75-75 181 75 181 181 75 181-75 75-181zm512-109v222q0 12-8 23t-20 13l-185 28q-19 54-39 91 35 50 107 138 10 12 10 25t-9 23q-27 37-99 108t-94 71q-12 0-26-9l-138-108q-44 23-91 38-16 136-29 186-7 28-36 28h-222q-14 0-24.5-8.5t-11.5-21.5l-28-184q-49-16-90-37l-141 107q-10 9-25 9-14 0-25-11-126-114-165-168-7-10-7-23 0-12 8-23 15-21 51-66.5t54-70.5q-27-50-41-99l-183-27q-13-2-21-12.5t-8-23.5v-222q0-12 8-23t19-13l186-28q14-46 39-92-40-57-107-138-10-12-10-24 0-10 9-23 26-36 98.5-107.5t94.5-71.5q13 0 26 10l138 107q44-23 91-38 16-136 29-186 7-28 36-28h222q14 0 24.5 8.5t11.5 21.5l28 184q49 16 90 37l142-107q9-9 24-9 13 0 25 10 129 119 165 170 7 8 7 22 0 12-8 23-15 21-51 66.5t-54 70.5q26 50 41 98l183 28q13 2 21 12.5t8 23.5z"/></svg>'
|
||||
+ '</div>'
|
||||
+ '<div class="pretix-widget-lightbox-inner" @click.stop="">'
|
||||
+ '<figure class="pretix-widget-lightbox-image">'
|
||||
+ '<img :src="$root.lightbox.image" :alt="$root.lightbox.description" @load="lightboxLoaded" ref="lightboxImage">'
|
||||
+ '<figcaption v-if="$root.lightbox.description">{{$root.lightbox.description}}</figcaption>'
|
||||
+ '</figure>'
|
||||
+ '<button type="button" class="pretix-widget-lightbox-close" @click="lightboxClose" aria-label="'+strings.close+'">'
|
||||
+ '<svg height="16" viewBox="0 0 512 512" width="16" xmlns="http://www.w3.org/2000/svg"><path fill="#fff" d="M437.5,386.6L306.9,256l130.6-130.6c14.1-14.1,14.1-36.8,0-50.9c-14.1-14.1-36.8-14.1-50.9,0L256,205.1L125.4,74.5 c-14.1-14.1-36.8-14.1-50.9,0c-14.1,14.1-14.1,36.8,0,50.9L205.1,256L74.5,386.6c-14.1,14.1-14.1,36.8,0,50.9 c14.1,14.1,36.8,14.1,50.9,0L256,306.9l130.6,130.6c14.1,14.1,36.8,14.1,50.9,0C451.5,423.4,451.5,400.6,437.5,386.6z"/></svg>'
|
||||
+ '</button>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
);
|
||||
|
||||
Vue.component('pretix-overlay', {
|
||||
template: ('<div class="pretix-widget-overlay">'
|
||||
+ shared_iframe_fragment
|
||||
+ shared_alert_fragment
|
||||
+ shared_lightbox_fragment
|
||||
+ '</div>'
|
||||
),
|
||||
watch: {
|
||||
'$root.lightbox': function (newValue, oldValue) {
|
||||
if (newValue) {
|
||||
if (newValue.image != oldValue?.image) {
|
||||
this.$set(newValue, "loading", true);
|
||||
}
|
||||
if (!oldValue) {
|
||||
window.addEventListener('keyup', this.lightboxCloseOnKeyup);
|
||||
}
|
||||
} else {
|
||||
window.removeEventListener('keyup', this.lightboxCloseOnKeyup);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
frameClasses: function () {
|
||||
return {
|
||||
@@ -841,27 +803,8 @@ Vue.component('pretix-overlay', {
|
||||
'pretix-widget-alert-shown': this.$root.error_message,
|
||||
};
|
||||
},
|
||||
lightboxClasses: function () {
|
||||
return {
|
||||
'pretix-widget-lightbox-holder': true,
|
||||
'pretix-widget-lightbox-shown': this.$root.lightbox,
|
||||
'pretix-widget-lightbox-isloading': this.$root.lightbox?.loading,
|
||||
};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
lightboxCloseOnKeyup: function (event) {
|
||||
if (event.keyCode === 27) {
|
||||
// abort on ESC-key
|
||||
this.lightboxClose();
|
||||
}
|
||||
},
|
||||
lightboxClose: function () {
|
||||
this.$root.lightbox = null;
|
||||
},
|
||||
lightboxLoaded: function () {
|
||||
this.$root.lightbox.loading = false;
|
||||
},
|
||||
errorClose: function () {
|
||||
this.$root.error_message = null;
|
||||
this.$root.error_url_after = null;
|
||||
@@ -1852,7 +1795,6 @@ var create_overlay = function (app) {
|
||||
error_url_after: null,
|
||||
error_url_after_new_tab: true,
|
||||
error_message: null,
|
||||
lightbox: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -179,7 +179,3 @@
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.wallet-loading + .wallet-loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -768,102 +768,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.pretix-widget-lightbox-holder {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
z-index: 16777271;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s, visibility 0.5s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.pretix-widget-lightbox-loading svg {
|
||||
margin: 40px;
|
||||
-webkit-animation: pretix-widget-spin 6s linear infinite;
|
||||
-moz-animation: pretix-widget-spin 6s linear infinite;
|
||||
animation: pretix-widget-spin 6s linear infinite;
|
||||
}
|
||||
|
||||
&.pretix-widget-lightbox-shown {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s, visibility 0.5s;
|
||||
}
|
||||
|
||||
.pretix-widget-lightbox-inner {
|
||||
position: relative;
|
||||
background: white;
|
||||
border-radius: 5px 5px 5px 5px;
|
||||
-moz-border-radius: 5px 5px 5px 5px;
|
||||
-webkit-border-radius: 5px 5px 5px 5px;
|
||||
box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.09);
|
||||
-webkit-box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.09);
|
||||
-moz-box-shadow: 0 4px 18px 0 rgba(0, 0, 0, 0.1), 0 6px 20px 0 rgba(0, 0, 0, 0.09);
|
||||
box-sizing: border-box;
|
||||
padding: 10px;
|
||||
max-width: 90%;
|
||||
max-height: 90%;
|
||||
}
|
||||
&.pretix-widget-lightbox-isloading .pretix-widget-lightbox-inner {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0,0,0,0);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.pretix-widget-lightbox-image {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
}
|
||||
.pretix-widget-lightbox-image img {
|
||||
max-width: 80vw;
|
||||
max-height: 80vh;
|
||||
object-fit: scale-down;
|
||||
}
|
||||
.pretix-widget-lightbox-image figcaption {
|
||||
margin: 0.5em 0 0;
|
||||
}
|
||||
|
||||
.pretix-widget-lightbox-close {
|
||||
position: absolute;
|
||||
right: -12px;
|
||||
top: -12px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background: $brand-primary;
|
||||
margin: 0;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
-moz-border-radius: 12px;
|
||||
-webkit-border-radius: 12px;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-family: sans-serif;
|
||||
text-decoration: none;
|
||||
padding: 4px 0;
|
||||
display: inline-block;
|
||||
line-height: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pretix-widget-lightbox-close svg {
|
||||
display: inline-block;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pretix-widget-primary-color {
|
||||
/* in SVG */
|
||||
fill: $brand-primary;
|
||||
|
||||
@@ -94,8 +94,6 @@ def test_initialize_valid_token(client, new_device: Device):
|
||||
'hardware_brand': 'Samsung',
|
||||
'hardware_model': 'Galaxy S',
|
||||
'software_brand': 'pretixdroid',
|
||||
'os_name': 'Android',
|
||||
'os_version': '2.3.3',
|
||||
'software_version': '4.0.0'
|
||||
})
|
||||
assert resp.status_code == 200
|
||||
@@ -107,7 +105,6 @@ def test_initialize_valid_token(client, new_device: Device):
|
||||
new_device.refresh_from_db()
|
||||
assert new_device.api_token
|
||||
assert new_device.initialized
|
||||
assert new_device.os_version == "2.3.3"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -145,8 +142,6 @@ def test_update_valid_fields(device_client, device: Device):
|
||||
resp = device_client.post('/api/v1/device/update', {
|
||||
'hardware_brand': 'Samsung',
|
||||
'hardware_model': 'Galaxy S',
|
||||
'os_name': 'Android',
|
||||
'os_version': '2.3.3',
|
||||
'software_brand': 'pretixdroid',
|
||||
'software_version': '5.0.0',
|
||||
'info': {
|
||||
@@ -156,23 +151,9 @@ def test_update_valid_fields(device_client, device: Device):
|
||||
assert resp.status_code == 200
|
||||
device.refresh_from_db()
|
||||
assert device.software_version == '5.0.0'
|
||||
assert device.os_version == '2.3.3'
|
||||
assert device.info == {'foo': 'bar'}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_update_valid_without_optional_fields(device_client, device: Device):
|
||||
resp = device_client.post('/api/v1/device/update', {
|
||||
'hardware_brand': 'Samsung',
|
||||
'hardware_model': 'Galaxy S',
|
||||
'software_brand': 'pretixdroid',
|
||||
'software_version': '5.0.0',
|
||||
}, format='json')
|
||||
assert resp.status_code == 200
|
||||
device.refresh_from_db()
|
||||
assert device.software_version == '5.0.0'
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_keyroll_required_auth(client, token_client, device: Device):
|
||||
resp = client.post('/api/v1/device/roll', {})
|
||||
|
||||
@@ -35,8 +35,6 @@ def device(organizer, event):
|
||||
unique_serial="UOS3GNZ27O39V3QS",
|
||||
initialization_token="frkso3m2w58zuw70",
|
||||
hardware_model="TC25",
|
||||
os_name="Android",
|
||||
os_version="8.1.0",
|
||||
software_brand="pretixSCAN",
|
||||
software_version="1.5.1",
|
||||
initialized=now(),
|
||||
@@ -60,8 +58,6 @@ TEST_DEV_RES = {
|
||||
"initialized": "2020-09-18T14:17:44.190021Z",
|
||||
"hardware_brand": "Zebra",
|
||||
"hardware_model": "TC25",
|
||||
"os_name": "Android",
|
||||
"os_version": "8.1.0",
|
||||
"software_brand": "pretixSCAN",
|
||||
"software_version": "1.5.1",
|
||||
"security_profile": "full"
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import json
|
||||
import zoneinfo
|
||||
from datetime import date, datetime, timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
@@ -32,7 +31,6 @@ from django.test import TestCase
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django_countries.fields import Country
|
||||
from django_scopes import scope
|
||||
from freezegun import freeze_time
|
||||
from tests.testdummy.signals import FoobazSalesChannel
|
||||
|
||||
from pretix.base.decimal import round_decimal
|
||||
@@ -408,56 +406,6 @@ def test_expiring_auto_disabled(event):
|
||||
assert o2.status == Order.STATUS_PENDING
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_expiring_auto_delayed(event):
|
||||
event.settings.set('payment_term_expire_delay_days', 3)
|
||||
event.settings.set('payment_term_last', date(2023, 7, 2))
|
||||
event.settings.set('timezone', 'Europe/Berlin')
|
||||
o1 = Order.objects.create(
|
||||
code='FOO', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING,
|
||||
datetime=datetime(2023, 6, 22, 12, 13, 14, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")),
|
||||
expires=datetime(2023, 6, 30, 23, 59, 59, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")),
|
||||
total=0,
|
||||
)
|
||||
o2 = Order.objects.create(
|
||||
code='FO2', event=event, email='dummy@dummy.test',
|
||||
status=Order.STATUS_PENDING,
|
||||
datetime=datetime(2023, 6, 22, 12, 13, 14, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")),
|
||||
expires=datetime(2023, 6, 28, 23, 59, 59, tzinfo=zoneinfo.ZoneInfo("Europe/Berlin")),
|
||||
total=0,
|
||||
)
|
||||
assert o1.payment_term_expire_date == o1.expires + timedelta(days=2) # limited by term_last
|
||||
assert o2.payment_term_expire_date == o2.expires + timedelta(days=3)
|
||||
with freeze_time("2023-06-29T00:01:00+02:00"):
|
||||
expire_orders(None)
|
||||
o1 = Order.objects.get(id=o1.id)
|
||||
assert o1.status == Order.STATUS_PENDING
|
||||
o2 = Order.objects.get(id=o2.id)
|
||||
assert o2.status == Order.STATUS_PENDING
|
||||
|
||||
with freeze_time("2023-07-01T23:50:00+02:00"):
|
||||
expire_orders(None)
|
||||
o1 = Order.objects.get(id=o1.id)
|
||||
assert o1.status == Order.STATUS_PENDING
|
||||
o2 = Order.objects.get(id=o2.id)
|
||||
assert o2.status == Order.STATUS_PENDING
|
||||
|
||||
with freeze_time("2023-07-02T00:01:00+02:00"):
|
||||
expire_orders(None)
|
||||
o1 = Order.objects.get(id=o1.id)
|
||||
assert o1.status == Order.STATUS_PENDING
|
||||
o2 = Order.objects.get(id=o2.id)
|
||||
assert o2.status == Order.STATUS_EXPIRED
|
||||
|
||||
with freeze_time("2023-07-03T00:01:00+02:00"):
|
||||
expire_orders(None)
|
||||
o1 = Order.objects.get(id=o1.id)
|
||||
assert o1.status == Order.STATUS_EXPIRED
|
||||
o2 = Order.objects.get(id=o2.id)
|
||||
assert o2.status == Order.STATUS_EXPIRED
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_do_not_expire_if_approval_pending(event):
|
||||
o1 = Order.objects.create(
|
||||
|
||||
@@ -30,8 +30,6 @@ from pretix.base.templatetags.rich_text import (
|
||||
# Test link detection
|
||||
("google.com",
|
||||
'<a href="http://google.com" rel="noopener" target="_blank">google.com</a>'),
|
||||
# Test link escaping
|
||||
("google\\.com", 'google.com'),
|
||||
# Test abslink_callback
|
||||
("[Call](tel:+12345)",
|
||||
'<a href="tel:+12345" rel="nofollow">Call</a>'),
|
||||
@@ -81,20 +79,3 @@ def test_newline_handling(content, result):
|
||||
])
|
||||
def test_newline_handling_email(content, result):
|
||||
assert markdown_compile_email(content) == result
|
||||
|
||||
|
||||
@pytest.mark.parametrize("content,result,result_snippet", [
|
||||
# attributes
|
||||
('<a onclick="javascript:foo()">foo</a>', '<p><a>foo</a></p>', '<a>foo</a>'),
|
||||
('<strong color="red">foo</strong>',
|
||||
'<p><strong>foo</strong></p>',
|
||||
'<strong>foo</strong>'),
|
||||
# protocols
|
||||
('<a href="javascript:foo()">foo</a>', '<p><a>foo</a></p>', '<a>foo</a>'),
|
||||
# tags
|
||||
('<script>foo</script>', '<script>foo</script>', 'foo'),
|
||||
])
|
||||
def test_cleanup(content, result, result_snippet):
|
||||
assert rich_text(content) == result
|
||||
assert rich_text_snippet(content) == result_snippet
|
||||
assert markdown_compile_email(content) == result
|
||||
|
||||
@@ -185,7 +185,6 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"max_price": None,
|
||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 0,
|
||||
"allow_waitinglist": True,
|
||||
"mandatory_priced_addons": False,
|
||||
@@ -205,7 +204,6 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"max_price": "14.00",
|
||||
"price": None,
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 4,
|
||||
"allow_waitinglist": True,
|
||||
"mandatory_priced_addons": False,
|
||||
@@ -267,7 +265,6 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00",
|
||||
"includes_mixed_tax_rate": False},
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 0,
|
||||
"allow_waitinglist": True,
|
||||
"mandatory_priced_addons": False,
|
||||
@@ -313,7 +310,6 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"max_price": "14.00",
|
||||
"price": None,
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 4,
|
||||
"allow_waitinglist": True,
|
||||
"mandatory_priced_addons": False,
|
||||
@@ -375,7 +371,6 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
"max_price": None,
|
||||
"price": {"gross": "23.00", "net": "19.33", "tax": "3.67", "name": "", "rate": "19.00", "includes_mixed_tax_rate": False},
|
||||
"picture": None,
|
||||
"picture_fullsize": None,
|
||||
"has_variations": 0,
|
||||
"allow_waitinglist": True,
|
||||
"mandatory_priced_addons": False,
|
||||
@@ -430,7 +425,6 @@ class WidgetCartTest(CartTestMixin, TestCase):
|
||||
'id': self.shirt.pk,
|
||||
'name': 'T-Shirt',
|
||||
'picture': None,
|
||||
"picture_fullsize": None,
|
||||
'description': None,
|
||||
'has_variations': 2,
|
||||
"allow_waitinglist": True,
|
||||
|
||||
Reference in New Issue
Block a user