forked from CGM_Public/pretix_original
Compare commits
22 Commits
widget-coo
...
oidc_query
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
39115152a6 | ||
|
|
7ce790dbb9 | ||
|
|
9a4a0df625 | ||
|
|
a8376b9a79 | ||
|
|
b21041a833 | ||
|
|
e8a716273e | ||
|
|
59a7845ac4 | ||
|
|
ef1024d231 | ||
|
|
71d0d72425 | ||
|
|
6252e526bf | ||
|
|
f3d8a1c2e1 | ||
|
|
148a3b2933 | ||
|
|
709633d6fb | ||
|
|
af3418db54 | ||
|
|
ca8d253114 | ||
|
|
f014a9bbd3 | ||
|
|
3e5bfb44d2 | ||
|
|
1736efbdc3 | ||
|
|
5cd7959e86 | ||
|
|
b847612e1a | ||
|
|
832f4e4d68 | ||
|
|
0a23aeece4 |
@@ -69,6 +69,10 @@ hidden_if_available integer **DEPRECATED*
|
||||
hidden_if_item_available integer The internal ID of a different item, or ``null``. If
|
||||
set, this item won't be shown publicly as long as this
|
||||
other item is available.
|
||||
hidden_if_item_available_mode string If ``hide`` (the default), this item is hidden in the shop
|
||||
if unavailable due to the ``hidden_if_item_available`` setting.
|
||||
If ``info``, the item is visible, but can't be purchased,
|
||||
and a note explaining the unavailability is displayed.
|
||||
require_voucher boolean If ``true``, this item can only be bought using a
|
||||
voucher that is specifically assigned to this item.
|
||||
hide_without_voucher boolean If ``true``, this item is only shown during the voucher
|
||||
@@ -239,6 +243,10 @@ meta_data object Values set fo
|
||||
The ``hidden_if_item_available`` attributes has been added, the ``hidden_if_available`` attribute has been
|
||||
deprecated.
|
||||
|
||||
.. versionchanged:: 2025.01
|
||||
|
||||
The ``hidden_if_item_available_mode`` attributes has been added.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
@@ -308,6 +316,7 @@ Endpoints
|
||||
"available_until_mode": "hide",
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"hidden_if_item_available_mode": "hide",
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -459,6 +468,7 @@ Endpoints
|
||||
"available_until_mode": "hide",
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"hidden_if_item_available_mode": "hide",
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -589,6 +599,7 @@ Endpoints
|
||||
"available_until_mode": "hide",
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"hidden_if_item_available_mode": "hide",
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -705,6 +716,7 @@ Endpoints
|
||||
"available_until_mode": "hide",
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"hidden_if_item_available_mode": "hide",
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"allow_cancel": true,
|
||||
@@ -855,6 +867,7 @@ Endpoints
|
||||
"available_until_mode": "hide",
|
||||
"hidden_if_available": null,
|
||||
"hidden_if_item_available": null,
|
||||
"hidden_if_item_available_mode": "hide",
|
||||
"require_voucher": false,
|
||||
"hide_without_voucher": false,
|
||||
"generate_tickets": null,
|
||||
|
||||
@@ -272,7 +272,7 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
|
||||
'require_voucher', 'hide_without_voucher', 'allow_cancel', 'require_bundling',
|
||||
'min_per_order', 'max_per_order', 'checkin_attention', 'checkin_text', 'has_variations', 'variations',
|
||||
'addons', 'bundles', 'original_price', 'require_approval', 'generate_tickets',
|
||||
'show_quota_left', 'hidden_if_available', 'hidden_if_item_available', 'allow_waitinglist',
|
||||
'show_quota_left', 'hidden_if_available', 'hidden_if_item_available', 'hidden_if_item_available_mode', 'allow_waitinglist',
|
||||
'issue_giftcard', 'meta_data',
|
||||
'require_membership', 'require_membership_types', 'require_membership_hidden', 'grant_membership_type',
|
||||
'grant_membership_duration_like_event', 'grant_membership_duration_days',
|
||||
|
||||
@@ -24,7 +24,7 @@ import hashlib
|
||||
import logging
|
||||
import time
|
||||
from datetime import datetime
|
||||
from urllib.parse import urlencode, urljoin
|
||||
from urllib.parse import parse_qsl, urlencode, urljoin
|
||||
|
||||
import jwt
|
||||
import requests
|
||||
@@ -139,6 +139,11 @@ def oidc_validate_and_complete_config(config):
|
||||
)
|
||||
)
|
||||
|
||||
if "query_parameters" in config and config["query_parameters"]:
|
||||
config["query_parameters"] = urlencode(
|
||||
parse_qsl(config["query_parameters"])
|
||||
)
|
||||
|
||||
config['provider_config'] = provider_config
|
||||
return config
|
||||
|
||||
@@ -154,6 +159,10 @@ def oidc_authorize_url(provider, state, redirect_uri):
|
||||
'state': state,
|
||||
'redirect_uri': redirect_uri,
|
||||
}
|
||||
|
||||
if "query_parameters" in provider.configuration and provider.configuration["query_parameters"]:
|
||||
params.update(parse_qsl(provider.configuration["query_parameters"]))
|
||||
|
||||
return endpoint + '?' + urlencode(params)
|
||||
|
||||
|
||||
|
||||
@@ -1025,10 +1025,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
'autocomplete': 'address-level2',
|
||||
}),
|
||||
'company': forms.TextInput(attrs={
|
||||
'data-display-dependency': '#id_is_business_1',
|
||||
'autocomplete': 'organization',
|
||||
}),
|
||||
'vat_id': forms.TextInput(attrs={'data-display-dependency': '#id_is_business_1'}),
|
||||
'vat_id': forms.TextInput(),
|
||||
'internal_reference': forms.TextInput,
|
||||
}
|
||||
labels = {
|
||||
@@ -1059,10 +1058,8 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# If an individual or company address is acceptable, #id_is_business_0 == individual, _1 == company.
|
||||
# However, if only company addresses are acceptable, #id_is_business_0 == company and is the only choice
|
||||
self.fields["company"].widget.attrs["data-display-dependency"] = f'#id_{self.add_prefix("is_business")}_{int(not self.company_required)}'
|
||||
self.fields["vat_id"].widget.attrs["data-display-dependency"] = f'#id_{self.add_prefix("is_business")}_{int(not self.company_required)}'
|
||||
self.fields["company"].widget.attrs["data-display-dependency"] = f'input[name="{self.add_prefix("is_business")}"][value="business"]'
|
||||
self.fields["vat_id"].widget.attrs["data-display-dependency"] = f'input[name="{self.add_prefix("is_business")}"][value="business"]'
|
||||
|
||||
if not self.ask_vat_id:
|
||||
del self.fields['vat_id']
|
||||
@@ -1143,9 +1140,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
)
|
||||
if self.address_required and not self.company_required and not self.all_optional:
|
||||
if not event.settings.invoice_name_required:
|
||||
self.fields['name_parts'].widget.attrs['data-required-if'] = f'#id_{self.add_prefix("is_business")}_0'
|
||||
self.fields['name_parts'].widget.attrs['data-required-if'] = f'input[name="{self.add_prefix("is_business")}"][value="individual"]'
|
||||
self.fields['name_parts'].widget.attrs['data-no-required-attr'] = '1'
|
||||
self.fields['company'].widget.attrs['data-required-if'] = f'#id_{self.add_prefix("is_business")}_1'
|
||||
self.fields['company'].widget.attrs['data-required-if'] = f'input[name="{self.add_prefix("is_business")}"][value="business"]'
|
||||
|
||||
if not event.settings.invoice_address_beneficiary:
|
||||
del self.fields['beneficiary']
|
||||
|
||||
165
src/pretix/base/logentrytype_registry.py
Normal file
165
src/pretix/base/logentrytype_registry.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.html import format_html
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from pretix.base.signals import EventPluginRegistry
|
||||
|
||||
|
||||
def make_link(a_map, wrapper, is_active=True, event=None, plugin_name=None):
|
||||
if a_map:
|
||||
if 'href' not in a_map:
|
||||
a_map['val'] = format_html('<i>{val}</i>', **a_map)
|
||||
elif is_active:
|
||||
a_map['val'] = format_html('<a href="{href}">{val}</a>', **a_map)
|
||||
elif event and plugin_name:
|
||||
a_map['val'] = format_html(
|
||||
'<i>{val}</i> <a href="{plugin_href}">'
|
||||
'<span data-toggle="tooltip" title="{errmes}" class="fa fa-warning fa-fw"></span></a>',
|
||||
**a_map,
|
||||
errmes=_("The relevant plugin is currently not active. To activate it, click here to go to the plugin settings."),
|
||||
plugin_href=reverse('control:event.settings.plugins', kwargs={
|
||||
'organizer': event.organizer.slug,
|
||||
'event': event.slug,
|
||||
}) + '#plugin_' + plugin_name,
|
||||
)
|
||||
else:
|
||||
a_map['val'] = format_html(
|
||||
'<i>{val}</i> <span data-toggle="tooltip" title="{errmes}" class="fa fa-warning fa-fw"></span>',
|
||||
**a_map,
|
||||
errmes=_("The relevant plugin is currently not active."),
|
||||
)
|
||||
return format_html(wrapper, **a_map)
|
||||
|
||||
|
||||
class LogEntryTypeRegistry(EventPluginRegistry):
|
||||
def __init__(self):
|
||||
super().__init__({'action_type': lambda o: getattr(o, 'action_type')})
|
||||
|
||||
def register(self, *objs):
|
||||
for obj in objs:
|
||||
if not isinstance(obj, LogEntryType):
|
||||
raise TypeError('Entries must be derived from LogEntryType')
|
||||
|
||||
if obj.__module__.startswith('pretix.base.'):
|
||||
raise TypeError('Must not register base classes, only derived ones')
|
||||
|
||||
return super().register(*objs)
|
||||
|
||||
def new_from_dict(self, data):
|
||||
"""
|
||||
Register multiple instance of a `LogEntryType` class with different `action_type`
|
||||
and plain text strings, as given by the items of the specified data dictionary.
|
||||
|
||||
This method is designed to be used as a decorator as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.item.added': _('The product has been created.'),
|
||||
'pretix.event.item.changed': _('The product has been changed.'),
|
||||
# ...
|
||||
})
|
||||
class CoreItemLogEntryType(ItemLogEntryType):
|
||||
# ...
|
||||
|
||||
:param data: action types and descriptions
|
||||
``{"some_action_type": "Plain text description", ...}``
|
||||
"""
|
||||
def reg(clz):
|
||||
for action_type, plain in data.items():
|
||||
self.register(clz(action_type=action_type, plain=plain))
|
||||
return clz
|
||||
return reg
|
||||
|
||||
|
||||
"""
|
||||
Registry for LogEntry types.
|
||||
|
||||
Each entry in this registry should be an instance of a subclass of ``LogEntryType``.
|
||||
They are annotated with their ``action_type`` and the defining ``plugin``.
|
||||
"""
|
||||
log_entry_types = LogEntryTypeRegistry()
|
||||
|
||||
|
||||
class LogEntryType:
|
||||
"""
|
||||
Base class for a type of LogEntry, identified by its action_type.
|
||||
"""
|
||||
|
||||
def __init__(self, action_type=None, plain=None):
|
||||
if action_type:
|
||||
self.action_type = action_type
|
||||
if plain:
|
||||
self.plain = plain
|
||||
|
||||
def display(self, logentry, data):
|
||||
"""
|
||||
Returns the message to be displayed for a given logentry of this type.
|
||||
|
||||
:return: `str` or `LazyI18nString`
|
||||
"""
|
||||
if hasattr(self, 'plain'):
|
||||
plain = str(self.plain)
|
||||
if '{' in plain:
|
||||
data = defaultdict(lambda: '?', data)
|
||||
return plain.format_map(data)
|
||||
else:
|
||||
return plain
|
||||
|
||||
def get_object_link_info(self, logentry) -> Optional[dict]:
|
||||
"""
|
||||
Return information to generate a link to the `content_object` of a given log entry.
|
||||
|
||||
Not implemented in the base class, causing the object link to be omitted.
|
||||
|
||||
:return: Dictionary with the keys ``href`` (URL to view/edit the object) and
|
||||
``val`` (text for the anchor element)
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_object_link(self, logentry):
|
||||
a_map = self.get_object_link_info(logentry)
|
||||
return make_link(a_map, self.object_link_wrapper)
|
||||
|
||||
object_link_wrapper = '{val}'
|
||||
|
||||
def shred_pii(self, logentry):
|
||||
"""
|
||||
To be used for shredding personally identified information contained in the data field of a LogEntry of this
|
||||
type.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class NoOpShredderMixin:
|
||||
def shred_pii(self, logentry):
|
||||
pass
|
||||
|
||||
|
||||
class ClearDataShredderMixin:
|
||||
def shred_pii(self, logentry):
|
||||
logentry.data = None
|
||||
@@ -19,137 +19,20 @@
|
||||
# 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/>.
|
||||
#
|
||||
from collections import defaultdict
|
||||
from typing import Optional
|
||||
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
|
||||
from pretix.base.signals import EventPluginRegistry
|
||||
from pretix.base.models import (
|
||||
Discount, Item, ItemCategory, Order, Question, Quota, SubEvent, TaxRule,
|
||||
Voucher,
|
||||
)
|
||||
|
||||
|
||||
def make_link(a_map, wrapper, is_active=True, event=None, plugin_name=None):
|
||||
if a_map:
|
||||
if is_active:
|
||||
a_map['val'] = '<a href="{href}">{val}</a>'.format_map(a_map)
|
||||
elif event and plugin_name:
|
||||
a_map['val'] = (
|
||||
'<i>{val}</i> <a href="{plugin_href}">'
|
||||
'<span data-toggle="tooltip" title="{errmes}" class="fa fa-warning fa-fw"></span></a>'
|
||||
).format_map({
|
||||
**a_map,
|
||||
"errmes": _("The relevant plugin is currently not active. To activate it, click here to go to the plugin settings."),
|
||||
"plugin_href": reverse('control:event.settings.plugins', kwargs={
|
||||
'organizer': event.organizer.slug,
|
||||
'event': event.slug,
|
||||
}) + '#plugin_' + plugin_name,
|
||||
})
|
||||
else:
|
||||
a_map['val'] = '<i>{val}</i> <span data-toggle="tooltip" title="{errmes}" class="fa fa-warning fa-fw"></span>'.format_map({
|
||||
**a_map,
|
||||
"errmes": _("The relevant plugin is currently not active."),
|
||||
})
|
||||
return wrapper.format_map(a_map)
|
||||
|
||||
|
||||
class LogEntryTypeRegistry(EventPluginRegistry):
|
||||
def __init__(self):
|
||||
super().__init__({'action_type': lambda o: getattr(o, 'action_type')})
|
||||
|
||||
def register(self, *objs):
|
||||
for obj in objs:
|
||||
if not isinstance(obj, LogEntryType):
|
||||
raise TypeError('Entries must be derived from LogEntryType')
|
||||
|
||||
if obj.__module__ == LogEntryType.__module__:
|
||||
raise TypeError('Must not register base classes, only derived ones')
|
||||
|
||||
return super().register(*objs)
|
||||
|
||||
def new_from_dict(self, data):
|
||||
"""
|
||||
Register multiple instance of a `LogEntryType` class with different `action_type`
|
||||
and plain text strings, as given by the items of the specified data dictionary.
|
||||
|
||||
This method is designed to be used as a decorator as follows:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.item.added': _('The product has been created.'),
|
||||
'pretix.event.item.changed': _('The product has been changed.'),
|
||||
# ...
|
||||
})
|
||||
class CoreItemLogEntryType(ItemLogEntryType):
|
||||
# ...
|
||||
|
||||
:param data: action types and descriptions
|
||||
``{"some_action_type": "Plain text description", ...}``
|
||||
"""
|
||||
def reg(clz):
|
||||
for action_type, plain in data.items():
|
||||
self.register(clz(action_type=action_type, plain=plain))
|
||||
return clz
|
||||
return reg
|
||||
|
||||
|
||||
"""
|
||||
Registry for LogEntry types.
|
||||
|
||||
Each entry in this registry should be an instance of a subclass of ``LogEntryType``.
|
||||
They are annotated with their ``action_type`` and the defining ``plugin``.
|
||||
"""
|
||||
log_entry_types = LogEntryTypeRegistry()
|
||||
|
||||
|
||||
class LogEntryType:
|
||||
"""
|
||||
Base class for a type of LogEntry, identified by its action_type.
|
||||
"""
|
||||
|
||||
def __init__(self, action_type=None, plain=None):
|
||||
if action_type:
|
||||
self.action_type = action_type
|
||||
if plain:
|
||||
self.plain = plain
|
||||
|
||||
def display(self, logentry):
|
||||
"""
|
||||
Returns the message to be displayed for a given logentry of this type.
|
||||
|
||||
:return: `str` or `LazyI18nString`
|
||||
"""
|
||||
if hasattr(self, 'plain'):
|
||||
plain = str(self.plain)
|
||||
if '{' in plain:
|
||||
data = defaultdict(lambda: '?', logentry.parsed_data)
|
||||
return plain.format_map(data)
|
||||
else:
|
||||
return plain
|
||||
|
||||
def get_object_link_info(self, logentry) -> dict:
|
||||
"""
|
||||
Return information to generate a link to the `content_object` of a given log entry.
|
||||
|
||||
Not implemented in the base class, causing the object link to be omitted.
|
||||
|
||||
:return: Dictionary with the keys ``href`` (containing a URL to view/edit the object) and ``val`` (containing the
|
||||
escaped text for the anchor element)
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_object_link(self, logentry):
|
||||
a_map = self.get_object_link_info(logentry)
|
||||
return make_link(a_map, self.object_link_wrapper)
|
||||
|
||||
object_link_wrapper = '{val}'
|
||||
|
||||
def shred_pii(self, logentry):
|
||||
"""
|
||||
To be used for shredding personally identified information contained in the data field of a LogEntry of this
|
||||
type.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
from .logentrytype_registry import ( # noqa
|
||||
ClearDataShredderMixin, LogEntryType, NoOpShredderMixin, log_entry_types,
|
||||
make_link, LogEntryTypeRegistry,
|
||||
)
|
||||
|
||||
|
||||
class EventLogEntryType(LogEntryType):
|
||||
@@ -157,15 +40,27 @@ class EventLogEntryType(LogEntryType):
|
||||
Base class for any `LogEntry` type whose `content_object` is either an `Event` itself or belongs to a specific `Event`.
|
||||
"""
|
||||
|
||||
def get_object_link_info(self, logentry) -> dict:
|
||||
if hasattr(self, 'object_link_viewname') and logentry.content_object:
|
||||
def get_object_link_info(self, logentry) -> Optional[dict]:
|
||||
if hasattr(self, 'object_link_viewname'):
|
||||
content = logentry.content_object
|
||||
if not content:
|
||||
if logentry.content_type_id:
|
||||
return {
|
||||
'val': _('(deleted)'),
|
||||
}
|
||||
else:
|
||||
return
|
||||
|
||||
if hasattr(self, 'content_type') and not isinstance(content, self.content_type):
|
||||
return
|
||||
|
||||
return {
|
||||
'href': reverse(self.object_link_viewname, kwargs={
|
||||
'event': logentry.event.slug,
|
||||
'organizer': logentry.event.organizer.slug,
|
||||
**self.object_link_args(logentry.content_object),
|
||||
**self.object_link_args(content),
|
||||
}),
|
||||
'val': escape(self.object_link_display_name(logentry.content_object)),
|
||||
'val': self.object_link_display_name(logentry.content_object),
|
||||
}
|
||||
|
||||
def object_link_args(self, content_object):
|
||||
@@ -182,6 +77,7 @@ class EventLogEntryType(LogEntryType):
|
||||
class OrderLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Order {val}')
|
||||
object_link_viewname = 'control:event.order'
|
||||
content_type = Order
|
||||
|
||||
def object_link_args(self, order):
|
||||
return {'code': order.code}
|
||||
@@ -194,6 +90,7 @@ class VoucherLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Voucher {val}…')
|
||||
object_link_viewname = 'control:event.voucher'
|
||||
object_link_argname = 'voucher'
|
||||
content_type = Voucher
|
||||
|
||||
def object_link_display_name(self, voucher):
|
||||
if len(voucher.code) > 6:
|
||||
@@ -205,49 +102,46 @@ class ItemLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Product {val}')
|
||||
object_link_viewname = 'control:event.item'
|
||||
object_link_argname = 'item'
|
||||
content_type = Item
|
||||
|
||||
|
||||
class SubEventLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = pgettext_lazy('subevent', 'Date {val}')
|
||||
object_link_viewname = 'control:event.subevent'
|
||||
object_link_argname = 'subevent'
|
||||
content_type = SubEvent
|
||||
|
||||
|
||||
class QuotaLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Quota {val}')
|
||||
object_link_viewname = 'control:event.items.quotas.show'
|
||||
object_link_argname = 'quota'
|
||||
content_type = Quota
|
||||
|
||||
|
||||
class DiscountLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Discount {val}')
|
||||
object_link_viewname = 'control:event.items.discounts.edit'
|
||||
object_link_argname = 'discount'
|
||||
content_type = Discount
|
||||
|
||||
|
||||
class ItemCategoryLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Category {val}')
|
||||
object_link_viewname = 'control:event.items.categories.edit'
|
||||
object_link_argname = 'category'
|
||||
content_type = ItemCategory
|
||||
|
||||
|
||||
class QuestionLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Question {val}')
|
||||
object_link_viewname = 'control:event.items.questions.show'
|
||||
object_link_argname = 'question'
|
||||
content_type = Question
|
||||
|
||||
|
||||
class TaxRuleLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Tax rule {val}')
|
||||
object_link_viewname = 'control:event.settings.tax.edit'
|
||||
object_link_argname = 'rule'
|
||||
|
||||
|
||||
class NoOpShredderMixin:
|
||||
def shred_pii(self, logentry):
|
||||
pass
|
||||
|
||||
|
||||
class ClearDataShredderMixin:
|
||||
def shred_pii(self, logentry):
|
||||
logentry.data = None
|
||||
content_type = TaxRule
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.16 on 2025-01-23 11:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("pretixbase", "0275_alter_question_valid_number_max_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="item",
|
||||
name="hidden_if_item_available_mode",
|
||||
field=models.CharField(default="hide", max_length=16),
|
||||
),
|
||||
]
|
||||
@@ -442,8 +442,12 @@ class Item(LoggedModel):
|
||||
UNAVAIL_MODE_INFO = "info"
|
||||
UNAVAIL_MODES = (
|
||||
(UNAVAIL_MODE_HIDDEN, _("Hide product if unavailable")),
|
||||
(UNAVAIL_MODE_INFO, _("Show info text if unavailable")),
|
||||
(UNAVAIL_MODE_INFO, _("Show product with info on why it’s unavailable")),
|
||||
)
|
||||
UNAVAIL_MODE_ICONS = {
|
||||
UNAVAIL_MODE_HIDDEN: 'eye-slash',
|
||||
UNAVAIL_MODE_INFO: 'info'
|
||||
}
|
||||
|
||||
MEDIA_POLICY_REUSE = 'reuse'
|
||||
MEDIA_POLICY_NEW = 'new'
|
||||
@@ -596,6 +600,11 @@ class Item(LoggedModel):
|
||||
"be a short period in which both products are visible while all tickets of the referenced "
|
||||
"product are reserved, but not yet sold.")
|
||||
)
|
||||
hidden_if_item_available_mode = models.CharField(
|
||||
choices=UNAVAIL_MODES,
|
||||
default=UNAVAIL_MODE_HIDDEN,
|
||||
max_length=16,
|
||||
)
|
||||
require_voucher = models.BooleanField(
|
||||
verbose_name=_('This product can only be bought using a voucher.'),
|
||||
default=False,
|
||||
@@ -885,6 +894,8 @@ class Item(LoggedModel):
|
||||
return 'available_from'
|
||||
elif subevent_item and subevent_item.available_until and subevent_item.available_until < now_dt:
|
||||
return 'available_until'
|
||||
elif self.hidden_if_item_available and self._dependency_available:
|
||||
return 'hidden_if_item_available'
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
from pretix.base.logentrytypes import log_entry_types, make_link
|
||||
from pretix.base.logentrytype_registry import log_entry_types, make_link
|
||||
from pretix.base.signals import is_app_active, logentry_object_link
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ class LogEntry(models.Model):
|
||||
def display(self):
|
||||
log_entry_type, meta = log_entry_types.get(action_type=self.action_type)
|
||||
if log_entry_type:
|
||||
return log_entry_type.display(self)
|
||||
return log_entry_type.display(self, self.parsed_data)
|
||||
|
||||
from ..signals import logentry_display
|
||||
|
||||
|
||||
@@ -355,7 +355,7 @@ class Order(LockModel, LoggedModel):
|
||||
|
||||
if not self.testmode:
|
||||
raise TypeError("Only test mode orders can be deleted.")
|
||||
self.event.log_action(
|
||||
self.log_action(
|
||||
'pretix.event.order.deleted', user=user, auth=auth,
|
||||
data={
|
||||
'code': self.code,
|
||||
|
||||
@@ -231,7 +231,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
raise ValidationError({
|
||||
'timezone': _('Your default locale must be specified.')
|
||||
})
|
||||
if not data.get("no_taxes") and not data.get("tax_rate"):
|
||||
if not data.get("no_taxes") and data.get("tax_rate") is None:
|
||||
raise ValidationError({
|
||||
'tax_rate': _('You have not specified a tax rate. If you do not want us to compute sales taxes, please '
|
||||
'check "{field}" above.').format(field=self.fields["no_taxes"].label)
|
||||
|
||||
@@ -476,6 +476,7 @@ class ItemCreateForm(I18nModelForm):
|
||||
'show_quota_left',
|
||||
'hidden_if_available',
|
||||
'hidden_if_item_available',
|
||||
'hidden_if_item_available_mode',
|
||||
'require_bundling',
|
||||
'require_membership',
|
||||
'grant_membership_type',
|
||||
@@ -646,18 +647,12 @@ class ItemUpdateForm(I18nModelForm):
|
||||
|
||||
self.fields['available_from_mode'].widget = ButtonGroupRadioSelect(
|
||||
choices=self.fields['available_from_mode'].choices,
|
||||
option_icons={
|
||||
Item.UNAVAIL_MODE_HIDDEN: 'eye-slash',
|
||||
Item.UNAVAIL_MODE_INFO: 'info'
|
||||
}
|
||||
option_icons=Item.UNAVAIL_MODE_ICONS
|
||||
)
|
||||
|
||||
self.fields['available_until_mode'].widget = ButtonGroupRadioSelect(
|
||||
choices=self.fields['available_until_mode'].choices,
|
||||
option_icons={
|
||||
Item.UNAVAIL_MODE_HIDDEN: 'eye-slash',
|
||||
Item.UNAVAIL_MODE_INFO: 'info'
|
||||
}
|
||||
option_icons=Item.UNAVAIL_MODE_ICONS
|
||||
)
|
||||
|
||||
self.fields['hide_without_voucher'].widget = ButtonGroupRadioSelect(
|
||||
@@ -672,6 +667,11 @@ class ItemUpdateForm(I18nModelForm):
|
||||
attrs={'data-checkbox-dependency': '#id_require_voucher'}
|
||||
)
|
||||
|
||||
self.fields['hidden_if_item_available_mode'].widget = ButtonGroupRadioSelect(
|
||||
choices=self.fields['hidden_if_item_available_mode'].choices,
|
||||
option_icons=Item.UNAVAIL_MODE_ICONS
|
||||
)
|
||||
|
||||
if self.instance.hidden_if_available_id:
|
||||
self.fields['hidden_if_available'].queryset = self.event.quotas.all()
|
||||
self.fields['hidden_if_available'].help_text = format_html(
|
||||
@@ -853,6 +853,7 @@ class ItemUpdateForm(I18nModelForm):
|
||||
'show_quota_left',
|
||||
'hidden_if_available',
|
||||
'hidden_if_item_available',
|
||||
'hidden_if_item_available_mode',
|
||||
'issue_giftcard',
|
||||
'require_membership',
|
||||
'require_membership_types',
|
||||
@@ -970,18 +971,12 @@ class ItemVariationForm(I18nModelForm):
|
||||
|
||||
self.fields['available_from_mode'].widget = ButtonGroupRadioSelect(
|
||||
choices=self.fields['available_from_mode'].choices,
|
||||
option_icons={
|
||||
Item.UNAVAIL_MODE_HIDDEN: 'eye-slash',
|
||||
Item.UNAVAIL_MODE_INFO: 'info'
|
||||
}
|
||||
option_icons=Item.UNAVAIL_MODE_ICONS
|
||||
)
|
||||
|
||||
self.fields['available_until_mode'].widget = ButtonGroupRadioSelect(
|
||||
choices=self.fields['available_until_mode'].choices,
|
||||
option_icons={
|
||||
Item.UNAVAIL_MODE_HIDDEN: 'eye-slash',
|
||||
Item.UNAVAIL_MODE_INFO: 'info'
|
||||
}
|
||||
option_icons=Item.UNAVAIL_MODE_ICONS
|
||||
)
|
||||
|
||||
self.meta_fields = []
|
||||
|
||||
@@ -1043,6 +1043,15 @@ class SSOProviderForm(I18nModelForm):
|
||||
label=pgettext_lazy('sso_oidc', 'Phone field'),
|
||||
required=False,
|
||||
)
|
||||
config_oidc_query_parameters = forms.CharField(
|
||||
label=pgettext_lazy('sso_oidc', 'Query parameters'),
|
||||
help_text=pgettext_lazy('sso_oidc', 'Optional query parameters, that will be added to calls to '
|
||||
'the authorization endpoint. Enter as: {example}'.format(
|
||||
example='<code>param1=value1&param2=value2</code>'
|
||||
),
|
||||
),
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = CustomerSSOProvider
|
||||
|
||||
@@ -33,16 +33,16 @@
|
||||
# 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 json
|
||||
from collections import defaultdict
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
|
||||
import bleach
|
||||
import dateutil.parser
|
||||
from django.dispatch import receiver
|
||||
from django.urls import reverse
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape
|
||||
from django.utils.html import escape, format_html
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from i18nfield.strings import LazyI18nString
|
||||
@@ -68,128 +68,212 @@ OVERVIEW_BANLIST = [
|
||||
]
|
||||
|
||||
|
||||
def _display_order_changed(event: Event, logentry: LogEntry):
|
||||
data = json.loads(logentry.data)
|
||||
class OrderChangeLogEntryType(OrderLogEntryType):
|
||||
prefix = _('The order has been changed:')
|
||||
|
||||
text = _('The order has been changed:')
|
||||
if logentry.action_type == 'pretix.event.order.changed.item':
|
||||
def display(self, logentry, data):
|
||||
return self.prefix + ' ' + self.display_prefixed(logentry.event, logentry, data)
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
return super().display(logentry, data)
|
||||
|
||||
|
||||
class OrderPositionChangeLogEntryType(OrderChangeLogEntryType):
|
||||
prefix = _('The order has been changed:')
|
||||
|
||||
def display(self, logentry, data):
|
||||
return super().display(logentry, {**data, 'posid': data.get('positionid', '?')})
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderItemChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.item'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
old_item = str(event.items.get(pk=data['old_item']))
|
||||
if data['old_variation']:
|
||||
old_item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['old_variation']))
|
||||
new_item = str(event.items.get(pk=data['new_item']))
|
||||
if data['new_variation']:
|
||||
new_item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['new_variation']))
|
||||
return text + ' ' + _('Position #{posid}: {old_item} ({old_price}) changed '
|
||||
'to {new_item} ({new_price}).').format(
|
||||
return _('Position #{posid}: {old_item} ({old_price}) changed to {new_item} ({new_price}).').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
old_item=old_item, new_item=new_item,
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
new_price=money_filter(Decimal(data['new_price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.membership':
|
||||
return text + ' ' + _('Position #{posid}: Used membership changed.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.seat':
|
||||
return text + ' ' + _('Position #{posid}: Seat "{old_seat}" changed '
|
||||
'to "{new_seat}".').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
old_seat=data.get('old_seat'), new_seat=data.get('new_seat'),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.subevent':
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderMembershipChanged(OrderPositionChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.membership'
|
||||
plain = _('Position #{posid}: Used membership changed.')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderSeatChanged(OrderPositionChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.seat'
|
||||
plain = _('Position #{posid}: Seat "{old_seat}" changed to "{new_seat}".')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderSubeventChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.subevent'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
old_se = str(event.subevents.get(pk=data['old_subevent']))
|
||||
new_se = str(event.subevents.get(pk=data['new_subevent']))
|
||||
return text + ' ' + _('Position #{posid}: Event date "{old_event}" ({old_price}) changed '
|
||||
'to "{new_event}" ({new_price}).').format(
|
||||
return _('Position #{posid}: Event date "{old_event}" ({old_price}) changed '
|
||||
'to "{new_event}" ({new_price}).').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
old_event=old_se, new_event=new_se,
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
new_price=money_filter(Decimal(data['new_price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.price':
|
||||
return text + ' ' + _('Price of position #{posid} changed from {old_price} '
|
||||
'to {new_price}.').format(
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderPriceChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.price'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
return _('Price of position #{posid} changed from {old_price} to {new_price}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
new_price=money_filter(Decimal(data['new_price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.tax_rule':
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderTaxRuleChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.tax_rule'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
if 'positionid' in data:
|
||||
return text + ' ' + _('Tax rule of position #{posid} changed from {old_rule} '
|
||||
'to {new_rule}.').format(
|
||||
return _('Tax rule of position #{posid} changed from {old_rule} to {new_rule}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
old_rule=TaxRule.objects.get(pk=data['old_taxrule']) if data['old_taxrule'] else '–',
|
||||
new_rule=TaxRule.objects.get(pk=data['new_taxrule']),
|
||||
)
|
||||
elif 'fee' in data:
|
||||
return text + ' ' + _('Tax rule of fee #{fee} changed from {old_rule} '
|
||||
'to {new_rule}.').format(
|
||||
return _('Tax rule of fee #{fee} changed from {old_rule} to {new_rule}.').format(
|
||||
fee=data.get('fee', '?'),
|
||||
old_rule=TaxRule.objects.get(pk=data['old_taxrule']) if data['old_taxrule'] else '–',
|
||||
new_rule=TaxRule.objects.get(pk=data['new_taxrule']),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.addfee':
|
||||
return text + ' ' + str(_('A fee has been added'))
|
||||
elif logentry.action_type == 'pretix.event.order.changed.feevalue':
|
||||
return text + ' ' + _('A fee was changed from {old_price} to {new_price}.').format(
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderFeeAdded(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.addfee'
|
||||
plain = _('A fee has been added')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderFeeChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.feevalue'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
return _('A fee was changed from {old_price} to {new_price}.').format(
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
new_price=money_filter(Decimal(data['new_price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.cancelfee':
|
||||
return text + ' ' + _('A fee of {old_price} was removed.').format(
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderFeeRemoved(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.cancelfee'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
return _('A fee of {old_price} was removed.').format(
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.cancel':
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderCanceled(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.cancel'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
old_item = str(event.items.get(pk=data['old_item']))
|
||||
if data['old_variation']:
|
||||
old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))
|
||||
return text + ' ' + _('Position #{posid} ({old_item}, {old_price}) canceled.').format(
|
||||
return _('Position #{posid} ({old_item}, {old_price}) canceled.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
old_item=old_item,
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.add':
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderPositionAdded(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.add'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
item = str(event.items.get(pk=data['item']))
|
||||
if data['variation']:
|
||||
item += ' - ' + str(ItemVariation.objects.get(item__event=event, pk=data['variation']))
|
||||
if data['addon_to']:
|
||||
addon_to = OrderPosition.objects.get(order__event=event, pk=data['addon_to'])
|
||||
return text + ' ' + _('Position #{posid} created: {item} ({price}) as an add-on to '
|
||||
'position #{addon_to}.').format(
|
||||
return _('Position #{posid} created: {item} ({price}) as an add-on to position #{addon_to}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
item=item, addon_to=addon_to.positionid,
|
||||
price=money_filter(Decimal(data['price']), event.currency),
|
||||
)
|
||||
else:
|
||||
return text + ' ' + _('Position #{posid} created: {item} ({price}).').format(
|
||||
return _('Position #{posid} created: {item} ({price}).').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
item=item,
|
||||
price=money_filter(Decimal(data['price']), event.currency),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.secret':
|
||||
return text + ' ' + _('A new secret has been generated for position #{posid}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.valid_from':
|
||||
return text + ' ' + _('The validity start date for position #{posid} has been changed to {value}.').format(
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderSecretChanged(OrderPositionChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.secret'
|
||||
plain = _('A new secret has been generated for position #{posid}.')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderValidFromChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.valid_from'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
return _('The validity start date for position #{posid} has been changed to {value}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
value=date_format(dateutil.parser.parse(data.get('new_value')), 'SHORT_DATETIME_FORMAT') if data.get(
|
||||
'new_value') else '–'
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.valid_until':
|
||||
return text + ' ' + _('The validity end date for position #{posid} has been changed to {value}.').format(
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderValidUntilChanged(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.valid_until'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
return _('The validity end date for position #{posid} has been changed to {value}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
value=date_format(dateutil.parser.parse(data.get('new_value')), 'SHORT_DATETIME_FORMAT') if data.get('new_value') else '–'
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.add_block':
|
||||
return text + ' ' + _('A block has been added for position #{posid}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.remove_block':
|
||||
return text + ' ' + _('A block has been removed for position #{posid}.').format(
|
||||
posid=data.get('positionid', '?'),
|
||||
)
|
||||
elif logentry.action_type == 'pretix.event.order.changed.split':
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderChangedBlockAdded(OrderPositionChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.add_block'
|
||||
plain = _('A block has been added for position #{posid}.')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderChangedBlockRemoved(OrderPositionChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.remove_block'
|
||||
plain = _('A block has been removed for position #{posid}.')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderChangedSplit(OrderChangeLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.split'
|
||||
|
||||
def display_prefixed(self, event: Event, logentry: LogEntry, data):
|
||||
old_item = str(event.items.get(pk=data['old_item']))
|
||||
if data['old_variation']:
|
||||
old_item += ' - ' + str(ItemVariation.objects.get(pk=data['old_variation']))
|
||||
@@ -198,194 +282,148 @@ def _display_order_changed(event: Event, logentry: LogEntry):
|
||||
'organizer': event.organizer.slug,
|
||||
'code': data['new_order']
|
||||
})
|
||||
return mark_safe(escape(text) + ' ' + _('Position #{posid} ({old_item}, {old_price}) split into new order: {order}').format(
|
||||
return mark_safe(self.prefix + ' ' + _('Position #{posid} ({old_item}, {old_price}) split into new order: {order}').format(
|
||||
old_item=escape(old_item),
|
||||
posid=data.get('positionid', '?'),
|
||||
order='<a href="{}">{}</a>'.format(url, data['new_order']),
|
||||
old_price=money_filter(Decimal(data['old_price']), event.currency),
|
||||
))
|
||||
elif logentry.action_type == 'pretix.event.order.changed.split_from':
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderChangedSplitFrom(OrderLogEntryType):
|
||||
action_type = 'pretix.event.order.changed.split_from'
|
||||
|
||||
def display(self, logentry: LogEntry, data):
|
||||
return _('This order has been created by splitting the order {order}').format(
|
||||
order=data['original_order'],
|
||||
)
|
||||
|
||||
|
||||
def _display_checkin(event, logentry):
|
||||
data = logentry.parsed_data
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.checkin.unknown': (
|
||||
_('Unknown scan of code "{barcode}…" at {datetime} for list "{list}", type "{type}".'),
|
||||
_('Unknown scan of code "{barcode}…" for list "{list}", type "{type}".'),
|
||||
),
|
||||
'pretix.event.checkin.revoked': (
|
||||
_('Scan of revoked code "{barcode}…" at {datetime} for list "{list}", type "{type}", was uploaded.'),
|
||||
_('Scan of revoked code "{barcode}" for list "{list}", type "{type}", was uploaded.'),
|
||||
),
|
||||
'pretix.event.checkin.denied': (
|
||||
_('Denied scan of position #{posid} at {datetime} for list "{list}", type "{type}", error code "{errorcode}".'),
|
||||
_('Denied scan of position #{posid} for list "{list}", type "{type}", error code "{errorcode}".'),
|
||||
),
|
||||
'pretix.control.views.checkin.reverted': _('The check-in of position #{posid} on list "{list}" has been reverted.'),
|
||||
'pretix.event.checkin.reverted': _('The check-in of position #{posid} on list "{list}" has been reverted.'),
|
||||
})
|
||||
class CheckinErrorLogEntryType(OrderLogEntryType):
|
||||
def display(self, logentry: LogEntry, data):
|
||||
self.display_plain(self.plain, logentry, data)
|
||||
|
||||
show_dt = False
|
||||
if 'datetime' in data:
|
||||
dt = dateutil.parser.parse(data.get('datetime'))
|
||||
show_dt = abs((logentry.datetime - dt).total_seconds()) > 5 or 'forced' in data
|
||||
tz = event.timezone
|
||||
dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT")
|
||||
def display_plain(self, plain, logentry: LogEntry, data):
|
||||
if isinstance(plain, tuple):
|
||||
plain_with_dt, plain_without_dt = plain
|
||||
else:
|
||||
plain_with_dt, plain_without_dt = plain, plain
|
||||
|
||||
if 'list' in data:
|
||||
try:
|
||||
checkin_list = event.checkin_lists.get(pk=data.get('list')).name
|
||||
except CheckinList.DoesNotExist:
|
||||
checkin_list = _("(unknown)")
|
||||
else:
|
||||
checkin_list = _("(unknown)")
|
||||
data = defaultdict(lambda: '?', data)
|
||||
|
||||
if logentry.action_type == 'pretix.event.checkin.unknown':
|
||||
if show_dt:
|
||||
return _(
|
||||
'Unknown scan of code "{barcode}…" at {datetime} for list "{list}", type "{type}".'
|
||||
).format(
|
||||
posid=data.get('positionid'),
|
||||
type=data.get('type'),
|
||||
barcode=data.get('barcode')[:16],
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
event = logentry.event
|
||||
|
||||
if 'list' in data:
|
||||
try:
|
||||
data['list'] = event.checkin_lists.get(pk=data.get('list')).name
|
||||
except CheckinList.DoesNotExist:
|
||||
data['list'] = _("(unknown)")
|
||||
else:
|
||||
data['list'] = _("(unknown)")
|
||||
|
||||
data['barcode'] = data.get('barcode', '')[:16]
|
||||
data['posid'] = logentry.parsed_data.get('positionid', '?')
|
||||
|
||||
if 'datetime' in data:
|
||||
dt = dateutil.parser.parse(data.get('datetime'))
|
||||
if abs((logentry.datetime - dt).total_seconds()) > 5 or 'forced' in data:
|
||||
tz = event.timezone
|
||||
data['datetime'] = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT")
|
||||
return str(plain_with_dt).format_map(data)
|
||||
else:
|
||||
return str(plain_without_dt).format_map(data)
|
||||
|
||||
|
||||
@log_entry_types.new('pretix.event.checkin')
|
||||
class CheckinLogEntryType(CheckinErrorLogEntryType):
|
||||
def display(self, logentry: LogEntry, data):
|
||||
if data.get('type') == Checkin.TYPE_EXIT:
|
||||
return self.display_plain((
|
||||
_('Position #{posid} has been checked out at {datetime} for list "{list}".'),
|
||||
_('Position #{posid} has been checked out for list "{list}".'),
|
||||
), logentry, data)
|
||||
elif data.get('first'):
|
||||
return self.display_plain((
|
||||
_('Position #{posid} has been checked in at {datetime} for list "{list}".'),
|
||||
_('Position #{posid} has been checked in for list "{list}".'),
|
||||
), logentry, data)
|
||||
elif data.get('forced'):
|
||||
return self.display_plain(
|
||||
_('A scan for position #{posid} at {datetime} for list "{list}" has been uploaded even though it has '
|
||||
'been scanned already.'),
|
||||
logentry, data
|
||||
)
|
||||
else:
|
||||
return _(
|
||||
'Unknown scan of code "{barcode}…" for list "{list}", type "{type}".'
|
||||
).format(
|
||||
posid=data.get('positionid'),
|
||||
type=data.get('type'),
|
||||
barcode=data.get('barcode')[:16],
|
||||
list=checkin_list
|
||||
return self.display_plain(
|
||||
_('Position #{posid} has been scanned and rejected because it has already been scanned before '
|
||||
'on list "{list}".'),
|
||||
logentry, data
|
||||
)
|
||||
|
||||
if logentry.action_type == 'pretix.event.checkin.revoked':
|
||||
if show_dt:
|
||||
return _(
|
||||
'Scan scan of revoked code "{barcode}…" at {datetime} for list "{list}", type "{type}", was uploaded.'
|
||||
).format(
|
||||
posid=data.get('positionid'),
|
||||
type=data.get('type'),
|
||||
barcode=data.get('barcode')[:16],
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
)
|
||||
else:
|
||||
return _(
|
||||
'Scan of revoked code "{barcode}" for list "{list}", type "{type}", was uploaded.'
|
||||
).format(
|
||||
posid=data.get('positionid'),
|
||||
type=data.get('type'),
|
||||
barcode=data.get('barcode')[:16],
|
||||
list=checkin_list
|
||||
)
|
||||
|
||||
if logentry.action_type == 'pretix.event.checkin.denied':
|
||||
if show_dt:
|
||||
return _(
|
||||
'Denied scan of position #{posid} at {datetime} for list "{list}", type "{type}", '
|
||||
'error code "{errorcode}".'
|
||||
).format(
|
||||
posid=data.get('positionid'),
|
||||
type=data.get('type'),
|
||||
errorcode=data.get('errorcode'),
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
)
|
||||
else:
|
||||
return _(
|
||||
'Denied scan of position #{posid} for list "{list}", type "{type}", error code "{errorcode}".'
|
||||
).format(
|
||||
posid=data.get('positionid'),
|
||||
type=data.get('type'),
|
||||
errorcode=data.get('errorcode'),
|
||||
list=checkin_list
|
||||
)
|
||||
@log_entry_types.new()
|
||||
class OrderConsentLogEntryType(OrderLogEntryType):
|
||||
action_type = 'pretix.event.order.consent'
|
||||
|
||||
if data.get('type') == Checkin.TYPE_EXIT:
|
||||
if show_dt:
|
||||
return _('Position #{posid} has been checked out at {datetime} for list "{list}".').format(
|
||||
posid=data.get('positionid'),
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
)
|
||||
def display(self, logentry: LogEntry, data):
|
||||
return _('The user confirmed the following message: "{}"').format(
|
||||
bleach.clean(data.get('msg'), tags=set(), strip=True)
|
||||
)
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderCanceledLogEntryType(OrderLogEntryType):
|
||||
action_type = 'pretix.event.order.canceled'
|
||||
|
||||
def display(self, logentry: LogEntry, data):
|
||||
comment = data.get('comment')
|
||||
if comment:
|
||||
return _('The order has been canceled (comment: "{comment}").').format(comment=comment)
|
||||
else:
|
||||
return _('Position #{posid} has been checked out for list "{list}".').format(
|
||||
posid=data.get('positionid'),
|
||||
list=checkin_list
|
||||
)
|
||||
if data.get('first'):
|
||||
if show_dt:
|
||||
return _('Position #{posid} has been checked in at {datetime} for list "{list}".').format(
|
||||
posid=data.get('positionid'),
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
)
|
||||
else:
|
||||
return _('Position #{posid} has been checked in for list "{list}".').format(
|
||||
posid=data.get('positionid'),
|
||||
list=checkin_list
|
||||
)
|
||||
else:
|
||||
if data.get('forced'):
|
||||
return _(
|
||||
'A scan for position #{posid} at {datetime} for list "{list}" has been uploaded even though it has '
|
||||
'been scanned already.'.format(
|
||||
posid=data.get('positionid'),
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
)
|
||||
)
|
||||
return _(
|
||||
'Position #{posid} has been scanned and rejected because it has already been scanned before '
|
||||
'on list "{list}".'.format(
|
||||
posid=data.get('positionid'),
|
||||
list=checkin_list
|
||||
)
|
||||
return _('The order has been canceled.')
|
||||
|
||||
|
||||
@log_entry_types.new()
|
||||
class OrderPrintLogEntryType(OrderLogEntryType):
|
||||
action_type = 'pretix.event.order.print'
|
||||
|
||||
def display(self, logentry: LogEntry, data):
|
||||
return _('Position #{posid} has been printed at {datetime} with type "{type}".').format(
|
||||
posid=data.get('positionid'),
|
||||
datetime=date_format(
|
||||
dateutil.parser.parse(data["datetime"]).astimezone(logentry.event.timezone),
|
||||
"SHORT_DATETIME_FORMAT"
|
||||
),
|
||||
type=dict(PrintLog.PRINT_TYPES)[data["type"]],
|
||||
)
|
||||
|
||||
|
||||
@receiver(signal=logentry_display, dispatch_uid="pretixcontrol_logentry_display")
|
||||
def pretixcontrol_logentry_display(sender: Event, logentry: LogEntry, **kwargs):
|
||||
|
||||
if logentry.action_type.startswith('pretix.event.order.changed'):
|
||||
return _display_order_changed(sender, logentry)
|
||||
|
||||
if logentry.action_type.startswith('pretix.event.payment.provider.'):
|
||||
return _('The settings of a payment provider have been changed.')
|
||||
|
||||
if logentry.action_type.startswith('pretix.event.tickets.provider.'):
|
||||
return _('The settings of a ticket output provider have been changed.')
|
||||
|
||||
if logentry.action_type == 'pretix.event.order.consent':
|
||||
return _('The user confirmed the following message: "{}"').format(
|
||||
bleach.clean(logentry.parsed_data.get('msg'), tags=set(), strip=True)
|
||||
)
|
||||
|
||||
if logentry.action_type == 'pretix.event.order.canceled':
|
||||
comment = logentry.parsed_data.get('comment')
|
||||
if comment:
|
||||
return _('The order has been canceled (comment: "{comment}").').format(comment=comment)
|
||||
else:
|
||||
return _('The order has been canceled.')
|
||||
|
||||
if logentry.action_type in ('pretix.control.views.checkin.reverted', 'pretix.event.checkin.reverted'):
|
||||
if 'list' in logentry.parsed_data:
|
||||
try:
|
||||
checkin_list = sender.checkin_lists.get(pk=logentry.parsed_data.get('list')).name
|
||||
except CheckinList.DoesNotExist:
|
||||
checkin_list = _("(unknown)")
|
||||
else:
|
||||
checkin_list = _("(unknown)")
|
||||
|
||||
return _('The check-in of position #{posid} on list "{list}" has been reverted.').format(
|
||||
posid=logentry.parsed_data.get('positionid'),
|
||||
list=checkin_list,
|
||||
)
|
||||
|
||||
if sender and logentry.action_type.startswith('pretix.event.checkin'):
|
||||
return _display_checkin(sender, logentry)
|
||||
|
||||
if logentry.action_type == 'pretix.event.order.print':
|
||||
return _('Position #{posid} has been printed at {datetime} with type "{type}".').format(
|
||||
posid=logentry.parsed_data.get('positionid'),
|
||||
datetime=date_format(
|
||||
dateutil.parser.parse(logentry.parsed_data["datetime"]).astimezone(sender.timezone),
|
||||
"SHORT_DATETIME_FORMAT"
|
||||
),
|
||||
type=dict(PrintLog.PRINT_TYPES)[logentry.parsed_data["type"]],
|
||||
)
|
||||
|
||||
|
||||
@receiver(signal=orderposition_blocked_display, dispatch_uid="pretixcontrol_orderposition_blocked_display")
|
||||
def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, block_name, **kwargs):
|
||||
@@ -396,6 +434,7 @@ def pretixcontrol_orderposition_blocked_display(sender: Event, orderposition, bl
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.order.deleted': _('The test mode order {code} has been deleted.'),
|
||||
'pretix.event.order.modified': _('The order details have been changed.'),
|
||||
'pretix.event.order.unpaid': _('The order has been marked as unpaid.'),
|
||||
'pretix.event.order.secret.changed': _('The order\'s secret has been changed.'),
|
||||
@@ -486,17 +525,16 @@ class VoucherRedeemedLogEntryType(VoucherLogEntryType):
|
||||
action_type = 'pretix.voucher.redeemed'
|
||||
plain = _('The voucher has been redeemed in order {order_code}.')
|
||||
|
||||
def display(self, logentry):
|
||||
data = json.loads(logentry.data)
|
||||
data = defaultdict(lambda: '?', data)
|
||||
def display(self, logentry, data):
|
||||
url = reverse('control:event.order', kwargs={
|
||||
'event': logentry.event.slug,
|
||||
'organizer': logentry.event.organizer.slug,
|
||||
'code': data['order_code']
|
||||
'code': data.get('order_code', '?')
|
||||
})
|
||||
return mark_safe(self.plain.format(
|
||||
order_code='<a href="{}">{}</a>'.format(url, data['order_code']),
|
||||
))
|
||||
return format_html(
|
||||
self.plain,
|
||||
order_code=format_html('<a href="{}">{}</a>', url, data.get('order_code', '?')),
|
||||
)
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
@@ -519,8 +557,8 @@ class CoreTaxRuleLogEntryType(TaxRuleLogEntryType):
|
||||
|
||||
|
||||
class TeamMembershipLogEntryType(LogEntryType):
|
||||
def display(self, logentry):
|
||||
return self.plain.format(user=logentry.parsed_data.get('email'))
|
||||
def display(self, logentry, data):
|
||||
return self.plain.format(user=data.get('email'))
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
@@ -537,9 +575,9 @@ class CoreTeamMembershipLogEntryType(TeamMembershipLogEntryType):
|
||||
class TeamMemberJoinedLogEntryType(LogEntryType):
|
||||
action_type = 'pretix.team.member.joined'
|
||||
|
||||
def display(self, logentry):
|
||||
def display(self, logentry, data):
|
||||
return _('{user} has joined the team using the invite sent to {email}.').format(
|
||||
user=logentry.parsed_data.get('email'), email=logentry.parsed_data.get('invite_email')
|
||||
user=data.get('email'), email=data.get('invite_email')
|
||||
)
|
||||
|
||||
|
||||
@@ -547,23 +585,23 @@ class TeamMemberJoinedLogEntryType(LogEntryType):
|
||||
class UserSettingsChangedLogEntryType(LogEntryType):
|
||||
action_type = 'pretix.user.settings.changed'
|
||||
|
||||
def display(self, logentry):
|
||||
def display(self, logentry, data):
|
||||
text = str(_('Your account settings have been changed.'))
|
||||
if 'email' in logentry.parsed_data:
|
||||
if 'email' in data:
|
||||
text = text + ' ' + str(
|
||||
_('Your email address has been changed to {email}.').format(email=logentry.parsed_data['email']))
|
||||
if 'new_pw' in logentry.parsed_data:
|
||||
_('Your email address has been changed to {email}.').format(email=data['email']))
|
||||
if 'new_pw' in data:
|
||||
text = text + ' ' + str(_('Your password has been changed.'))
|
||||
if logentry.parsed_data.get('is_active') is True:
|
||||
if data.get('is_active') is True:
|
||||
text = text + ' ' + str(_('Your account has been enabled.'))
|
||||
elif logentry.parsed_data.get('is_active') is False:
|
||||
elif data.get('is_active') is False:
|
||||
text = text + ' ' + str(_('Your account has been disabled.'))
|
||||
return text
|
||||
|
||||
|
||||
class UserImpersonatedLogEntryType(LogEntryType):
|
||||
def display(self, logentry):
|
||||
return self.plain.format(logentry.parsed_data['other_email'])
|
||||
def display(self, logentry, data):
|
||||
return self.plain.format(data['other_email'])
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
@@ -688,16 +726,13 @@ class CoreLogEntryType(LogEntryType):
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.order.deleted': _('The test mode order {code} has been deleted.'),
|
||||
'pretix.event.item_meta_property.added': _('A meta property has been added to this event.'),
|
||||
'pretix.event.item_meta_property.deleted': _('A meta property has been removed from this event.'),
|
||||
'pretix.event.item_meta_property.changed': _('A meta property has been changed on this event.'),
|
||||
'pretix.event.checkinlist.added': _('The check-in list has been added.'),
|
||||
'pretix.event.checkinlist.deleted': _('The check-in list has been deleted.'),
|
||||
'pretix.event.checkinlists.deleted': _('The check-in list has been deleted.'), # backwards compatibility
|
||||
'pretix.event.checkinlist.changed': _('The check-in list has been changed.'),
|
||||
'pretix.event.settings': _('The event settings have been changed.'),
|
||||
'pretix.event.tickets.settings': _('The ticket download settings have been changed.'),
|
||||
'pretix.event.tickets.provider': _('The settings of a ticket output provider have been changed.'),
|
||||
'pretix.event.payment.provider': _('The settings of a payment provider have been changed.'),
|
||||
'pretix.event.live.activated': _('The shop has been taken live.'),
|
||||
'pretix.event.live.deactivated': _('The shop has been taken offline.'),
|
||||
'pretix.event.testmode.activated': _('The shop has been taken into test mode.'),
|
||||
@@ -717,6 +752,19 @@ class CoreEventLogEntryType(EventLogEntryType):
|
||||
pass
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.checkinlist.added': _('The check-in list has been added.'),
|
||||
'pretix.event.checkinlist.deleted': _('The check-in list has been deleted.'),
|
||||
'pretix.event.checkinlists.deleted': _('The check-in list has been deleted.'), # backwards compatibility
|
||||
'pretix.event.checkinlist.changed': _('The check-in list has been changed.'),
|
||||
})
|
||||
class CheckinlistLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Check-in list {val}')
|
||||
object_link_viewname = 'control:event.orders.checkinlists.edit'
|
||||
object_link_argname = 'list'
|
||||
content_type = CheckinList
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
'pretix.event.plugins.enabled': _('The plugin has been enabled.'),
|
||||
'pretix.event.plugins.disabled': _('The plugin has been disabled.'),
|
||||
@@ -724,7 +772,7 @@ class CoreEventLogEntryType(EventLogEntryType):
|
||||
class EventPluginStateLogEntryType(EventLogEntryType):
|
||||
object_link_wrapper = _('Plugin {val}')
|
||||
|
||||
def get_object_link_info(self, logentry) -> dict:
|
||||
def get_object_link_info(self, logentry) -> Optional[dict]:
|
||||
if 'plugin' in logentry.parsed_data:
|
||||
app = app_cache.get(logentry.parsed_data['plugin'])
|
||||
if app and hasattr(app, 'PretixPluginMeta'):
|
||||
@@ -759,17 +807,17 @@ class CoreItemLogEntryType(ItemLogEntryType):
|
||||
'pretix.event.item.variation.changed': _('The variation "{value}" has been changed.'),
|
||||
})
|
||||
class VariationLogEntryType(ItemLogEntryType):
|
||||
def display(self, logentry):
|
||||
if 'value' not in logentry.parsed_data:
|
||||
def display(self, logentry, data):
|
||||
if 'value' not in data:
|
||||
# Backwards compatibility
|
||||
var = ItemVariation.objects.filter(id=logentry.parsed_data['id']).first()
|
||||
var = ItemVariation.objects.filter(id=data['id']).first()
|
||||
if var:
|
||||
logentry.parsed_data['value'] = str(var.value)
|
||||
data['value'] = str(var.value)
|
||||
else:
|
||||
logentry.parsed_data['value'] = '?'
|
||||
data['value'] = '?'
|
||||
else:
|
||||
logentry.parsed_data['value'] = LazyI18nString(logentry.parsed_data['value'])
|
||||
return super().display(logentry)
|
||||
data['value'] = LazyI18nString(data['value'])
|
||||
return super().display(logentry, data)
|
||||
|
||||
|
||||
@log_entry_types.new_from_dict({
|
||||
@@ -825,27 +873,27 @@ class CoreDiscountLogEntryType(DiscountLogEntryType):
|
||||
class LegacyCheckinLogEntryType(OrderLogEntryType):
|
||||
action_type = 'pretix.control.views.checkin'
|
||||
|
||||
def display(self, logentry):
|
||||
def display(self, logentry, data):
|
||||
# deprecated
|
||||
dt = dateutil.parser.parse(logentry.parsed_data.get('datetime'))
|
||||
dt = dateutil.parser.parse(data.get('datetime'))
|
||||
tz = logentry.event.timezone
|
||||
dt_formatted = date_format(dt.astimezone(tz), "SHORT_DATETIME_FORMAT")
|
||||
if 'list' in logentry.parsed_data:
|
||||
if 'list' in data:
|
||||
try:
|
||||
checkin_list = logentry.event.checkin_lists.get(pk=logentry.parsed_data.get('list')).name
|
||||
checkin_list = logentry.event.checkin_lists.get(pk=data.get('list')).name
|
||||
except CheckinList.DoesNotExist:
|
||||
checkin_list = _("(unknown)")
|
||||
else:
|
||||
checkin_list = _("(unknown)")
|
||||
|
||||
if logentry.parsed_data.get('first'):
|
||||
if data.get('first'):
|
||||
return _('Position #{posid} has been checked in manually at {datetime} on list "{list}".').format(
|
||||
posid=logentry.parsed_data.get('positionid'),
|
||||
posid=data.get('positionid'),
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list,
|
||||
)
|
||||
return _('Position #{posid} has been checked in again at {datetime} on list "{list}".').format(
|
||||
posid=logentry.parsed_data.get('positionid'),
|
||||
posid=data.get('positionid'),
|
||||
datetime=dt_formatted,
|
||||
list=checkin_list
|
||||
)
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
{% if form.hidden_if_available %}
|
||||
{% bootstrap_field form.hidden_if_available layout="control" horizontal_field_class="col-md-7" %}
|
||||
{% endif %}
|
||||
{% bootstrap_field form.hidden_if_item_available layout="control" horizontal_field_class="col-md-7" %}
|
||||
{% bootstrap_field form.hidden_if_item_available visibility_field=form.hidden_if_item_available_mode layout="control_with_visibility" %}
|
||||
</fieldset>
|
||||
{% for v in formsets.values %}
|
||||
<fieldset>
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
{% if mode == "hide" %}
|
||||
<span class="pull-right text-muted unavail-mode-indicator" data-toggle="tooltip" title="{% trans "Hide product if unavailable" %}. {% if f.variation %}{% trans "You can change this option in the variation settings." %}{% else %}{% trans "You can change this option in the product settings." %}{% endif %}"><span class="fa fa-eye-slash"></span></span>
|
||||
{% else %}
|
||||
<span class="pull-right text-muted unavail-mode-indicator" data-toggle="tooltip" title="{% trans "Show info text if unavailable" %}. {% if f.variation %}{% trans "You can change this option in the variation settings." %}{% else %}{% trans "You can change this option in the product settings." %}{% endif %}"><span class="fa fa-info-circle"></span></span>
|
||||
<span class="pull-right text-muted unavail-mode-indicator" data-toggle="tooltip" title="{% trans "Show product with info on why it’s unavailable" %}. {% if f.variation %}{% trans "You can change this option in the variation settings." %}{% else %}{% trans "You can change this option in the product settings." %}{% endif %}"><span class="fa fa-info-circle"></span></span>
|
||||
{% endif %}
|
||||
@@ -491,8 +491,11 @@ class PaymentProviderSettings(EventSettingsViewMixin, EventPermissionRequiredMix
|
||||
if self.form.is_valid():
|
||||
if self.form.has_changed():
|
||||
self.request.event.log_action(
|
||||
'pretix.event.payment.provider.' + self.provider.identifier, user=self.request.user, data={
|
||||
k: self.form.cleaned_data.get(k) for k in self.form.changed_data
|
||||
'pretix.event.payment.provider', user=self.request.user, data={
|
||||
'provider': self.provider.identifier,
|
||||
'new_values': {
|
||||
k: self.form.cleaned_data.get(k) for k in self.form.changed_data
|
||||
}
|
||||
}
|
||||
)
|
||||
self.form.save()
|
||||
@@ -888,11 +891,14 @@ class TicketSettings(EventSettingsViewMixin, EventPermissionRequiredMixin, FormV
|
||||
provider.form.save()
|
||||
if provider.form.has_changed():
|
||||
self.request.event.log_action(
|
||||
'pretix.event.tickets.provider.' + provider.identifier, user=self.request.user, data={
|
||||
k: (provider.form.cleaned_data.get(k).name
|
||||
if isinstance(provider.form.cleaned_data.get(k), File)
|
||||
else provider.form.cleaned_data.get(k))
|
||||
for k in provider.form.changed_data
|
||||
'pretix.event.tickets.provider', user=self.request.user, data={
|
||||
'provider': provider.identifier,
|
||||
'new_values': {
|
||||
k: (provider.form.cleaned_data.get(k).name
|
||||
if isinstance(provider.form.cleaned_data.get(k), File)
|
||||
else provider.form.cleaned_data.get(k))
|
||||
for k in provider.form.changed_data
|
||||
}
|
||||
}
|
||||
)
|
||||
tickets.invalidate_cache.apply_async(kwargs={'event': self.request.event.pk, 'provider': provider.identifier})
|
||||
|
||||
@@ -313,7 +313,7 @@ class EventWizard(SafeSessionWizardView):
|
||||
)
|
||||
event.set_defaults()
|
||||
|
||||
if basics_data['tax_rate']:
|
||||
if basics_data['tax_rate'] is not None:
|
||||
if not event.settings.tax_rate_default or event.settings.tax_rate_default.rate != basics_data['tax_rate']:
|
||||
event.settings.tax_rate_default = event.tax_rules.create(
|
||||
name=LazyI18nString.from_gettext(gettext('VAT')),
|
||||
|
||||
@@ -62,7 +62,8 @@ class MetricsMiddleware(object):
|
||||
t0 = time.perf_counter()
|
||||
resp = self.get_response(request)
|
||||
tdiff = time.perf_counter() - t0
|
||||
pretix_view_duration_seconds.observe(tdiff, status_code=resp.status_code, method=request.method,
|
||||
url_name=url.namespace + ':' + url.url_name)
|
||||
if url.url_name:
|
||||
pretix_view_duration_seconds.observe(tdiff, status_code=resp.status_code, method=request.method,
|
||||
url_name=url.namespace + ':' + url.url_name)
|
||||
|
||||
return resp
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-21 16:42+0000\n"
|
||||
"PO-Revision-Date: 2024-11-24 01:00+0000\n"
|
||||
"Last-Translator: gabriblas <github@unowen.simplelogin.com>\n"
|
||||
"PO-Revision-Date: 2025-01-25 22:00+0000\n"
|
||||
"Last-Translator: Rosariocastellana <rosariocastellana@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"it/>\n"
|
||||
"Language: it\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.8.3\n"
|
||||
"X-Generator: Weblate 5.9.2\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -93,7 +93,7 @@ msgstr "Italiano"
|
||||
|
||||
#: pretix/_base_settings.py:105
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
msgstr "Giapponese"
|
||||
|
||||
#: pretix/_base_settings.py:106
|
||||
msgid "Latvian"
|
||||
@@ -3415,6 +3415,8 @@ msgid ""
|
||||
"The relevant plugin is currently not active. To activate it, click here to "
|
||||
"go to the plugin settings."
|
||||
msgstr ""
|
||||
"Il plugin in questione non è al momento attivo. Per attivarlo, clicca qui "
|
||||
"per andare alle impostazioni del plugin."
|
||||
|
||||
#: pretix/base/logentrytypes.py:50
|
||||
#, fuzzy
|
||||
@@ -3425,48 +3427,48 @@ msgstr "Questo prodotto non è al momento disponibile."
|
||||
#: pretix/base/logentrytypes.py:183
|
||||
#, python-brace-format
|
||||
msgid "Order {val}"
|
||||
msgstr ""
|
||||
msgstr "Ordine {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:194
|
||||
#, python-brace-format
|
||||
msgid "Voucher {val}…"
|
||||
msgstr ""
|
||||
msgstr "Voucher {val}…"
|
||||
|
||||
#: pretix/base/logentrytypes.py:205
|
||||
#, python-brace-format
|
||||
msgid "Product {val}"
|
||||
msgstr ""
|
||||
msgstr "Prodotto {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:211
|
||||
#, python-brace-format
|
||||
msgctxt "subevent"
|
||||
msgid "Date {val}"
|
||||
msgstr ""
|
||||
msgstr "Data {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:217
|
||||
#, python-brace-format
|
||||
msgid "Quota {val}"
|
||||
msgstr ""
|
||||
msgstr "Quota {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:223
|
||||
#, python-brace-format
|
||||
msgid "Discount {val}"
|
||||
msgstr ""
|
||||
msgstr "Sconto {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:229
|
||||
#, python-brace-format
|
||||
msgid "Category {val}"
|
||||
msgstr ""
|
||||
msgstr "Categoria {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:235
|
||||
#, python-brace-format
|
||||
msgid "Question {val}"
|
||||
msgstr ""
|
||||
msgstr "Domanda {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:241
|
||||
#, python-brace-format
|
||||
msgid "Tax rule {val}"
|
||||
msgstr ""
|
||||
msgstr "Regola fiscale {val}"
|
||||
|
||||
#: pretix/base/media.py:71
|
||||
msgid "Barcode / QR-Code"
|
||||
@@ -4771,7 +4773,7 @@ msgstr ""
|
||||
|
||||
#: pretix/base/models/items.py:455
|
||||
msgid "Require either an existing or a new medium to be used"
|
||||
msgstr ""
|
||||
msgstr "Richiede l'utilizzo di un mezzo esistente o di uno nuovo"
|
||||
|
||||
#: pretix/base/models/items.py:471 pretix/base/models/items.py:1446
|
||||
msgid "Category"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-21 16:42+0000\n"
|
||||
"PO-Revision-Date: 2025-01-22 16:00+0000\n"
|
||||
"PO-Revision-Date: 2025-01-27 10:00+0000\n"
|
||||
"Last-Translator: Hijiri Umemoto <hijiri@umemoto.org>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix/"
|
||||
"ja/>\n"
|
||||
@@ -2956,7 +2956,7 @@ msgstr ""
|
||||
#: pretix/base/forms/user.py:56 pretix/control/forms/users.py:45
|
||||
#: pretix/presale/forms/customer.py:283 pretix/presale/forms/customer.py:371
|
||||
msgid "Please enter the same password twice"
|
||||
msgstr "同じパスワードを2回入力してください"
|
||||
msgstr "同じパスワードを2回入力してください"
|
||||
|
||||
#: pretix/base/forms/auth.py:172 pretix/base/forms/auth.py:224
|
||||
#: pretix/presale/forms/customer.py:296 pretix/presale/forms/customer.py:390
|
||||
@@ -3401,13 +3401,12 @@ msgstr "イベントの日程: {date_range}"
|
||||
msgid ""
|
||||
"The relevant plugin is currently not active. To activate it, click here to "
|
||||
"go to the plugin settings."
|
||||
msgstr ""
|
||||
msgstr "関連するプラグインは、現在アクティブではありません。アクティブにするには、こ"
|
||||
"こをクリックしてプラグイン設定に進んでください。"
|
||||
|
||||
#: pretix/base/logentrytypes.py:50
|
||||
#, fuzzy
|
||||
#| msgid "This product is currently not available."
|
||||
msgid "The relevant plugin is currently not active."
|
||||
msgstr "この製品は現在利用できません。"
|
||||
msgstr "関連するプラグインは、現在無効です。"
|
||||
|
||||
#: pretix/base/logentrytypes.py:183
|
||||
#, python-brace-format
|
||||
@@ -16353,22 +16352,17 @@ msgid "A user has been removed from the event team."
|
||||
msgstr "ユーザーがイベントチームから削除されました。"
|
||||
|
||||
#: pretix/control/logdisplay.py:721
|
||||
#, fuzzy
|
||||
#| msgid "A plugin has been enabled."
|
||||
msgid "The plugin has been enabled."
|
||||
msgstr "プラグインが有効になりました。"
|
||||
|
||||
#: pretix/control/logdisplay.py:722
|
||||
#, fuzzy
|
||||
#| msgid "A plugin has been disabled."
|
||||
msgid "The plugin has been disabled."
|
||||
msgstr "プラグインが無効になっています。"
|
||||
|
||||
#: pretix/control/logdisplay.py:725
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Question {val}"
|
||||
#, python-brace-format
|
||||
msgid "Plugin {val}"
|
||||
msgstr "質問{val}"
|
||||
msgstr "プラグイン{val}"
|
||||
|
||||
#: pretix/control/logdisplay.py:741
|
||||
msgid "The product has been created."
|
||||
@@ -29723,10 +29717,9 @@ msgid "An email rule was deleted"
|
||||
msgstr "電子メールのルールが削除されました"
|
||||
|
||||
#: pretix/plugins/sendmail/signals.py:140
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgid "Tax rule {val}"
|
||||
#, python-brace-format
|
||||
msgid "Mail rule {val}"
|
||||
msgstr "税金のルール {val}"
|
||||
msgstr "メールのルール {val}"
|
||||
|
||||
#: pretix/plugins/sendmail/templates/pretixplugins/sendmail/history.html:8
|
||||
msgid ""
|
||||
@@ -32173,7 +32166,7 @@ msgstr "この製品の価格が自動割引によって削減されました。
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:346
|
||||
#, python-format
|
||||
msgid "%(percent)s %% Discount"
|
||||
msgstr ""
|
||||
msgstr "%(percent)s %% 割引"
|
||||
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:277
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:350
|
||||
|
||||
@@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-21 16:42+0000\n"
|
||||
"PO-Revision-Date: 2024-11-14 20:00+0000\n"
|
||||
"Last-Translator: Gravity Fox <gravityraposa@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-01-28 01:00+0000\n"
|
||||
"Last-Translator: Cornelius Kibelka <ckibelka-ctr@wikimedia.org>\n"
|
||||
"Language-Team: Portuguese (Brazil) <https://translate.pretix.eu/projects/"
|
||||
"pretix/pretix/pt_BR/>\n"
|
||||
"Language: pt_BR\n"
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 5.8.3\n"
|
||||
"X-Generator: Weblate 5.9.2\n"
|
||||
|
||||
#: pretix/_base_settings.py:87
|
||||
msgid "English"
|
||||
@@ -93,7 +93,7 @@ msgstr "Italiano"
|
||||
|
||||
#: pretix/_base_settings.py:105
|
||||
msgid "Japanese"
|
||||
msgstr ""
|
||||
msgstr "Japonês"
|
||||
|
||||
#: pretix/_base_settings.py:106
|
||||
msgid "Latvian"
|
||||
@@ -2436,10 +2436,8 @@ msgid "Download a spreadsheet of all payments or refunds of every order."
|
||||
msgstr "Baixe uma planilha de todos os pagamentos e reembolsos de cada pedido."
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1012
|
||||
#, fuzzy
|
||||
#| msgid "Date of last payment"
|
||||
msgid "Date range (payment date)"
|
||||
msgstr "Data do último pagamento"
|
||||
msgstr "Intervalo de datas (data de pagamento)"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1015
|
||||
msgid ""
|
||||
@@ -2450,7 +2448,7 @@ msgstr ""
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1019
|
||||
msgid "Date range (start of transaction)"
|
||||
msgstr ""
|
||||
msgstr "Intervalo de datas (início da transação)"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1025
|
||||
msgid "Payment states"
|
||||
@@ -2576,10 +2574,8 @@ msgid "Current user's carts"
|
||||
msgstr "Carrinhos de usuários atuais"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1135
|
||||
#, fuzzy
|
||||
#| msgid "Paid orders"
|
||||
msgid "Exited orders"
|
||||
msgstr "Ordens pagas"
|
||||
msgstr "Pedidos encerrados"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1135
|
||||
msgid "Current availability"
|
||||
@@ -2593,22 +2589,19 @@ msgid "Infinite"
|
||||
msgstr "Infinito"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1181
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgid "Gift card transactions"
|
||||
msgstr "Cartão Presente"
|
||||
msgstr "Transações com cartões-presente"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1183
|
||||
#: pretix/base/exporters/orderlist.py:1288
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgctxt "export_category"
|
||||
msgid "Gift cards"
|
||||
msgstr "Cartão Presente"
|
||||
msgstr "Cartões-presente"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1184
|
||||
msgid "Download a spreadsheet of all gift card transactions."
|
||||
msgstr ""
|
||||
"Faça o download de uma planilha de todas as transações com cartões-presente."
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1212
|
||||
#: pretix/base/exporters/orderlist.py:1259
|
||||
@@ -2619,10 +2612,8 @@ msgstr ""
|
||||
#: pretix/control/templates/pretixcontrol/organizers/giftcard.html:28
|
||||
#: pretix/control/templates/pretixcontrol/organizers/giftcards.html:56
|
||||
#: pretix/presale/templates/pretixpresale/event/fragment_cart.html:156
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgid "Gift card code"
|
||||
msgstr "Cartão Presente"
|
||||
msgstr "Código do cartão-presente"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1213
|
||||
#: pretix/base/exporters/orderlist.py:1302
|
||||
@@ -2631,7 +2622,7 @@ msgstr "Cartão Presente"
|
||||
#: pretix/control/forms/filter.py:1413 pretix/control/forms/filter.py:1416
|
||||
#: pretix/control/templates/pretixcontrol/event/live.html:75
|
||||
msgid "Test mode"
|
||||
msgstr ""
|
||||
msgstr "Modo de teste"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1218 pretix/base/models/organizer.py:96
|
||||
#: pretix/control/forms/event.py:110 pretix/control/forms/event.py:116
|
||||
@@ -2678,41 +2669,41 @@ msgid "TEST MODE"
|
||||
msgstr "MODO DE TESTE"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1240
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgid "Gift card redemptions"
|
||||
msgstr "Cartão Presente"
|
||||
msgstr "Resgates de cartões-presente"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1242
|
||||
msgid ""
|
||||
"Download a spreadsheet of all payments or refunds that involve gift cards."
|
||||
msgstr ""
|
||||
"Faça o download de uma planilha de todos os pagamentos ou reembolsos que "
|
||||
"envolvam cartões-presente."
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1259
|
||||
#: pretix/control/templates/pretixcontrol/giftcards/payment.html:16
|
||||
msgid "Issuer"
|
||||
msgstr ""
|
||||
msgstr "Emissor"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1286 pretix/control/navigation.py:538
|
||||
#: pretix/control/navigation.py:556
|
||||
#: pretix/control/templates/pretixcontrol/organizers/edit.html:156
|
||||
#: pretix/plugins/reports/accountingreport.py:898
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgid "Gift cards"
|
||||
msgstr "Cartão Presente"
|
||||
msgstr "Cartões-presente"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1289
|
||||
msgid "Download a spreadsheet of all gift cards including their current value."
|
||||
msgstr ""
|
||||
"Faça o download de uma planilha de todos os cartões-presente, incluindo seu "
|
||||
"valor atual."
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1296
|
||||
msgid "Show value at"
|
||||
msgstr ""
|
||||
msgstr "Mostrar o valor em"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1299
|
||||
msgid "Defaults to the time of report."
|
||||
msgstr ""
|
||||
msgstr "Por padrão, a hora do relatório."
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1304
|
||||
#: pretix/base/exporters/orderlist.py:1314 pretix/control/forms/filter.py:518
|
||||
@@ -2738,20 +2729,20 @@ msgstr "Todos"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1306 pretix/control/forms/filter.py:1417
|
||||
msgid "Live"
|
||||
msgstr ""
|
||||
msgstr "Em tempo real"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1315 pretix/control/forms/filter.py:1425
|
||||
#: pretix/control/templates/pretixcontrol/pdf/index.html:252
|
||||
msgid "Empty"
|
||||
msgstr ""
|
||||
msgstr "Vazio"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1316 pretix/control/forms/filter.py:1426
|
||||
msgid "Valid and with value"
|
||||
msgstr ""
|
||||
msgstr "Válido e com valor"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1317 pretix/control/forms/filter.py:1427
|
||||
msgid "Expired and with value"
|
||||
msgstr ""
|
||||
msgstr "Expirado e com valor"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1318 pretix/control/forms/filter.py:227
|
||||
#: pretix/control/forms/filter.py:1428 pretix/control/forms/filter.py:2097
|
||||
@@ -2766,14 +2757,14 @@ msgstr "Expirado"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1356 pretix/base/models/giftcards.py:98
|
||||
msgid "Test mode card"
|
||||
msgstr ""
|
||||
msgstr "Cartão de modo de teste"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1358
|
||||
#: pretix/base/modelimport_orders.py:516 pretix/base/models/giftcards.py:102
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:202
|
||||
#: pretix/control/templates/pretixcontrol/organizers/giftcards.html:62
|
||||
msgid "Expiry date"
|
||||
msgstr ""
|
||||
msgstr "Data de expiração"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1359 pretix/control/forms/orders.py:875
|
||||
msgid "Special terms and conditions"
|
||||
@@ -2792,17 +2783,12 @@ msgid "Created in order"
|
||||
msgstr "Criado em ordem"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1363
|
||||
#, fuzzy
|
||||
#| msgctxt "invoice"
|
||||
#| msgid "Invoice number"
|
||||
msgid "Last invoice number of order"
|
||||
msgstr "Número da fatura"
|
||||
msgstr "Último número de fatura do pedido"
|
||||
|
||||
#: pretix/base/exporters/orderlist.py:1364
|
||||
#, fuzzy
|
||||
#| msgid "Only paid orders"
|
||||
msgid "Last invoice date of order"
|
||||
msgstr "Apenas ordens pagas"
|
||||
msgstr "Data da última fatura do pedido"
|
||||
|
||||
#: pretix/base/exporters/reusablemedia.py:34 pretix/control/navigation.py:616
|
||||
#: pretix/control/templates/pretixcontrol/organizers/edit.html:204
|
||||
@@ -2830,11 +2816,9 @@ msgid "Media type"
|
||||
msgstr "Tipo de mídia"
|
||||
|
||||
#: pretix/base/exporters/reusablemedia.py:47 pretix/base/models/media.py:73
|
||||
#, fuzzy
|
||||
#| msgid "Internal identifier"
|
||||
msgctxt "reusable_medium"
|
||||
msgid "Identifier"
|
||||
msgstr "Identificador interno"
|
||||
msgstr "Identificador"
|
||||
|
||||
#: pretix/base/exporters/reusablemedia.py:49 pretix/base/models/media.py:81
|
||||
#: pretix/base/models/orders.py:265 pretix/base/models/orders.py:3094
|
||||
@@ -2846,27 +2830,18 @@ msgstr "Data de validade"
|
||||
#: pretix/base/exporters/reusablemedia.py:50 pretix/base/models/media.py:90
|
||||
#: pretix/control/templates/pretixcontrol/order/index.html:215
|
||||
#: pretix/presale/templates/pretixpresale/event/checkout_confirm.html:132
|
||||
#, fuzzy
|
||||
#| msgctxt "refund_source"
|
||||
#| msgid "Customer"
|
||||
msgid "Customer account"
|
||||
msgstr "Cliente"
|
||||
msgstr "Conta do cliente"
|
||||
|
||||
#: pretix/base/exporters/reusablemedia.py:51 pretix/base/models/media.py:97
|
||||
#, fuzzy
|
||||
#| msgid "Multiline text"
|
||||
msgid "Linked ticket"
|
||||
msgstr "Texto multilinha"
|
||||
msgstr "Bilhete vinculado"
|
||||
|
||||
#: pretix/base/exporters/reusablemedia.py:52 pretix/base/models/media.py:104
|
||||
#, fuzzy
|
||||
#| msgid "Gift card"
|
||||
msgid "Linked gift card"
|
||||
msgstr "Cartão Presente"
|
||||
msgstr "Cartão-presente vinculado"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:42
|
||||
#, fuzzy
|
||||
#| msgid "Waiting list"
|
||||
msgctxt "export_category"
|
||||
msgid "Waiting list"
|
||||
msgstr "Lista de espera"
|
||||
@@ -2874,23 +2849,24 @@ msgstr "Lista de espera"
|
||||
#: pretix/base/exporters/waitinglist.py:43
|
||||
msgid "Download a spread sheet with all your waiting list data."
|
||||
msgstr ""
|
||||
"Faça o download de uma planilha com todos os dados de sua lista de espera."
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:49
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:102
|
||||
msgid "All entries"
|
||||
msgstr ""
|
||||
msgstr "Todos os registros"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:54
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:105
|
||||
msgid "Waiting for a voucher"
|
||||
msgstr ""
|
||||
msgstr "Aguardando um voucher"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:59
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:107
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:227
|
||||
#: pretix/control/views/waitinglist.py:326
|
||||
msgid "Voucher assigned"
|
||||
msgstr ""
|
||||
msgstr "Voucher atribuído"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:64
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:110
|
||||
@@ -2903,7 +2879,7 @@ msgstr "Descrição da categoria"
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:223
|
||||
#: pretix/control/views/waitinglist.py:322
|
||||
msgid "Voucher redeemed"
|
||||
msgstr ""
|
||||
msgstr "Voucher resgatado"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:80
|
||||
#: pretix/control/templates/pretixcontrol/waitinglist/index.html:116
|
||||
@@ -2918,7 +2894,7 @@ msgstr "O carrinho expirou"
|
||||
#: pretix/control/forms/event.py:1764
|
||||
#: pretix/control/templates/pretixcontrol/items/index.html:38
|
||||
msgid "Product name"
|
||||
msgstr ""
|
||||
msgstr "Nome do produto"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:115
|
||||
#: pretix/base/modelimport_orders.py:95 pretix/base/modelimport_vouchers.py:60
|
||||
@@ -2932,7 +2908,7 @@ msgstr "Data"
|
||||
#: pretix/base/exporters/waitinglist.py:119
|
||||
#: pretix/control/views/waitinglist.py:308
|
||||
msgid "Priority"
|
||||
msgstr ""
|
||||
msgstr "Prioridade"
|
||||
|
||||
#: pretix/base/exporters/waitinglist.py:121
|
||||
#: pretix/base/modelimport_vouchers.py:39 pretix/base/models/vouchers.py:190
|
||||
@@ -2951,7 +2927,7 @@ msgstr "Código do voucher"
|
||||
#: pretix/base/forms/__init__.py:118
|
||||
#, python-brace-format
|
||||
msgid "You can use {markup_name} in this field."
|
||||
msgstr ""
|
||||
msgstr "Você pode usar {markup_name} nesse campo."
|
||||
|
||||
#: pretix/base/forms/__init__.py:178
|
||||
#, python-format
|
||||
@@ -2959,6 +2935,8 @@ msgid ""
|
||||
"Due to technical reasons you cannot set inputs, that need to be masked (e.g. "
|
||||
"passwords), to %(value)s."
|
||||
msgstr ""
|
||||
"Por motivos técnicos, não é possível definir entradas que precisam ser "
|
||||
"mascaradas (por exemplo, senhas), como %(value)s."
|
||||
|
||||
#: pretix/base/forms/auth.py:61 pretix/base/forms/auth.py:179
|
||||
msgid "Keep me logged in"
|
||||
@@ -2966,12 +2944,12 @@ msgstr "Mantenha-me conectado"
|
||||
|
||||
#: pretix/base/forms/auth.py:65 pretix/base/forms/auth.py:272
|
||||
msgid "This combination of credentials is not known to our system."
|
||||
msgstr ""
|
||||
msgstr "Essa combinação de credenciais não é conhecida pelo nosso sistema."
|
||||
|
||||
#: pretix/base/forms/auth.py:66 pretix/base/forms/user.py:57
|
||||
#: pretix/presale/forms/customer.py:372 pretix/presale/forms/customer.py:444
|
||||
msgid "For security reasons, please wait 5 minutes before you try again."
|
||||
msgstr ""
|
||||
msgstr "Por motivos de segurança, aguarde 5 minutos antes de tentar novamente."
|
||||
|
||||
#: pretix/base/forms/auth.py:67 pretix/base/forms/auth.py:273
|
||||
msgid "This account is inactive."
|
||||
@@ -3018,35 +2996,43 @@ msgstr "Referência interna"
|
||||
#: pretix/base/forms/questions.py:320
|
||||
msgctxt "phonenumber"
|
||||
msgid "Phone number (without international area code)"
|
||||
msgstr ""
|
||||
msgstr "Número de telefone (sem código de área internacional)"
|
||||
|
||||
#: pretix/base/forms/questions.py:481
|
||||
msgid ""
|
||||
"You uploaded an image in landscape orientation. Please upload an image in "
|
||||
"portrait orientation."
|
||||
msgstr ""
|
||||
"Você fez o upload de uma imagem na orientação paisagem. Carregue uma imagem "
|
||||
"na orientação retrato."
|
||||
|
||||
#: pretix/base/forms/questions.py:484
|
||||
msgid "Please upload an image where the width is 3/4 of the height."
|
||||
msgstr ""
|
||||
msgstr "Carregue uma imagem em que a largura seja 3/4 da altura."
|
||||
|
||||
#: pretix/base/forms/questions.py:487
|
||||
msgid ""
|
||||
"The file you uploaded has a very large number of pixels, please upload an "
|
||||
"image no larger than 10000 x 10000 pixels."
|
||||
msgstr ""
|
||||
"O arquivo que você carregou tem um número muito grande de pixels. Carregue "
|
||||
"uma imagem que não seja maior que 10.000 x 10.000 pixels."
|
||||
|
||||
#: pretix/base/forms/questions.py:490 pretix/helpers/images.py:75
|
||||
msgid ""
|
||||
"Upload a valid image. The file you uploaded was either not an image or a "
|
||||
"corrupted image."
|
||||
msgstr ""
|
||||
"Faça upload de uma imagem válida. O arquivo que você carregou não era uma "
|
||||
"imagem ou era uma imagem corrompida."
|
||||
|
||||
#: pretix/base/forms/questions.py:633 pretix/base/forms/questions.py:642
|
||||
msgid ""
|
||||
"If you keep this empty, the ticket will be valid starting at the time of "
|
||||
"purchase."
|
||||
msgstr ""
|
||||
"Se você mantiver esse campo vazio, o bilhete será válido a partir do momento "
|
||||
"da compra."
|
||||
|
||||
#: pretix/base/forms/questions.py:689 pretix/base/forms/questions.py:1014
|
||||
msgid "Street and Number"
|
||||
@@ -3064,16 +3050,20 @@ msgid ""
|
||||
"Optional, but depending on the country you reside in we might need to charge "
|
||||
"you additional taxes if you do not enter it."
|
||||
msgstr ""
|
||||
"Opcional, mas, dependendo do país em que você reside, talvez seja necessário "
|
||||
"cobrar impostos adicionais se você não os inserir."
|
||||
|
||||
#: pretix/base/forms/questions.py:1071 pretix/base/forms/questions.py:1077
|
||||
msgid "If you are registered in Switzerland, you can enter your UID instead."
|
||||
msgstr ""
|
||||
msgstr "Se você estiver registrado na Suíça, poderá inserir seu UID."
|
||||
|
||||
#: pretix/base/forms/questions.py:1075
|
||||
msgid ""
|
||||
"Optional, but it might be required for you to claim tax benefits on your "
|
||||
"invoice depending on your and the seller’s country of residence."
|
||||
msgstr ""
|
||||
"Opcional, mas pode ser necessário para que você solicite benefícios fiscais "
|
||||
"em sua fatura, dependendo do seu país de residência e do país do vendedor."
|
||||
|
||||
#: pretix/base/forms/questions.py:1174
|
||||
msgid "You need to provide a company name."
|
||||
@@ -3109,7 +3099,7 @@ msgstr "Senha incorreta."
|
||||
|
||||
#: pretix/base/forms/user.py:58
|
||||
msgid "Please choose a password different to your current one."
|
||||
msgstr ""
|
||||
msgstr "Escolha uma senha diferente da atual."
|
||||
|
||||
#: pretix/base/forms/user.py:63 pretix/presale/forms/customer.py:379
|
||||
#: pretix/presale/forms/customer.py:448
|
||||
@@ -3164,6 +3154,10 @@ msgid ""
|
||||
"up. Please note: to use literal \"{\" or \"}\", you need to double them as "
|
||||
"\"{{\" and \"}}\"."
|
||||
msgstr ""
|
||||
"Há um erro na sintaxe de seu placeholder. Verifique se os colchetes “{” de "
|
||||
"abertura e “}” de fechamento em seus placeholders estão corretos. Observação:"
|
||||
" para usar literalmente “{” ou “}”, você precisa duplicá-los como “{{” e "
|
||||
"“}}”."
|
||||
|
||||
#: pretix/base/forms/validators.py:72 pretix/control/views/event.py:758
|
||||
#, fuzzy, python-format
|
||||
@@ -3348,7 +3342,7 @@ msgstr "Montante"
|
||||
#, python-brace-format
|
||||
msgctxt "invoice"
|
||||
msgid "Single price: {net_price} net / {gross_price} gross"
|
||||
msgstr ""
|
||||
msgstr "Preço único: {net_price} líquido / {gross_price} bruto"
|
||||
|
||||
#: pretix/base/invoice.py:668
|
||||
#, fuzzy, python-brace-format
|
||||
@@ -3372,7 +3366,7 @@ msgstr "Ordens pagas"
|
||||
#: pretix/base/invoice.py:707
|
||||
msgctxt "invoice"
|
||||
msgid "Outstanding payments"
|
||||
msgstr ""
|
||||
msgstr "Pagamentos pendentes"
|
||||
|
||||
#: pretix/base/invoice.py:724
|
||||
#, fuzzy
|
||||
@@ -3438,7 +3432,7 @@ msgstr ""
|
||||
|
||||
#: pretix/base/invoice.py:867
|
||||
msgid "Default invoice renderer (European-style letter)"
|
||||
msgstr ""
|
||||
msgstr "Renderizador de faturas padrão (letra no estilo europeu)"
|
||||
|
||||
#: pretix/base/invoice.py:956
|
||||
#, fuzzy
|
||||
@@ -3450,7 +3444,7 @@ msgstr "Você precisa selecionar uma data."
|
||||
|
||||
#: pretix/base/invoice.py:1003
|
||||
msgid "Simplified invoice renderer"
|
||||
msgstr ""
|
||||
msgstr "Renderizador de faturas simplificado"
|
||||
|
||||
#: pretix/base/invoice.py:1022
|
||||
#, fuzzy, python-brace-format
|
||||
@@ -3465,6 +3459,8 @@ msgid ""
|
||||
"The relevant plugin is currently not active. To activate it, click here to "
|
||||
"go to the plugin settings."
|
||||
msgstr ""
|
||||
"O plug-in relevante não está ativo no momento. Para ativá-lo, clique aqui "
|
||||
"para acessar as configurações do plug-in."
|
||||
|
||||
#: pretix/base/logentrytypes.py:50
|
||||
#, fuzzy
|
||||
@@ -3496,7 +3492,7 @@ msgstr "Data {val}"
|
||||
#: pretix/base/logentrytypes.py:217
|
||||
#, python-brace-format
|
||||
msgid "Quota {val}"
|
||||
msgstr ""
|
||||
msgstr "Contingente {val}"
|
||||
|
||||
#: pretix/base/logentrytypes.py:223
|
||||
#, fuzzy, python-brace-format
|
||||
@@ -3521,12 +3517,12 @@ msgstr "Norma fiscal {val}"
|
||||
|
||||
#: pretix/base/media.py:71
|
||||
msgid "Barcode / QR-Code"
|
||||
msgstr ""
|
||||
msgstr "Código de barras / código QR"
|
||||
|
||||
#: pretix/base/media.py:88
|
||||
#: pretix/control/templates/pretixcontrol/organizers/edit.html:237
|
||||
msgid "NFC UID-based"
|
||||
msgstr ""
|
||||
msgstr "Baseado em NFC UID"
|
||||
|
||||
#: pretix/base/migrations/0077_auto_20171124_1629.py:33
|
||||
#: pretix/base/migrations/0077_auto_20171124_1629_squashed_0088_auto_20180328_1217.py:35
|
||||
@@ -3535,28 +3531,28 @@ msgstr "Lista padrão"
|
||||
|
||||
#: pretix/base/modelimport.py:112
|
||||
msgid "Keep empty"
|
||||
msgstr ""
|
||||
msgstr "Manter vazio"
|
||||
|
||||
#: pretix/base/modelimport.py:139
|
||||
#, python-brace-format
|
||||
msgid "Invalid setting for column \"{header}\"."
|
||||
msgstr ""
|
||||
msgstr "Configuração inválida para a coluna “{header}”."
|
||||
|
||||
#: pretix/base/modelimport.py:199
|
||||
#, python-brace-format
|
||||
msgid "Could not parse {value} as a yes/no value."
|
||||
msgstr ""
|
||||
msgstr "Não foi possível analisar {value} como um valor sim/não."
|
||||
|
||||
#: pretix/base/modelimport.py:222
|
||||
#, python-brace-format
|
||||
msgid "Could not parse {value} as a date and time."
|
||||
msgstr ""
|
||||
msgstr "Não foi possível analisar {value} como uma data e hora."
|
||||
|
||||
#: pretix/base/modelimport.py:232 pretix/control/views/orders.py:1162
|
||||
#: pretix/control/views/orders.py:1191 pretix/control/views/orders.py:1235
|
||||
#: pretix/control/views/orders.py:1270 pretix/control/views/orders.py:1293
|
||||
msgid "You entered an invalid number."
|
||||
msgstr ""
|
||||
msgstr "Você inseriu um número inválido."
|
||||
|
||||
#: pretix/base/modelimport.py:279 pretix/base/modelimport.py:291
|
||||
#, fuzzy
|
||||
@@ -3568,7 +3564,7 @@ msgstr "Nenhum evento arquivado encontrado."
|
||||
#: pretix/base/modelimport.py:281 pretix/base/modelimport.py:293
|
||||
msgctxt "subevent"
|
||||
msgid "Multiple matching dates were found."
|
||||
msgstr ""
|
||||
msgstr "Foram encontradas várias datas correspondentes."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:85
|
||||
#, fuzzy
|
||||
@@ -3591,7 +3587,7 @@ msgstr "Nenhum evento arquivado encontrado."
|
||||
#: pretix/base/modelimport_orders.py:130
|
||||
#: pretix/base/modelimport_vouchers.py:196
|
||||
msgid "Multiple matching products were found."
|
||||
msgstr ""
|
||||
msgstr "Foram encontrados vários produtos correspondentes."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:139
|
||||
#: pretix/base/modelimport_vouchers.py:205 pretix/base/models/items.py:1245
|
||||
@@ -3611,7 +3607,7 @@ msgstr "Informações da conta alteradas"
|
||||
#: pretix/base/modelimport_vouchers.py:227
|
||||
#: pretix/base/modelimport_vouchers.py:261
|
||||
msgid "Multiple matching variations were found."
|
||||
msgstr ""
|
||||
msgstr "Foram encontradas diversas variações correspondentes."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:164
|
||||
#, fuzzy
|
||||
@@ -3700,7 +3696,7 @@ msgstr "Tipo de dispositivo"
|
||||
|
||||
#: pretix/base/modelimport_orders.py:460
|
||||
msgid "You cannot assign a position secret that already exists."
|
||||
msgstr ""
|
||||
msgstr "Não é possível atribuir um segredo de posição que já exista."
|
||||
|
||||
#: pretix/base/modelimport_orders.py:491
|
||||
#, fuzzy
|
||||
@@ -3788,7 +3784,7 @@ msgstr "Um voucher com esse código já existe."
|
||||
#: pretix/base/models/vouchers.py:196 pretix/control/views/vouchers.py:120
|
||||
#: pretix/presale/templates/pretixpresale/organizers/customer_membership.html:52
|
||||
msgid "Maximum usages"
|
||||
msgstr ""
|
||||
msgstr "Usos máximos"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:79
|
||||
#, fuzzy
|
||||
@@ -3814,20 +3810,22 @@ msgstr "Quantidade máxima por pedido"
|
||||
#: pretix/base/modelimport_vouchers.py:119 pretix/base/models/vouchers.py:225
|
||||
#: pretix/control/forms/filter.py:2106
|
||||
msgid "Reserve ticket from quota"
|
||||
msgstr ""
|
||||
msgstr "Reservar bilhete da cota"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:127 pretix/base/models/vouchers.py:233
|
||||
msgid "Allow to bypass quota"
|
||||
msgstr ""
|
||||
msgstr "Permitir ignorar a cota"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:135 pretix/base/models/vouchers.py:239
|
||||
msgid "Price mode"
|
||||
msgstr ""
|
||||
msgstr "Modo de preço"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:150
|
||||
#, python-brace-format
|
||||
msgid "Could not parse {value} as a price mode, use one of {options}."
|
||||
msgstr ""
|
||||
"Não foi possível analisar {value} como um modo de preço, use uma das "
|
||||
"{options}."
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:160 pretix/base/models/vouchers.py:245
|
||||
msgid "Voucher value"
|
||||
@@ -3835,18 +3833,18 @@ msgstr "Valor do voucher"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:165
|
||||
msgid "It is pointless to set a value without a price mode."
|
||||
msgstr ""
|
||||
msgstr "Não faz sentido definir um valor sem um modo de preço."
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:237 pretix/base/models/items.py:2081
|
||||
#: pretix/base/models/vouchers.py:272
|
||||
#: pretix/control/templates/pretixcontrol/items/quota_edit.html:8
|
||||
#: pretix/control/templates/pretixcontrol/items/quota_edit.html:15
|
||||
msgid "Quota"
|
||||
msgstr ""
|
||||
msgstr "Cota"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:253
|
||||
msgid "You cannot specify a quota if you specified a product."
|
||||
msgstr ""
|
||||
msgstr "Não é possível especificar uma cota se você especificou um produto."
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:282 pretix/base/models/vouchers.py:495
|
||||
#, fuzzy
|
||||
@@ -3869,11 +3867,9 @@ msgid "Seat-specific vouchers can only be used once."
|
||||
msgstr "Este produto não será vendido após a data indicada."
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:306 pretix/base/models/vouchers.py:519
|
||||
#, fuzzy, python-brace-format
|
||||
#| msgctxt "subevent"
|
||||
#| msgid "You need to select a date."
|
||||
#, python-brace-format
|
||||
msgid "You need to choose the product \"{prod}\" for this seat."
|
||||
msgstr "Você precisa selecionar uma data."
|
||||
msgstr "Você precisa escolher o produto “{prod}” para esse assento."
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:318 pretix/base/models/vouchers.py:285
|
||||
#: pretix/control/templates/pretixcontrol/vouchers/index.html:129
|
||||
@@ -3884,17 +3880,20 @@ msgstr "Tag"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:334 pretix/base/models/vouchers.py:297
|
||||
msgid "Shows hidden products that match this voucher"
|
||||
msgstr ""
|
||||
msgstr "Mostra produtos ocultos que correspondem a esse voucher"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:343 pretix/base/models/vouchers.py:301
|
||||
msgid "Offer all add-on products for free when redeeming this voucher"
|
||||
msgstr ""
|
||||
"Ofereça todos os produtos adicionais gratuitamente ao resgatar este voucher"
|
||||
|
||||
#: pretix/base/modelimport_vouchers.py:351 pretix/base/models/vouchers.py:305
|
||||
msgid ""
|
||||
"Include all bundled products without a designated price when redeeming this "
|
||||
"voucher"
|
||||
msgstr ""
|
||||
"Inclua todos os produtos incluídos em um pacote sem um preço designado ao "
|
||||
"resgatar esse voucher"
|
||||
|
||||
#: pretix/base/models/auth.py:248
|
||||
msgid "Is active"
|
||||
@@ -3968,22 +3967,28 @@ msgid ""
|
||||
"and valid for check-in regardless of which date they are purchased for. You "
|
||||
"can limit their validity through the advanced check-in rules, though."
|
||||
msgstr ""
|
||||
"Se você escolher “todas as datas”, os bilhetes serão considerados parte "
|
||||
"dessa lista e válidos para o check-in, independentemente da data para a qual "
|
||||
"foram comprados. No entanto, você pode limitar a validade deles por meio das "
|
||||
"regras avançadas de check-in."
|
||||
|
||||
#: pretix/base/models/checkin.py:65
|
||||
msgctxt "checkin"
|
||||
msgid "Ignore check-ins on this list in statistics"
|
||||
msgstr ""
|
||||
msgstr "Ignorar os check-ins dessa lista nas estatísticas"
|
||||
|
||||
#: pretix/base/models/checkin.py:69
|
||||
msgctxt "checkin"
|
||||
msgid "Tickets with a check-in on this list should be considered \"used\""
|
||||
msgstr ""
|
||||
msgstr "Os bilhetes com um check-in nessa lista devem ser considerados “usados”"
|
||||
|
||||
#: pretix/base/models/checkin.py:70
|
||||
msgid ""
|
||||
"This is relevant in various situations, e.g. for deciding if a ticket can "
|
||||
"still be canceled by the customer."
|
||||
msgstr ""
|
||||
"Isso é relevante em várias situações, por exemplo, para decidir se um "
|
||||
"bilhete ainda pode ser cancelado pelo cliente."
|
||||
|
||||
#: pretix/base/models/checkin.py:74
|
||||
msgctxt "checkin"
|
||||
@@ -4007,6 +4012,8 @@ msgstr ""
|
||||
#: pretix/base/models/checkin.py:79
|
||||
msgid "Allow checking in add-on tickets by scanning the main ticket"
|
||||
msgstr ""
|
||||
"Permitir o check-in de bilhetes adicionais por meio da leitura do bilhete "
|
||||
"principal"
|
||||
|
||||
#: pretix/base/models/checkin.py:81
|
||||
msgid ""
|
||||
@@ -4014,6 +4021,9 @@ msgid ""
|
||||
"there is always exactly one matching add-on ticket. Ambiguous scans will be "
|
||||
"rejected.."
|
||||
msgstr ""
|
||||
"Um escaneamento só será possível se a lista de check-in estiver configurada "
|
||||
"de forma que sempre haja exatamente um bilhete complementar correspondente. "
|
||||
"Os escaneamentos ambíguos serão rejeitados..."
|
||||
|
||||
#: pretix/base/models/checkin.py:85 pretix/control/navigation.py:640
|
||||
#: pretix/control/templates/pretixcontrol/organizers/gates.html:5
|
||||
@@ -4028,19 +4038,23 @@ msgid ""
|
||||
"Does not have any effect for the validation of tickets, only for the "
|
||||
"automatic configuration of check-in devices."
|
||||
msgstr ""
|
||||
"Não tem efeito na validação de bilhetes, apenas na configuração automática "
|
||||
"dos dispositivos de check-in."
|
||||
|
||||
#: pretix/base/models/checkin.py:90
|
||||
msgid "Allow re-entering after an exit scan"
|
||||
msgstr ""
|
||||
msgstr "Permitir o reingresso após um escaneamento de saída"
|
||||
|
||||
#: pretix/base/models/checkin.py:94
|
||||
msgid "Allow multiple entries per ticket"
|
||||
msgstr ""
|
||||
msgstr "Permitir várias entradas por bilhete"
|
||||
|
||||
#: pretix/base/models/checkin.py:95
|
||||
msgid ""
|
||||
"Use this option to turn off warnings if a ticket is scanned a second time."
|
||||
msgstr ""
|
||||
"Use essa opção para desativar os avisos se um bilhete for escaneado uma "
|
||||
"segunda vez."
|
||||
|
||||
#: pretix/base/models/checkin.py:99
|
||||
#, fuzzy
|
||||
@@ -4056,7 +4070,7 @@ msgstr "País"
|
||||
|
||||
#: pretix/base/models/checkin.py:337
|
||||
msgid "Exit"
|
||||
msgstr ""
|
||||
msgstr "Saída"
|
||||
|
||||
#: pretix/base/models/checkin.py:355
|
||||
#, fuzzy
|
||||
|
||||
@@ -51,8 +51,8 @@ def register_payment_provider(sender, **kwargs):
|
||||
class PaypalEventLogEntryType(EventLogEntryType):
|
||||
action_type = 'pretix.plugins.paypal.event'
|
||||
|
||||
def display(self, logentry):
|
||||
event_type = logentry.parsed_data.get('event_type')
|
||||
def display(self, logentry, data):
|
||||
event_type = data.get('event_type')
|
||||
text = None
|
||||
plains = {
|
||||
'PAYMENT.SALE.COMPLETED': _('Payment completed.'),
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box unavailable">
|
||||
<p><small><a href="#voucher">{% trans "Enter a voucher code below to buy this product." %}</a></small></p>
|
||||
</div>
|
||||
{% elif item.current_unavailability_reason == 'hidden_if_item_available' %}
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box unavailable">
|
||||
<p><small>{% trans "Not available yet." %}</small></p>
|
||||
</div>
|
||||
{% elif item.current_unavailability_reason == 'available_from' or var.current_unavailability_reason == 'available_from' %}
|
||||
<div class="col-md-2 col-sm-3 col-xs-6 availability-box unavailable">
|
||||
<p><small>{% trans "Not available yet." %}</small></p>
|
||||
|
||||
@@ -70,7 +70,7 @@ from pretix.base.models import (
|
||||
)
|
||||
from pretix.base.models.event import Event, SubEvent
|
||||
from pretix.base.models.items import (
|
||||
ItemAddOn, ItemBundle, SubEventItem, SubEventItemVariation,
|
||||
Item, ItemAddOn, ItemBundle, SubEventItem, SubEventItemVariation,
|
||||
)
|
||||
from pretix.base.services.placeholders import PlaceholderContext
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
@@ -302,14 +302,14 @@ def get_grouped_items(event, *, channel: SalesChannel, subevent=None, voucher=No
|
||||
|
||||
if item.hidden_if_item_available:
|
||||
if item.hidden_if_item_available.has_variations:
|
||||
dependency_available = any(
|
||||
item._dependency_available = any(
|
||||
var.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)[0] == Quota.AVAILABILITY_OK
|
||||
for var in item.hidden_if_item_available.available_variations
|
||||
)
|
||||
else:
|
||||
q = item.hidden_if_item_available.check_quotas(subevent=subevent, _cache=quota_cache, include_bundled=True)
|
||||
dependency_available = q[0] == Quota.AVAILABILITY_OK
|
||||
if dependency_available:
|
||||
item._dependency_available = q[0] == Quota.AVAILABILITY_OK
|
||||
if item._dependency_available and item.hidden_if_item_available_mode == Item.UNAVAIL_MODE_HIDDEN:
|
||||
item._remove = True
|
||||
continue
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ var strings = {
|
||||
'unavailable_available_from': django.pgettext('widget', 'Not yet available'),
|
||||
'unavailable_available_until': django.pgettext('widget', 'Not available anymore'),
|
||||
'unavailable_active': django.pgettext('widget', 'Currently not available'),
|
||||
'unavailable_hidden_if_item_available': django.pgettext('widget', 'Not yet available'),
|
||||
'order_min': django.pgettext('widget', 'minimum amount to order: %s'),
|
||||
'exit': django.pgettext('widget', 'Close ticket shop'),
|
||||
'loading_error': django.pgettext('widget', 'The ticket shop could not be loaded.'),
|
||||
@@ -229,7 +230,7 @@ Vue.component('availbox', {
|
||||
+ ' v-bind:aria-label="label_select_item"'
|
||||
+ '>'
|
||||
+ '</label>'
|
||||
+ '<div :class="count_group_classes" v-else>'
|
||||
+ '<div :class="count_group_classes" v-else role="group" v-bind:aria-label="item.name">'
|
||||
+ '<button v-if="!$root.use_native_spinners" type="button" @click.prevent.stop="on_step" data-step="-1" v-bind:data-controls="\'input_\' + input_name" class="pretix-widget-btn-default pretix-widget-item-count-dec" aria-label="' + strings.quantity_dec + '"><span>-</span></button>'
|
||||
+ '<input type="number" inputmode="numeric" pattern="\d*" class="pretix-widget-item-count-multiple" placeholder="0" min="0"'
|
||||
+ ' v-model="amount_selected" :max="order_max" :name="input_name" :id="\'input_\' + input_name"'
|
||||
@@ -722,7 +723,6 @@ var shared_methods = {
|
||||
},
|
||||
buy_callback: function (data) {
|
||||
if (data.redirect) {
|
||||
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
|
||||
if (data.cart_id) {
|
||||
this.$root.cart_id = data.cart_id;
|
||||
setCookie(this.$root.cookieName, data.cart_id, 30);
|
||||
@@ -747,7 +747,7 @@ var shared_methods = {
|
||||
}
|
||||
this.$root.overlay.frame_loading = false;
|
||||
} else {
|
||||
iframe.src = url;
|
||||
this.$root.overlay.frame_src = url;
|
||||
}
|
||||
} else {
|
||||
this.async_task_id = data.async_id;
|
||||
@@ -786,9 +786,7 @@ var shared_methods = {
|
||||
if (this.$root.additionalURLParams) {
|
||||
redirect_url += '&' + this.$root.additionalURLParams;
|
||||
}
|
||||
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
|
||||
this.$root.overlay.frame_loading = true;
|
||||
iframe.src = redirect_url;
|
||||
this.$root.overlay.frame_src = redirect_url;
|
||||
},
|
||||
voucher_open: function (voucher) {
|
||||
var redirect_url;
|
||||
@@ -800,9 +798,7 @@ var shared_methods = {
|
||||
redirect_url += '&' + this.$root.additionalURLParams;
|
||||
}
|
||||
if (this.$root.useIframe) {
|
||||
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
|
||||
this.$root.overlay.frame_loading = true;
|
||||
iframe.src = redirect_url;
|
||||
this.$root.overlay.frame_src = redirect_url;
|
||||
} else {
|
||||
window.open(redirect_url);
|
||||
}
|
||||
@@ -825,9 +821,7 @@ var shared_methods = {
|
||||
redirect_url += '&' + this.$root.additionalURLParams;
|
||||
}
|
||||
if (this.$root.useIframe) {
|
||||
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
|
||||
this.$root.overlay.frame_loading = true;
|
||||
iframe.src = redirect_url;
|
||||
this.$root.overlay.frame_src = redirect_url;
|
||||
} else {
|
||||
window.open(redirect_url);
|
||||
}
|
||||
@@ -852,27 +846,27 @@ var shared_loading_fragment = (
|
||||
);
|
||||
|
||||
var shared_iframe_fragment = (
|
||||
'<div :class="frameClasses">'
|
||||
'<div :class="frameClasses" role="dialog" aria-modal="true" >'
|
||||
+ '<div class="pretix-widget-frame-loading" v-show="$root.frame_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-frame-inner" ref="frame-container" v-show="$root.frame_shown">'
|
||||
+ '<div class="pretix-widget-frame-close"><a href="#" @click.prevent.stop="close" role="button" 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>'
|
||||
+ '</a></div>'
|
||||
+ '<iframe frameborder="0" width="650" height="650" @load="iframeLoaded" '
|
||||
+ ' :name="$root.parent.widget_id" src="about:blank" v-once'
|
||||
+ ' allow="autoplay *; camera *; fullscreen *; payment *"'
|
||||
+ ' referrerpolicy="origin">'
|
||||
+ 'Please enable frames in your browser!'
|
||||
+ '</iframe>'
|
||||
+ '<div class="pretix-widget-frame-close"><a href="#" @click.prevent.stop="close" role="button" 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>'
|
||||
+ '</a></div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
);
|
||||
|
||||
var shared_alert_fragment = (
|
||||
'<div :class="alertClasses">'
|
||||
+ '<transition name="bounce">'
|
||||
'<div :class="alertClasses" role="dialog" aria-modal="true" aria-live="polite">'
|
||||
+ '<transition name="bounce" @after-enter="focusButton">'
|
||||
+ '<div class="pretix-widget-alert-box" v-if="$root.error_message">'
|
||||
+ '<p>{{ $root.error_message }}</p>'
|
||||
+ '<p><button v-if="$root.error_url_after" @click.prevent.stop="errorContinue">' + strings.continue + '</button>'
|
||||
@@ -965,8 +959,7 @@ Vue.component('pretix-overlay', {
|
||||
window.open(this.$root.error_url_after);
|
||||
return;
|
||||
}
|
||||
var iframe = this.$refs['frame-container'].children[0];
|
||||
iframe.src = this.$root.error_url_after;
|
||||
this.$root.overlay.frame_src = this.$root.error_url_after;
|
||||
this.$root.frame_loading = true;
|
||||
this.$root.error_message = null;
|
||||
this.$root.error_url_after = null;
|
||||
@@ -974,15 +967,22 @@ Vue.component('pretix-overlay', {
|
||||
close: function () {
|
||||
this.$root.frame_shown = false;
|
||||
this.$root.parent.frame_dismissed = true;
|
||||
this.$root.frame_src = "";
|
||||
this.$root.parent.reload();
|
||||
this.$root.parent.trigger_close_callback();
|
||||
},
|
||||
iframeLoaded: function () {
|
||||
if (this.$root.frame_loading) {
|
||||
this.$root.frame_loading = false;
|
||||
this.$root.frame_shown = true;
|
||||
if (this.$root.frame_src) {
|
||||
this.$root.frame_shown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
focusButton: function () {
|
||||
this.$el.querySelector(".pretix-widget-alert-box button").focus();
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1737,8 +1737,7 @@ var shared_root_methods = {
|
||||
} else {
|
||||
url += '?iframe=1';
|
||||
}
|
||||
this.$root.overlay.$children[0].$refs['frame-container'].children[0].src = url;
|
||||
this.$root.overlay.frame_loading = true;
|
||||
this.$root.overlay.frame_src = url;
|
||||
} else {
|
||||
event.target.href = url;
|
||||
return;
|
||||
@@ -1901,9 +1900,7 @@ var shared_root_methods = {
|
||||
redirect_url += '&' + this.$root.additionalURLParams;
|
||||
}
|
||||
if (this.$root.useIframe) {
|
||||
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
|
||||
this.$root.overlay.frame_loading = true;
|
||||
iframe.src = redirect_url;
|
||||
this.$root.overlay.frame_src = redirect_url;
|
||||
} else {
|
||||
window.open(redirect_url);
|
||||
}
|
||||
@@ -1927,9 +1924,7 @@ var shared_root_methods = {
|
||||
redirect_url += '&' + this.$root.additionalURLParams;
|
||||
}
|
||||
if (this.$root.useIframe) {
|
||||
var iframe = this.$root.overlay.$children[0].$refs['frame-container'].children[0];
|
||||
this.$root.overlay.frame_loading = true;
|
||||
iframe.src = redirect_url;
|
||||
this.$root.overlay.frame_src = redirect_url;
|
||||
} else {
|
||||
window.open(redirect_url);
|
||||
}
|
||||
@@ -2066,9 +2061,41 @@ var create_overlay = function (app) {
|
||||
error_url_after_new_tab: true,
|
||||
error_message: null,
|
||||
lightbox: null,
|
||||
prevActiveElement: null,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
frame_src: String,
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
watch: {
|
||||
frame_src: function (newValue, oldValue) {
|
||||
// show loading spinner only when previously no frame_src was set
|
||||
if (newValue && !oldValue) {
|
||||
this.frame_loading = true;
|
||||
}
|
||||
// to close and unload the iframe, frame_src can be empty -> make it valid HTML with about:blank
|
||||
this.$el.querySelector("iframe").src = newValue || "about:blank";
|
||||
},
|
||||
frame_shown: function (newValue) {
|
||||
if (newValue) {
|
||||
this.prevActiveElement = document.activeElement;
|
||||
var btn = this.$el?.querySelector(".pretix-widget-frame-close a");
|
||||
this.$nextTick(function () {
|
||||
btn?.focus();
|
||||
});
|
||||
} else {
|
||||
this.prevActiveElement?.focus();
|
||||
}
|
||||
},
|
||||
error_message: function (newValue) {
|
||||
if (newValue) {
|
||||
this.prevActiveElement = document.activeElement;
|
||||
} else {
|
||||
this.prevActiveElement?.focus();
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
app.$root.overlay = framechild;
|
||||
|
||||
@@ -323,6 +323,7 @@ TEST_ITEM_RES = {
|
||||
"max_per_order": None,
|
||||
"hidden_if_available": None,
|
||||
"hidden_if_item_available": None,
|
||||
"hidden_if_item_available_mode": "hide",
|
||||
"checkin_attention": False,
|
||||
"checkin_text": None,
|
||||
"has_variations": False,
|
||||
|
||||
Reference in New Issue
Block a user