forked from CGM_Public/pretix_original
Compare commits
73 Commits
v4.15.0
...
dnspython2
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cdb992134 | |||
| 9b0b8e2061 | |||
| b6e65e7356 | |||
| 5d82305e18 | |||
| c8983ca863 | |||
| 52f6b7c971 | |||
| 809177397a | |||
| b83cb7d8c4 | |||
| bfd980fc30 | |||
| 5bc3503d04 | |||
| a582db3280 | |||
| bd4ea5d8f8 | |||
| 5dec94606b | |||
| ab97082c85 | |||
| 0723ff92ee | |||
| 15272cc3e6 | |||
| 60554dad9a | |||
| b288ea1e96 | |||
| 6a4b792501 | |||
| 8dd83e5a35 | |||
| bd5c9a4cb5 | |||
| 0cd8bbf9a9 | |||
| d46989473b | |||
| b31b2d34c0 | |||
| 5e963d87d9 | |||
| a8e0eea69a | |||
| efa9f6dfe5 | |||
| 857377d16c | |||
| 229b6fed4a | |||
| b2e4fb6db3 | |||
| 16b15057fd | |||
| e4168ff06a | |||
| b208db32c7 | |||
| ce177227c7 | |||
| 2cd70ef434 | |||
| 633755ab13 | |||
| 6ade32d7cb | |||
| cea6c340be | |||
| ad1dab3b7f | |||
| 930abe0cc5 | |||
| ba2cc56c82 | |||
| cb1f63bf80 | |||
| aab7042cda | |||
| 495a21c683 | |||
| 86b5ba6937 | |||
| 3d9679a144 | |||
| 5f899ed5c5 | |||
| 47dabc1fe7 | |||
| 2d7c4a3d42 | |||
| 51ef98f736 | |||
| 2d7d2b1a90 | |||
| cede7ba3aa | |||
| 4fd8726b05 | |||
| b344ce90ba | |||
| 69dc7f56e5 | |||
| 247a61489f | |||
| 979d23e997 | |||
| 28e529995d | |||
| a982cbf6b6 | |||
| f1c2ae5b6b | |||
| 5b27ac66f9 | |||
| c71ac2141f | |||
| e59498d65d | |||
| dfe3454915 | |||
| b64c5735a8 | |||
| 11eecd739d | |||
| 07a6d4898a | |||
| a759e23504 | |||
| 3eaf05502a | |||
| 04df1c2032 | |||
| 6a8df75a9f | |||
| 547cfdffd6 | |||
| f72a0b4c09 |
@@ -9,6 +9,7 @@ updates:
|
||||
directory: "/src"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
versioning-strategy: increase
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/src/pretix/static/npm_dir"
|
||||
schedule:
|
||||
|
||||
@@ -17,16 +17,19 @@ on:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Spellcheck
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
|
||||
@@ -15,16 +15,19 @@ on:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-22.04
|
||||
name: Check gettext syntax
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
@@ -47,10 +50,10 @@ jobs:
|
||||
name: Spellcheck
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
|
||||
@@ -15,16 +15,19 @@ on:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
isort:
|
||||
name: isort
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
@@ -42,10 +45,10 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
@@ -63,10 +66,10 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python 3.8
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
python-version: 3.9
|
||||
- name: Install Dependencies
|
||||
run: pip3 install licenseheaders
|
||||
- name: Run licenseheaders
|
||||
|
||||
@@ -15,6 +15,9 @@ on:
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -60,7 +63,7 @@ jobs:
|
||||
- name: Install system dependencies
|
||||
run: sudo apt update && sudo apt install gettext mariadb-client
|
||||
- name: Install Python dependencies
|
||||
run: pip3 install -e ".[dev]" mysqlclient psycopg2-binary
|
||||
run: pip3 install --ignore-requires-python -e ".[dev]" mysqlclient psycopg2-binary # We ignore that flake8 needs newer python as we don't run flake8 during tests
|
||||
working-directory: ./src
|
||||
- name: Run checks
|
||||
run: python manage.py check
|
||||
|
||||
@@ -318,6 +318,27 @@ example::
|
||||
(venv)$ python -m pretix rebuild
|
||||
# systemctl restart pretix-web pretix-worker
|
||||
|
||||
System updates
|
||||
--------------
|
||||
|
||||
After system updates, such as updates to a new Ubuntu or Debian release, you might be using a new Python version.
|
||||
That's great, but requires some adjustments. First, adjust any old version paths in your nginx configuration file.
|
||||
Then, re-create your Python environment::
|
||||
|
||||
$ source /var/pretix/venv/bin/activate
|
||||
(venv)$ pip3 freeze > /tmp/pip-backup.txt
|
||||
$ rm -rf /var/pretix/venv
|
||||
$ python3 -m venv /var/pretix/venv
|
||||
$ source /var/pretix/venv/bin/activate
|
||||
(venv)$ pip3 install -U pip wheel setuptools
|
||||
(venv)$ pip3 install -r /tmp/pip-backup.txt
|
||||
|
||||
Then, proceed like after any plugin installation::
|
||||
|
||||
(venv)$ python -m pretix migrate
|
||||
(venv)$ python -m pretix rebuild
|
||||
(venv)$ python -m pretix updatestyles
|
||||
# systemctl restart pretix-web pretix-worker
|
||||
|
||||
.. _Postfix: https://www.digitalocean.com/community/tutorials/how-to-install-and-configure-postfix-as-a-send-only-smtp-server-on-ubuntu-16-04
|
||||
.. _nginx: https://botleg.com/stories/https-with-lets-encrypt-and-nginx/
|
||||
|
||||
@@ -48,10 +48,11 @@ Possible permissions are:
|
||||
Compatibility
|
||||
-------------
|
||||
|
||||
We currently see pretix' API as a beta-stage feature. We therefore do not give any guarantees
|
||||
for compatibility between feature releases of pretix (such as 1.5 and 1.6). However, as always,
|
||||
we try not to break things when we don't need to. Any backwards-incompatible changes will be
|
||||
prominently noted in the release notes.
|
||||
We try to avoid any breaking changes to our API to avoid hassle on your end. If possible, we'll
|
||||
build new features in a way that keeps all pre-existing API usage unchanged. In some cases,
|
||||
this might not be possible or only possible with restrictions. In these case, any
|
||||
backwards-incompatible changes will be prominently noted in the "Changes to the REST API"
|
||||
section of our release notes. If possible, we will announce them multiple releases in advance.
|
||||
|
||||
We treat the following types of changes as *backwards-compatible* so we ask you to make sure
|
||||
that your clients can deal with them properly:
|
||||
@@ -60,6 +61,7 @@ that your clients can deal with them properly:
|
||||
* Support of new HTTP methods for a given API endpoint
|
||||
* Support of new query parameters for a given API endpoint
|
||||
* New fields contained in API responses
|
||||
* New possible values of enumeration-like fields
|
||||
* Response body structure or message texts on failed requests (``4xx``, ``5xx`` response codes)
|
||||
|
||||
We treat the following types of changes as *backwards-incompatible*:
|
||||
|
||||
@@ -43,8 +43,13 @@ available_until datetime The last date t
|
||||
hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
|
||||
redemption process, but not in the normal shop
|
||||
frontend.
|
||||
meta_data object Values set for event-specific meta data parameters.
|
||||
===================================== ========================== =======================================================
|
||||
|
||||
.. versionchanged:: 4.16
|
||||
|
||||
The ``meta_data`` attribute has been added.
|
||||
|
||||
Endpoints
|
||||
---------
|
||||
|
||||
@@ -94,6 +99,7 @@ Endpoints
|
||||
"default_price": "223.00",
|
||||
"price": 223.0,
|
||||
"original_price": null,
|
||||
"meta_data": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
@@ -108,7 +114,8 @@ Endpoints
|
||||
"description": {},
|
||||
"position": 1,
|
||||
"default_price": null,
|
||||
"price": 15.0
|
||||
"price": 15.0,
|
||||
"meta_data": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -161,7 +168,8 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"position": 0
|
||||
"position": 0,
|
||||
"meta_data": {}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to fetch
|
||||
@@ -198,7 +206,8 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"position": 0
|
||||
"position": 0,
|
||||
"meta_data": {}
|
||||
}
|
||||
|
||||
**Example response**:
|
||||
@@ -225,7 +234,8 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"position": 0
|
||||
"position": 0,
|
||||
"meta_data": {}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer of the event/item to create a variation for
|
||||
@@ -283,7 +293,8 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"position": 1
|
||||
"position": 1,
|
||||
"meta_data": {}
|
||||
}
|
||||
|
||||
:param organizer: The ``slug`` field of the organizer to modify
|
||||
|
||||
@@ -123,6 +123,7 @@ variations list of objects A list with one
|
||||
├ hide_without_voucher boolean If ``true``, this variation is only shown during the voucher
|
||||
redemption process, but not in the normal shop
|
||||
frontend.
|
||||
├ meta_data object Values set for event-specific meta data parameters.
|
||||
└ position integer An integer, used for sorting
|
||||
addons list of objects Definition of add-ons that can be chosen for this item.
|
||||
Only writable during creation,
|
||||
@@ -155,6 +156,10 @@ meta_data object Values set for
|
||||
|
||||
The attributes ``require_membership_hidden`` attribute has been added.
|
||||
|
||||
.. versionchanged:: 4.16
|
||||
|
||||
The ``variations[x].meta_data`` attribute has been added.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
@@ -247,6 +252,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
@@ -262,6 +268,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 1
|
||||
}
|
||||
],
|
||||
@@ -361,6 +368,7 @@ Endpoints
|
||||
"available_from": null,
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"meta_data": {},
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
@@ -376,6 +384,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 1
|
||||
}
|
||||
],
|
||||
@@ -455,6 +464,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
@@ -470,6 +480,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 1
|
||||
}
|
||||
],
|
||||
@@ -538,6 +549,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
@@ -553,6 +565,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 1
|
||||
}
|
||||
],
|
||||
@@ -652,6 +665,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 0
|
||||
},
|
||||
{
|
||||
@@ -667,6 +681,7 @@ Endpoints
|
||||
"available_until": null,
|
||||
"hide_without_voucher": false,
|
||||
"description": null,
|
||||
"meta_data": {},
|
||||
"position": 1
|
||||
}
|
||||
],
|
||||
|
||||
@@ -91,6 +91,7 @@ Field Type Description
|
||||
===================================== ========================== =======================================================
|
||||
id integer Internal content ID
|
||||
title multi-lingual string The content title (required)
|
||||
internal_name string An optional name that is only used in the backend
|
||||
content_type string The type of content, valid values are ``webinar``, ``video``, ``livestream``, ``link``, ``file``
|
||||
url string The location of the digital content
|
||||
file file A downloadable file. Either ``url`` or ``file`` must be ``null``.
|
||||
|
||||
@@ -19,4 +19,4 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
__version__ = "4.15.0"
|
||||
__version__ = "4.16.0.dev0"
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Generated by Django 3.2.16 on 2022-12-17 18:47
|
||||
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
import oauth2_provider.generators
|
||||
import oauth2_provider.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0226_itemvariationmetavalue'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('pretixapi', '0008_webhookcallretry'),
|
||||
]
|
||||
run_before = [
|
||||
('oauth2_provider', '0002_auto_20190406_1805'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='oauthapplication',
|
||||
name='algorithm',
|
||||
field=models.CharField(default='', max_length=5),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oauthgrant',
|
||||
name='claims',
|
||||
field=models.TextField(default=''),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oauthgrant',
|
||||
name='code_challenge',
|
||||
field=models.CharField(default='', max_length=128),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oauthgrant',
|
||||
name='code_challenge_method',
|
||||
field=models.CharField(default='', max_length=10),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oauthgrant',
|
||||
name='nonce',
|
||||
field=models.CharField(default='', max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='oauthapplication',
|
||||
name='client_secret',
|
||||
field=oauth2_provider.models.ClientSecretField(db_index=True, default=oauth2_provider.generators.generate_client_secret, max_length=255),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='OAuthIDToken',
|
||||
fields=[
|
||||
('id', models.BigAutoField(primary_key=True, serialize=False)),
|
||||
('jti', models.UUIDField(default=uuid.uuid4, unique=True)),
|
||||
('expires', models.DateTimeField()),
|
||||
('scope', models.TextField()),
|
||||
('created', models.DateTimeField(auto_now_add=True)),
|
||||
('updated', models.DateTimeField(auto_now=True)),
|
||||
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||
('organizers', models.ManyToManyField(to='pretixbase.Organizer')),
|
||||
('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='pretixapi_oauthidtoken', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='oauthaccesstoken',
|
||||
name='id_token',
|
||||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='access_token', to='pretixapi.oauthidtoken'),
|
||||
),
|
||||
]
|
||||
@@ -29,8 +29,8 @@ from oauth2_provider.generators import (
|
||||
generate_client_id, generate_client_secret,
|
||||
)
|
||||
from oauth2_provider.models import (
|
||||
AbstractAccessToken, AbstractApplication, AbstractGrant,
|
||||
AbstractRefreshToken,
|
||||
AbstractAccessToken, AbstractApplication, AbstractGrant, AbstractIDToken,
|
||||
AbstractRefreshToken, ClientSecretField,
|
||||
)
|
||||
from oauth2_provider.validators import URIValidator
|
||||
|
||||
@@ -46,7 +46,7 @@ class OAuthApplication(AbstractApplication):
|
||||
verbose_name=_("Client ID"),
|
||||
max_length=100, unique=True, default=generate_client_id, db_index=True
|
||||
)
|
||||
client_secret = models.CharField(
|
||||
client_secret = ClientSecretField(
|
||||
verbose_name=_("Client secret"),
|
||||
max_length=255, blank=False, default=generate_client_secret, db_index=True
|
||||
)
|
||||
@@ -67,12 +67,26 @@ class OAuthGrant(AbstractGrant):
|
||||
redirect_uri = models.CharField(max_length=2500) # Only 255 in AbstractGrant, which caused problems
|
||||
|
||||
|
||||
class OAuthIDToken(AbstractIDToken):
|
||||
application = models.ForeignKey(
|
||||
OAuthApplication, on_delete=models.CASCADE,
|
||||
)
|
||||
organizers = models.ManyToManyField('pretixbase.Organizer')
|
||||
|
||||
|
||||
class OAuthAccessToken(AbstractAccessToken):
|
||||
source_refresh_token = models.OneToOneField(
|
||||
# unique=True implied by the OneToOneField
|
||||
'OAuthRefreshToken', on_delete=models.SET_NULL, blank=True, null=True,
|
||||
related_name="refreshed_access_token"
|
||||
)
|
||||
id_token = models.OneToOneField(
|
||||
OAuthIDToken,
|
||||
on_delete=models.CASCADE,
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name="access_token",
|
||||
)
|
||||
application = models.ForeignKey(
|
||||
OAuthApplication, on_delete=models.CASCADE, blank=True, null=True,
|
||||
)
|
||||
|
||||
@@ -47,13 +47,14 @@ from pretix.api.serializers.fields import UploadedFileField
|
||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.base.models import (
|
||||
Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaValue, ItemVariation,
|
||||
Question, QuestionOption, Quota,
|
||||
ItemVariationMetaValue, Question, QuestionOption, Quota,
|
||||
)
|
||||
|
||||
|
||||
class InlineItemVariationSerializer(I18nAwareModelSerializer):
|
||||
price = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=10,
|
||||
coerce_to_string=True)
|
||||
meta_data = MetaDataField(required=False, source='*')
|
||||
|
||||
class Meta:
|
||||
model = ItemVariation
|
||||
@@ -61,16 +62,23 @@ class InlineItemVariationSerializer(I18nAwareModelSerializer):
|
||||
'position', 'default_price', 'price', 'original_price', 'require_approval',
|
||||
'require_membership', 'require_membership_types',
|
||||
'require_membership_hidden', 'available_from', 'available_until',
|
||||
'sales_channels', 'hide_without_voucher',)
|
||||
'sales_channels', 'hide_without_voucher', 'meta_data')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['require_membership_types'].queryset = lazy(lambda: self.context['event'].organizer.membership_types.all(), QuerySet)
|
||||
|
||||
def validate_meta_data(self, value):
|
||||
for key in value['meta_data'].keys():
|
||||
if key not in self.parent.parent.item_meta_properties:
|
||||
raise ValidationError(_('Item meta data property \'{name}\' does not exist.').format(name=key))
|
||||
return value
|
||||
|
||||
|
||||
class ItemVariationSerializer(I18nAwareModelSerializer):
|
||||
price = serializers.DecimalField(read_only=True, decimal_places=2, max_digits=10,
|
||||
coerce_to_string=True)
|
||||
meta_data = MetaDataField(required=False, source='*')
|
||||
|
||||
class Meta:
|
||||
model = ItemVariation
|
||||
@@ -78,12 +86,63 @@ class ItemVariationSerializer(I18nAwareModelSerializer):
|
||||
'position', 'default_price', 'price', 'original_price', 'require_approval',
|
||||
'require_membership', 'require_membership_types',
|
||||
'require_membership_hidden', 'available_from', 'available_until',
|
||||
'sales_channels', 'hide_without_voucher',)
|
||||
'sales_channels', 'hide_without_voucher', 'meta_data')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['require_membership_types'].queryset = self.context['event'].organizer.membership_types.all()
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
meta_data = validated_data.pop('meta_data', None)
|
||||
variation = ItemVariation.objects.create(**validated_data)
|
||||
|
||||
# Meta data
|
||||
if meta_data is not None:
|
||||
for key, value in meta_data.items():
|
||||
ItemVariationMetaValue.objects.create(
|
||||
property=self.item_meta_properties.get(key),
|
||||
value=value,
|
||||
variation=variation
|
||||
)
|
||||
return variation
|
||||
|
||||
@cached_property
|
||||
def item_meta_properties(self):
|
||||
return {
|
||||
p.name: p for p in self.context['request'].event.item_meta_properties.all()
|
||||
}
|
||||
|
||||
def validate_meta_data(self, value):
|
||||
for key in value['meta_data'].keys():
|
||||
if key not in self.item_meta_properties:
|
||||
raise ValidationError(_('Item meta data property \'{name}\' does not exist.').format(name=key))
|
||||
return value
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
meta_data = validated_data.pop('meta_data', None)
|
||||
variation = super().update(instance, validated_data)
|
||||
|
||||
# Meta data
|
||||
if meta_data is not None:
|
||||
current = {mv.property: mv for mv in variation.meta_values.select_related('property')}
|
||||
for key, value in meta_data.items():
|
||||
prop = self.item_meta_properties.get(key)
|
||||
if prop in current:
|
||||
current[prop].value = value
|
||||
current[prop].save()
|
||||
else:
|
||||
variation.meta_values.create(
|
||||
property=self.item_meta_properties.get(key),
|
||||
value=value
|
||||
)
|
||||
|
||||
for prop, current_object in current.items():
|
||||
if prop.name not in meta_data:
|
||||
current_object.delete()
|
||||
|
||||
return variation
|
||||
|
||||
|
||||
class InlineItemBundleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
@@ -263,9 +322,19 @@ class ItemSerializer(I18nAwareModelSerializer):
|
||||
|
||||
for variation_data in variations_data:
|
||||
require_membership_types = variation_data.pop('require_membership_types', [])
|
||||
var_meta_data = variation_data.pop('meta_data', {})
|
||||
v = ItemVariation.objects.create(item=item, **variation_data)
|
||||
if require_membership_types:
|
||||
v.require_membership_types.add(*require_membership_types)
|
||||
|
||||
if var_meta_data is not None:
|
||||
for key, value in var_meta_data.items():
|
||||
ItemVariationMetaValue.objects.create(
|
||||
property=self.item_meta_properties.get(key),
|
||||
value=value,
|
||||
variation=v
|
||||
)
|
||||
|
||||
for addon_data in addons_data:
|
||||
ItemAddOn.objects.create(base_item=item, **addon_data)
|
||||
for bundle_data in bundles_data:
|
||||
|
||||
@@ -371,10 +371,19 @@ class PdfDataSerializer(serializers.Field):
|
||||
for k, v in ev._cached_meta_data.items():
|
||||
res['meta:' + k] = v
|
||||
|
||||
if not hasattr(instance.item, '_cached_meta_data'):
|
||||
instance.item._cached_meta_data = instance.item.meta_data
|
||||
for k, v in instance.item._cached_meta_data.items():
|
||||
res['itemmeta:' + k] = v
|
||||
if instance.variation_id:
|
||||
print(instance, instance.variation, instance.variation_id, instance.item)
|
||||
if not hasattr(instance.variation, '_cached_meta_data'):
|
||||
instance.variation.item = instance.item # saves some database lookups
|
||||
instance.variation._cached_meta_data = instance.variation.meta_data
|
||||
print(instance.variation._cached_meta_data.items())
|
||||
for k, v in instance.variation._cached_meta_data.items():
|
||||
res['itemmeta:' + k] = v
|
||||
else:
|
||||
if not hasattr(instance.item, '_cached_meta_data'):
|
||||
instance.item._cached_meta_data = instance.item.meta_data
|
||||
for k, v in instance.item._cached_meta_data.items():
|
||||
res['itemmeta:' + k] = v
|
||||
|
||||
res['images'] = {}
|
||||
|
||||
@@ -627,7 +636,7 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
for fname, field in list(self.fields.items()):
|
||||
if fname in includes:
|
||||
continue
|
||||
elif hasattr(field, 'child'):
|
||||
elif hasattr(field, 'child'): # Nested list serializers
|
||||
found_any = False
|
||||
for childfname, childfield in list(field.child.fields.items()):
|
||||
if f'{fname}.{childfname}' not in includes:
|
||||
@@ -636,6 +645,15 @@ class OrderSerializer(I18nAwareModelSerializer):
|
||||
found_any = True
|
||||
if not found_any:
|
||||
self.fields.pop(fname)
|
||||
elif isinstance(field, serializers.Serializer): # Nested serializers
|
||||
found_any = False
|
||||
for childfname, childfield in list(field.fields.items()):
|
||||
if f'{fname}.{childfname}' not in includes:
|
||||
field.fields.pop(childfname)
|
||||
else:
|
||||
found_any = True
|
||||
if not found_any:
|
||||
self.fields.pop(fname)
|
||||
else:
|
||||
self.fields.pop(fname)
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
import importlib
|
||||
|
||||
from django.apps import apps
|
||||
from django.conf.urls import include, re_path
|
||||
from django.conf.urls import re_path
|
||||
from django.urls import include
|
||||
from rest_framework import routers
|
||||
|
||||
from pretix.api.views import cart
|
||||
|
||||
@@ -84,7 +84,9 @@ class ItemViewSet(ConditionalListView, viewsets.ModelViewSet):
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.event.items.select_related('tax_rule').prefetch_related(
|
||||
'variations', 'addons', 'bundles', 'meta_values'
|
||||
'variations', 'addons', 'bundles', 'meta_values', 'meta_values__property',
|
||||
'variations__meta_values', 'variations__meta_values__property',
|
||||
'require_membership_types', 'variations__require_membership_types',
|
||||
).all()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
@@ -147,7 +149,11 @@ class ItemVariationViewSet(viewsets.ModelViewSet):
|
||||
return get_object_or_404(Item, pk=self.kwargs['item'], event=self.request.event)
|
||||
|
||||
def get_queryset(self):
|
||||
return self.item.variations.all()
|
||||
return self.item.variations.all().prefetch_related(
|
||||
'meta_values',
|
||||
'meta_values__property',
|
||||
'require_membership_types'
|
||||
)
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
|
||||
@@ -65,9 +65,10 @@ from pretix.api.views import RichOrderingFilter
|
||||
from pretix.base.i18n import language
|
||||
from pretix.base.models import (
|
||||
CachedCombinedTicket, CachedTicket, Checkin, Device, EventMetaValue,
|
||||
Invoice, InvoiceAddress, ItemMetaValue, Order, OrderFee, OrderPayment,
|
||||
OrderPosition, OrderRefund, Quota, SubEvent, SubEventMetaValue, TaxRule,
|
||||
TeamAPIToken, generate_secret,
|
||||
Invoice, InvoiceAddress, ItemMetaValue, ItemVariation,
|
||||
ItemVariationMetaValue, Order, OrderFee, OrderPayment, OrderPosition,
|
||||
OrderRefund, Quota, SubEvent, SubEventMetaValue, TaxRule, TeamAPIToken,
|
||||
generate_secret,
|
||||
)
|
||||
from pretix.base.models.orders import QuestionAnswer, RevokedTicketSecret
|
||||
from pretix.base.payment import PaymentException
|
||||
@@ -232,7 +233,9 @@ class OrderViewSet(viewsets.ModelViewSet):
|
||||
Prefetch('item', queryset=self.request.event.items.prefetch_related(
|
||||
Prefetch('meta_values', ItemMetaValue.objects.select_related('property'), to_attr='meta_values_cached')
|
||||
)),
|
||||
'variation',
|
||||
Prefetch('variation', queryset=ItemVariation.objects.prefetch_related(
|
||||
Prefetch('meta_values', ItemVariationMetaValue.objects.select_related('property'), to_attr='meta_values_cached')
|
||||
)),
|
||||
'answers', 'answers__options', 'answers__question',
|
||||
'item__category',
|
||||
'addon_to__answers', 'addon_to__answers__options', 'addon_to__answers__question',
|
||||
@@ -999,7 +1002,11 @@ class OrderPositionViewSet(viewsets.ModelViewSet):
|
||||
Prefetch('meta_values', ItemMetaValue.objects.select_related('property'),
|
||||
to_attr='meta_values_cached')
|
||||
)),
|
||||
'variation', 'answers', 'answers__options', 'answers__question',
|
||||
Prefetch('variation', queryset=self.request.event.items.prefetch_related(
|
||||
Prefetch('meta_values', ItemVariationMetaValue.objects.select_related('property'),
|
||||
to_attr='meta_values_cached')
|
||||
)),
|
||||
'answers', 'answers__options', 'answers__question',
|
||||
'item__category',
|
||||
Prefetch('subevent', queryset=self.request.event.subevents.prefetch_related(
|
||||
Prefetch('meta_values', to_attr='meta_values_cached',
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
#
|
||||
# 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 django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from localflavor.ar.forms import ARPostalCodeField
|
||||
from localflavor.at.forms import ATZipCodeField
|
||||
from localflavor.au.forms import AUPostCodeField
|
||||
from localflavor.be.forms import BEPostalCodeField
|
||||
from localflavor.br.forms import BRZipCodeField
|
||||
from localflavor.ca.forms import CAPostalCodeField
|
||||
from localflavor.ch.forms import CHZipCodeField
|
||||
from localflavor.cn.forms import CNPostCodeField
|
||||
from localflavor.cu.forms import CUPostalCodeField
|
||||
from localflavor.cz.forms import CZPostalCodeField
|
||||
from localflavor.de.forms import DEZipCodeField
|
||||
from localflavor.dk.forms import DKPostalCodeField
|
||||
from localflavor.ee.forms import EEZipCodeField
|
||||
from localflavor.es.forms import ESPostalCodeField
|
||||
from localflavor.fi.forms import FIZipCodeField
|
||||
from localflavor.fr.forms import FRZipCodeField
|
||||
from localflavor.gb.forms import GBPostcodeField
|
||||
from localflavor.gr.forms import GRPostalCodeField
|
||||
from localflavor.hr.forms import HRPostalCodeField
|
||||
from localflavor.id_.forms import IDPostCodeField
|
||||
from localflavor.ie.forms import EircodeField
|
||||
from localflavor.il.forms import ILPostalCodeField
|
||||
from localflavor.in_.forms import INZipCodeField
|
||||
from localflavor.ir.forms import IRPostalCodeField
|
||||
from localflavor.is_.is_postalcodes import IS_POSTALCODES
|
||||
from localflavor.it.forms import ITZipCodeField
|
||||
from localflavor.jp.forms import JPPostalCodeField
|
||||
from localflavor.lt.forms import LTPostalCodeField
|
||||
from localflavor.lv.forms import LVPostalCodeField
|
||||
from localflavor.ma.forms import MAPostalCodeField
|
||||
from localflavor.mt.forms import MTPostalCodeField
|
||||
from localflavor.mx.forms import MXZipCodeField
|
||||
from localflavor.nl.forms import NLZipCodeField
|
||||
from localflavor.no.forms import NOZipCodeField
|
||||
from localflavor.nz.forms import NZPostCodeField
|
||||
from localflavor.pk.forms import PKPostCodeField
|
||||
from localflavor.pl.forms import PLPostalCodeField
|
||||
from localflavor.pt.forms import PTZipCodeField
|
||||
from localflavor.ro.forms import ROPostalCodeField
|
||||
from localflavor.ru.forms import RUPostalCodeField
|
||||
from localflavor.se.forms import SEPostalCodeField
|
||||
from localflavor.sg.forms import SGPostCodeField
|
||||
from localflavor.si.si_postalcodes import SI_POSTALCODES
|
||||
from localflavor.sk.forms import SKPostalCodeField
|
||||
from localflavor.tr.forms import TRPostalCodeField
|
||||
from localflavor.ua.forms import UAPostalCodeField
|
||||
from localflavor.us.forms import USZipCodeField
|
||||
from localflavor.za.forms import ZAPostCodeField
|
||||
|
||||
from pretix.base.settings import COUNTRIES_WITH_STATE_IN_ADDRESS
|
||||
|
||||
_validator_classes = defaultdict(list)
|
||||
|
||||
COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED = {
|
||||
# We don't presume this for countries we don't have knowledge about, there are countries in the
|
||||
# world e.g. without zipcodes
|
||||
'AR', 'AT', 'AU', 'BE', 'BR', 'CA', 'CH', 'CN', 'CU', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR',
|
||||
'GB', 'GR', 'HR', 'ID', 'IE', 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'LT', 'LV', 'MA', 'MT', 'MX',
|
||||
'NL', 'NO', 'NZ', 'PK', 'PL', 'PT', 'RO', 'RU', 'SE', 'SG', 'SI', 'SK', 'TR', 'UA', 'US', 'ZA',
|
||||
}
|
||||
|
||||
|
||||
def validate_address(address: dict, all_optional=False):
|
||||
"""
|
||||
:param address: A dictionary with at least the entries ``street``, ``zipcode``, ``city``, ``country``,
|
||||
``state``
|
||||
:return: The dictionary, possibly with changes
|
||||
"""
|
||||
if not address.get('street') and not address.get('zipcode') and not address.get('city'):
|
||||
# Consider the actual address part to be empty, no further validation necessary, if the
|
||||
# address should be required, it's the callers job to validate that at least one of these
|
||||
# fields is filled
|
||||
return address
|
||||
|
||||
if not address.get('country'):
|
||||
raise ValidationError({'country': [_('This field is required.')]})
|
||||
|
||||
if str(address['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS and not address.get('state') and not all_optional:
|
||||
raise ValidationError({'state': [_('This field is required.')]})
|
||||
|
||||
if str(address['country']) in COUNTRIES_WITH_STREET_ZIPCODE_AND_CITY_REQUIRED and not all_optional:
|
||||
for f in ('street', 'zipcode', 'city'):
|
||||
if not address.get(f):
|
||||
raise ValidationError({f: [_('This field is required.')]})
|
||||
|
||||
for klass in _validator_classes[str(address['country'])]:
|
||||
validator = klass()
|
||||
try:
|
||||
if address.get('zipcode'):
|
||||
address['zipcode'] = validator.validate_zipcode(address['zipcode'])
|
||||
except ValidationError as e:
|
||||
raise ValidationError({'zipcode': list(e)})
|
||||
|
||||
return address
|
||||
|
||||
|
||||
def register_validator_for(country):
|
||||
def inner(klass):
|
||||
_validator_classes[country].append(klass)
|
||||
return klass
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
class BaseValidator:
|
||||
required_fields = []
|
||||
|
||||
def validate_zipcode(self, value):
|
||||
return value
|
||||
|
||||
|
||||
"""
|
||||
Currently, mostly have validators that are auto-generated from django-localflavor
|
||||
but custom ones can be added like this:
|
||||
|
||||
@register_validator_for('DE')
|
||||
class DEValidator(BaseValidator):
|
||||
def validate_zipcode(value):
|
||||
return value
|
||||
|
||||
In the future, we can also add additional methods to validate that e.g. a city
|
||||
is plausible for a given zip code.
|
||||
"""
|
||||
|
||||
_zip_code_fields = {
|
||||
'AR': ARPostalCodeField,
|
||||
'AT': ATZipCodeField,
|
||||
'AU': AUPostCodeField,
|
||||
'BE': BEPostalCodeField,
|
||||
'BR': BRZipCodeField,
|
||||
'CA': CAPostalCodeField,
|
||||
'CH': CHZipCodeField,
|
||||
'CN': CNPostCodeField,
|
||||
'CU': CUPostalCodeField,
|
||||
'CZ': CZPostalCodeField,
|
||||
'DE': DEZipCodeField,
|
||||
'DK': DKPostalCodeField,
|
||||
'EE': EEZipCodeField,
|
||||
'ES': ESPostalCodeField,
|
||||
'FI': FIZipCodeField,
|
||||
'FR': FRZipCodeField,
|
||||
'GB': GBPostcodeField,
|
||||
'GR': GRPostalCodeField,
|
||||
'HR': HRPostalCodeField,
|
||||
'ID': IDPostCodeField,
|
||||
'IE': EircodeField,
|
||||
'IL': ILPostalCodeField,
|
||||
'IN': INZipCodeField,
|
||||
'IR': IRPostalCodeField,
|
||||
'IT': ITZipCodeField,
|
||||
'JP': JPPostalCodeField,
|
||||
'LT': LTPostalCodeField,
|
||||
'LV': LVPostalCodeField,
|
||||
'MA': MAPostalCodeField,
|
||||
'MT': MTPostalCodeField,
|
||||
'MX': MXZipCodeField,
|
||||
'NL': NLZipCodeField,
|
||||
'NO': NOZipCodeField,
|
||||
'NZ': NZPostCodeField,
|
||||
'PK': PKPostCodeField,
|
||||
'PL': PLPostalCodeField,
|
||||
'PT': PTZipCodeField,
|
||||
'RO': ROPostalCodeField,
|
||||
'RU': RUPostalCodeField,
|
||||
'SE': SEPostalCodeField,
|
||||
'SG': SGPostCodeField,
|
||||
'SK': SKPostalCodeField,
|
||||
'TR': TRPostalCodeField,
|
||||
'UA': UAPostalCodeField,
|
||||
'US': USZipCodeField,
|
||||
'ZA': ZAPostCodeField,
|
||||
}
|
||||
|
||||
|
||||
def _generate_class_from_zipcode_field(field_class):
|
||||
class _GeneratedValidator(BaseValidator):
|
||||
def validate_zipcode(self, value):
|
||||
return field_class().clean(value)
|
||||
return _GeneratedValidator
|
||||
|
||||
|
||||
for cc, field_class in _zip_code_fields.items():
|
||||
register_validator_for(cc)(_generate_class_from_zipcode_field(field_class))
|
||||
|
||||
|
||||
@register_validator_for('IS')
|
||||
class ISValidator(BaseValidator):
|
||||
def validate_zipcode(self, value):
|
||||
if value not in [entry[0] for entry in IS_POSTALCODES]:
|
||||
raise ValidationError(_('Enter a postal code in the format XXX.'), code='invalid')
|
||||
return value
|
||||
|
||||
|
||||
@register_validator_for('SI')
|
||||
class SIValidator(BaseValidator):
|
||||
def validate_zipcode(self, value):
|
||||
try:
|
||||
if int(value) not in [entry[0] for entry in SI_POSTALCODES]:
|
||||
raise ValidationError(_('Enter a postal code in the format XXXX.'), code='invalid')
|
||||
except ValueError:
|
||||
raise ValidationError(_('Enter a postal code in the format XXXX.'), code='invalid')
|
||||
return value
|
||||
@@ -381,6 +381,14 @@ def base_placeholders(sender, **kwargs):
|
||||
SimpleFunctionalMailTextPlaceholder(
|
||||
'currency', ['event'], lambda event: event.currency, lambda event: event.currency
|
||||
),
|
||||
SimpleFunctionalMailTextPlaceholder(
|
||||
'order_email', ['order'], lambda order: order.email, 'john@example.org'
|
||||
),
|
||||
SimpleFunctionalMailTextPlaceholder(
|
||||
'invoice_number', ['invoice'],
|
||||
lambda invoice: invoice.full_invoice_no,
|
||||
f'{sender.settings.invoice_numbers_prefix or (sender.slug.upper() + "-")}00000'
|
||||
),
|
||||
SimpleFunctionalMailTextPlaceholder(
|
||||
'refund_amount', ['event_or_subevent', 'refund_amount'],
|
||||
lambda event_or_subevent, refund_amount: LazyCurrencyNumber(refund_amount, event_or_subevent.currency),
|
||||
|
||||
@@ -29,7 +29,7 @@ from openpyxl.utils import get_column_letter
|
||||
from ...helpers.safe_openpyxl import SafeCell
|
||||
from ..channels import get_all_sales_channels
|
||||
from ..exporter import ListExporter
|
||||
from ..models import ItemMetaValue
|
||||
from ..models import ItemMetaValue, ItemVariation, ItemVariationMetaValue
|
||||
from ..signals import register_data_exporters
|
||||
|
||||
|
||||
@@ -106,18 +106,27 @@ class ItemDataExporter(ListExporter):
|
||||
yield row
|
||||
|
||||
for i in self.event.items.prefetch_related(
|
||||
'variations',
|
||||
Prefetch(
|
||||
'meta_values',
|
||||
ItemMetaValue.objects.select_related('property'),
|
||||
to_attr='meta_values_cached'
|
||||
)
|
||||
),
|
||||
Prefetch(
|
||||
'variations',
|
||||
queryset=ItemVariation.objects.prefetch_related(
|
||||
Prefetch(
|
||||
'meta_values',
|
||||
ItemVariationMetaValue.objects.select_related('property'),
|
||||
to_attr='meta_values_cached'
|
||||
),
|
||||
),
|
||||
),
|
||||
).select_related('category', 'tax_rule'):
|
||||
m = i.meta_data
|
||||
vars = list(i.variations.all())
|
||||
|
||||
if vars:
|
||||
for v in vars:
|
||||
m = v.meta_data
|
||||
row = [
|
||||
i.pk,
|
||||
v.pk,
|
||||
@@ -160,6 +169,7 @@ class ItemDataExporter(ListExporter):
|
||||
yield row
|
||||
|
||||
else:
|
||||
m = i.meta_data
|
||||
row = [
|
||||
i.pk,
|
||||
"",
|
||||
|
||||
@@ -36,9 +36,11 @@ import json
|
||||
from decimal import Decimal
|
||||
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db.models import Prefetch
|
||||
from django.dispatch import receiver
|
||||
|
||||
from ..exporter import BaseExporter
|
||||
from ..models import ItemMetaValue, ItemVariation, ItemVariationMetaValue
|
||||
from ..signals import register_data_exporters
|
||||
|
||||
|
||||
@@ -106,9 +108,26 @@ class JSONExporter(BaseExporter):
|
||||
'available_from': variation.available_from,
|
||||
'available_until': variation.available_until,
|
||||
'hide_without_voucher': variation.hide_without_voucher,
|
||||
'meta_data': variation.meta_data,
|
||||
} for variation in item.variations.all()
|
||||
]
|
||||
} for item in self.event.items.select_related('tax_rule').prefetch_related('variations')
|
||||
} for item in self.event.items.select_related('tax_rule').prefetch_related(
|
||||
Prefetch(
|
||||
'meta_values',
|
||||
ItemMetaValue.objects.select_related('property'),
|
||||
to_attr='meta_values_cached'
|
||||
),
|
||||
Prefetch(
|
||||
'variations',
|
||||
queryset=ItemVariation.objects.prefetch_related(
|
||||
Prefetch(
|
||||
'meta_values',
|
||||
ItemVariationMetaValue.objects.select_related('property'),
|
||||
to_attr='meta_values_cached'
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
'questions': [
|
||||
{
|
||||
|
||||
@@ -135,6 +135,10 @@ class NamePartsWidget(forms.MultiWidget):
|
||||
data.append(value.get(fname, ""))
|
||||
if '_legacy' in value and not data[-1]:
|
||||
data[-1] = value.get('_legacy', '')
|
||||
elif not any(d for d in data) and '_scheme' in value:
|
||||
scheme = PERSON_NAME_SCHEMES[value['_scheme']]
|
||||
data[-1] = scheme['concatenation'](value).strip()
|
||||
|
||||
return data
|
||||
|
||||
def render(self, name: str, value, attrs=None, renderer=None) -> str:
|
||||
@@ -915,6 +919,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
|
||||
class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
vat_warning = False
|
||||
address_validation = False
|
||||
|
||||
class Meta:
|
||||
model = InvoiceAddress
|
||||
@@ -1050,6 +1055,9 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
v.widget.attrs['autocomplete'] = 'section-invoice billing ' + v.widget.attrs.get('autocomplete', '')
|
||||
|
||||
def clean(self):
|
||||
from pretix.base.addressvalidation import \
|
||||
validate_address # local import to prevent impact on startup time
|
||||
|
||||
data = self.cleaned_data
|
||||
if not data.get('is_business'):
|
||||
data['company'] = ''
|
||||
@@ -1065,9 +1073,8 @@ class BaseInvoiceAddressForm(forms.ModelForm):
|
||||
if 'vat_id' in self.changed_data or not data.get('vat_id'):
|
||||
self.instance.vat_id_validated = False
|
||||
|
||||
if data.get('city') and data.get('country') and str(data['country']) in COUNTRIES_WITH_STATE_IN_ADDRESS:
|
||||
if not data.get('state'):
|
||||
self.add_error('state', _('This field is required.'))
|
||||
if self.address_validation:
|
||||
self.cleaned_data = data = validate_address(data, self.all_optional)
|
||||
|
||||
self.instance.name_parts = data.get('name_parts')
|
||||
|
||||
|
||||
+18
-12
@@ -242,6 +242,12 @@ class BaseReportlabInvoiceRenderer(BaseInvoiceRenderer):
|
||||
buffer.seek(0)
|
||||
return 'invoice.pdf', 'application/pdf', buffer.read()
|
||||
|
||||
def _clean_text(self, text, tags=None):
|
||||
return bleach.clean(
|
||||
text,
|
||||
tags=tags or []
|
||||
).strip().replace('<br>', '<br />').replace('\n', '<br />\n')
|
||||
|
||||
|
||||
class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
identifier = 'classic'
|
||||
@@ -266,7 +272,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
invoice_to_top = 52 * mm
|
||||
|
||||
def _draw_invoice_to(self, canvas):
|
||||
p = Paragraph(bleach.clean(self.invoice.address_invoice_to, tags=[]).strip().replace('\n', '<br />\n'),
|
||||
p = Paragraph(self._clean_text(self.invoice.address_invoice_to),
|
||||
style=self.stylesheet['Normal'])
|
||||
p.wrapOn(canvas, self.invoice_to_width, self.invoice_to_height)
|
||||
p_size = p.wrap(self.invoice_to_width, self.invoice_to_height)
|
||||
@@ -279,7 +285,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
def _draw_invoice_from(self, canvas):
|
||||
p = Paragraph(
|
||||
bleach.clean(self.invoice.full_invoice_from, tags=[]).strip().replace('\n', '<br />\n'),
|
||||
self._clean_text(self.invoice.full_invoice_from),
|
||||
style=self.stylesheet['InvoiceFrom']
|
||||
)
|
||||
p.wrapOn(canvas, self.invoice_from_width, self.invoice_from_height)
|
||||
@@ -474,8 +480,8 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
if self.invoice.custom_field:
|
||||
story.append(Paragraph(
|
||||
'{}: {}'.format(
|
||||
bleach.clean(str(self.invoice.event.settings.invoice_address_custom_field), tags=[]).strip().replace('\n', '<br />\n'),
|
||||
bleach.clean(self.invoice.custom_field, tags=[]).strip().replace('\n', '<br />\n'),
|
||||
self._clean_text(str(self.invoice.event.settings.invoice_address_custom_field)),
|
||||
self._clean_text(self.invoice.custom_field),
|
||||
),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
@@ -483,7 +489,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
if self.invoice.internal_reference:
|
||||
story.append(Paragraph(
|
||||
pgettext('invoice', 'Customer reference: {reference}').format(
|
||||
reference=bleach.clean(self.invoice.internal_reference, tags=[]).strip().replace('\n', '<br />\n'),
|
||||
reference=self._clean_text(self.invoice.internal_reference),
|
||||
),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
@@ -491,20 +497,20 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
if self.invoice.invoice_to_vat_id:
|
||||
story.append(Paragraph(
|
||||
pgettext('invoice', 'Customer VAT ID') + ': ' +
|
||||
bleach.clean(self.invoice.invoice_to_vat_id, tags=[]).replace("\n", "<br />\n"),
|
||||
self._clean_text(self.invoice.invoice_to_vat_id),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.invoice_to_beneficiary:
|
||||
story.append(Paragraph(
|
||||
pgettext('invoice', 'Beneficiary') + ':<br />' +
|
||||
bleach.clean(self.invoice.invoice_to_beneficiary, tags=[]).replace("\n", "<br />\n"),
|
||||
self._clean_text(self.invoice.invoice_to_beneficiary),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
|
||||
if self.invoice.introductory_text:
|
||||
story.append(Paragraph(
|
||||
self.invoice.introductory_text,
|
||||
self._clean_text(self.invoice.introductory_text, tags=['br']),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
story.append(Spacer(1, 10 * mm))
|
||||
@@ -571,7 +577,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
description = description + "\n" + single_price_line
|
||||
tdata.append((
|
||||
Paragraph(
|
||||
bleach.clean(description, tags=['br']).strip().replace('<br>', '<br/>').replace('\n', '<br />\n'),
|
||||
self._clean_text(description, tags=['br']),
|
||||
self.stylesheet['Normal']
|
||||
),
|
||||
str(len(lines)),
|
||||
@@ -587,7 +593,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
description = description + "\n" + single_price_line
|
||||
tdata.append((
|
||||
Paragraph(
|
||||
bleach.clean(description, tags=['br']).strip().replace('<br>', '<br/>').replace('\n', '<br />\n'),
|
||||
self._clean_text(description, tags=['br']),
|
||||
self.stylesheet['Normal']
|
||||
),
|
||||
str(len(lines)),
|
||||
@@ -657,7 +663,7 @@ class ClassicInvoiceRenderer(BaseReportlabInvoiceRenderer):
|
||||
|
||||
if self.invoice.additional_text:
|
||||
story.append(Paragraph(
|
||||
self.invoice.additional_text,
|
||||
self._clean_text(self.invoice.additional_text, tags=['br']),
|
||||
self.stylesheet['Normal']
|
||||
))
|
||||
story.append(Spacer(1, 5 * mm))
|
||||
@@ -794,7 +800,7 @@ class Modern1Renderer(ClassicInvoiceRenderer):
|
||||
if not self.invoice.invoice_from:
|
||||
return
|
||||
c = [
|
||||
bleach.clean(l, tags=[]).strip().replace('\n', '<br />\n')
|
||||
self._clean_text(l)
|
||||
for l in self.invoice.address_invoice_from.strip().split('\n')
|
||||
]
|
||||
p = Paragraph(' · '.join(c), style=self.stylesheet['Sender'])
|
||||
|
||||
@@ -224,6 +224,11 @@ def _merge_csp(a, b):
|
||||
if k not in a:
|
||||
a[k] = b[k]
|
||||
|
||||
for k, v in a.items():
|
||||
if "'unsafe-inline'" in v:
|
||||
# If we need unsafe-inline, drop any hashes or nonce as they will be ignored otherwise
|
||||
a[k] = [i for i in v if not i.startswith("'nonce-") and not i.startswith("'sha-")]
|
||||
|
||||
|
||||
class SecurityMiddleware(MiddlewareMixin):
|
||||
CSP_EXEMPT = (
|
||||
@@ -301,7 +306,7 @@ class SecurityMiddleware(MiddlewareMixin):
|
||||
resp['Content-Security-Policy'] = _render_csp(h).format(static=staticdomain, dynamic=dynamicdomain,
|
||||
media=mediadomain)
|
||||
for k, v in h.items():
|
||||
h[k] = ' '.join(v).format(static=staticdomain, dynamic=dynamicdomain, media=mediadomain).split(' ')
|
||||
h[k] = sorted(set(' '.join(v).format(static=staticdomain, dynamic=dynamicdomain, media=mediadomain).split(' ')))
|
||||
resp['Content-Security-Policy'] = _render_csp(h)
|
||||
elif 'Content-Security-Policy' in resp:
|
||||
del resp['Content-Security-Policy']
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Generated by Django 3.2.16 on 2022-12-09 10:06
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
import pretix.base.models.base
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pretixbase', '0225_orderpayment_process_initiated'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ItemVariationMetaValue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
|
||||
('value', models.TextField()),
|
||||
('property', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='variation_values', to='pretixbase.itemmetaproperty')),
|
||||
('variation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='meta_values', to='pretixbase.itemvariation')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('variation', 'property')},
|
||||
},
|
||||
bases=(models.Model, pretix.base.models.base.LoggingMixin),
|
||||
),
|
||||
]
|
||||
@@ -34,8 +34,8 @@ from .giftcards import GiftCard, GiftCardAcceptance, GiftCardTransaction
|
||||
from .invoices import Invoice, InvoiceLine, invoice_filename
|
||||
from .items import (
|
||||
Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaProperty, ItemMetaValue,
|
||||
ItemVariation, Question, QuestionOption, Quota, SubEventItem,
|
||||
SubEventItemVariation, itempicture_upload_to,
|
||||
ItemVariation, ItemVariationMetaValue, Question, QuestionOption, Quota,
|
||||
SubEventItem, SubEventItemVariation, itempicture_upload_to,
|
||||
)
|
||||
from .log import LogEntry
|
||||
from .memberships import Membership, MembershipType
|
||||
|
||||
@@ -28,6 +28,7 @@ from typing import Dict, Optional, Tuple
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, pgettext_lazy
|
||||
from django_scopes import ScopedManager
|
||||
|
||||
@@ -198,6 +199,14 @@ class Discount(LoggedModel):
|
||||
'subevent_mode': self.subevent_mode,
|
||||
})
|
||||
|
||||
def is_available_by_time(self, now_dt=None) -> bool:
|
||||
now_dt = now_dt or now()
|
||||
if self.available_from and self.available_from > now_dt:
|
||||
return False
|
||||
if self.available_until and self.available_until < now_dt:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _apply_min_value(self, positions, idx_group, result):
|
||||
if self.condition_min_value and sum(positions[idx][2] for idx in idx_group) < self.condition_min_value:
|
||||
return
|
||||
|
||||
@@ -728,7 +728,7 @@ class Event(EventMixin, LoggedModel):
|
||||
from ..signals import event_copy_data
|
||||
from . import (
|
||||
Discount, Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaValue,
|
||||
Question, Quota,
|
||||
ItemVariationMetaValue, Question, Quota,
|
||||
)
|
||||
|
||||
# Note: avoid self.set_active_plugins(), it causes trouble e.g. for the badges plugin.
|
||||
@@ -804,12 +804,18 @@ class Event(EventMixin, LoggedModel):
|
||||
v.item = i
|
||||
v.save(force_insert=True)
|
||||
|
||||
for imv in ItemMetaValue.objects.filter(item__event=other).prefetch_related('item', 'property'):
|
||||
for imv in ItemMetaValue.objects.filter(item__event=other):
|
||||
imv.pk = None
|
||||
imv.property = item_meta_properties_map[imv.property.pk]
|
||||
imv.property = item_meta_properties_map[imv.property_id]
|
||||
imv.item = item_map[imv.item.pk]
|
||||
imv.save(force_insert=True)
|
||||
|
||||
for imv in ItemVariationMetaValue.objects.filter(variation__item__event=other):
|
||||
imv.pk = None
|
||||
imv.property = item_meta_properties_map[imv.property_id]
|
||||
imv.variation = variation_map[imv.variation_id]
|
||||
imv.save(force_insert=True)
|
||||
|
||||
for ia in ItemAddOn.objects.filter(base_item__event=other).prefetch_related('base_item', 'addon_category'):
|
||||
ia.pk = None
|
||||
ia.base_item = item_map[ia.base_item.pk]
|
||||
|
||||
@@ -1008,6 +1008,16 @@ class ItemVariation(models.Model):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def meta_data(self):
|
||||
data = self.item.meta_data
|
||||
if hasattr(self, 'meta_values_cached'):
|
||||
data.update({v.property.name: v.value for v in self.meta_values_cached})
|
||||
else:
|
||||
data.update({v.property.name: v.value for v in self.meta_values.select_related('property').all()})
|
||||
|
||||
return OrderedDict((k, v) for k, v in sorted(data.items(), key=lambda k: k[0]))
|
||||
|
||||
|
||||
class ItemAddOn(models.Model):
|
||||
"""
|
||||
@@ -1787,8 +1797,21 @@ class ItemMetaValue(LoggedModel):
|
||||
class Meta:
|
||||
unique_together = ('item', 'property')
|
||||
|
||||
def delete(self, *args, **kwargs):
|
||||
super().delete(*args, **kwargs)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
class ItemVariationMetaValue(LoggedModel):
|
||||
"""
|
||||
A meta-data value assigned to an item variation, overriding the value on the item.
|
||||
|
||||
:param variation: The variation this metadata is valid for
|
||||
:type variation: ItemVariation
|
||||
:param property: The property this value belongs to
|
||||
:type property: ItemMetaProperty
|
||||
:param value: The actual value
|
||||
:type value: str
|
||||
"""
|
||||
variation = models.ForeignKey('ItemVariation', on_delete=models.CASCADE, related_name='meta_values')
|
||||
property = models.ForeignKey('ItemMetaProperty', on_delete=models.CASCADE, related_name='variation_values')
|
||||
value = models.TextField()
|
||||
|
||||
class Meta:
|
||||
unique_together = ('variation', 'property')
|
||||
|
||||
@@ -80,6 +80,7 @@ from pretix.base.settings import PERSON_NAME_SCHEMES
|
||||
from pretix.base.signals import order_gracefully_delete
|
||||
|
||||
from ...helpers.countries import CachedCountries, FastCountryField
|
||||
from ...helpers.format import format_map
|
||||
from ._transactions import (
|
||||
_fail, _transactions_mark_order_clean, _transactions_mark_order_dirty,
|
||||
)
|
||||
@@ -996,7 +997,7 @@ class Order(LockModel, LoggedModel):
|
||||
position and the attendee email will be used if available.
|
||||
"""
|
||||
from pretix.base.services.mail import (
|
||||
SendMailException, TolerantDict, mail, render_mail,
|
||||
SendMailException, mail, render_mail,
|
||||
)
|
||||
|
||||
if not self.email and not (position and position.attendee_email):
|
||||
@@ -1012,7 +1013,7 @@ class Order(LockModel, LoggedModel):
|
||||
|
||||
try:
|
||||
email_content = render_mail(template, context)
|
||||
subject = str(subject).format_map(TolerantDict(context))
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.locale, self, headers=headers, sender=sender,
|
||||
@@ -1651,7 +1652,7 @@ class OrderPayment(models.Model):
|
||||
}, user=user, auth=auth)
|
||||
|
||||
def confirm(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
|
||||
ignore_date=False, lock=True, payment_date=None):
|
||||
ignore_date=False, lock=True, payment_date=None, generate_invoice=True):
|
||||
"""
|
||||
Marks the payment as complete. If possible, this also marks the order as paid if no further
|
||||
payment is required
|
||||
@@ -1714,10 +1715,11 @@ class OrderPayment(models.Model):
|
||||
))
|
||||
return
|
||||
|
||||
self._mark_order_paid(count_waitinglist, send_mail, force, user, auth, mail_text, ignore_date, lock, payment_sum - refund_sum)
|
||||
self._mark_order_paid(count_waitinglist, send_mail, force, user, auth, mail_text, ignore_date, lock, payment_sum - refund_sum,
|
||||
generate_invoice)
|
||||
|
||||
def _mark_order_paid(self, count_waitinglist=True, send_mail=True, force=False, user=None, auth=None, mail_text='',
|
||||
ignore_date=False, lock=True, payment_refund_sum=0):
|
||||
ignore_date=False, lock=True, payment_refund_sum=0, allow_generate_invoice=True):
|
||||
from pretix.base.services.invoices import (
|
||||
generate_invoice, invoice_qualified,
|
||||
)
|
||||
@@ -1734,7 +1736,7 @@ class OrderPayment(models.Model):
|
||||
ignore_date=ignore_date)
|
||||
|
||||
invoice = None
|
||||
if invoice_qualified(self.order):
|
||||
if invoice_qualified(self.order) and allow_generate_invoice:
|
||||
invoices = self.order.invoices.filter(is_cancellation=False).count()
|
||||
cancellations = self.order.invoices.filter(is_cancellation=True).count()
|
||||
gen_invoice = (
|
||||
@@ -2413,7 +2415,7 @@ class OrderPosition(AbstractPosition):
|
||||
:param attach_ical: Attach relevant ICS files
|
||||
"""
|
||||
from pretix.base.services.mail import (
|
||||
SendMailException, TolerantDict, mail, render_mail,
|
||||
SendMailException, mail, render_mail,
|
||||
)
|
||||
|
||||
if not self.attendee_email:
|
||||
@@ -2426,7 +2428,7 @@ class OrderPosition(AbstractPosition):
|
||||
recipient = self.attendee_email
|
||||
try:
|
||||
email_content = render_mail(template, context)
|
||||
subject = str(subject).format_map(TolerantDict(context))
|
||||
subject = format_map(subject, context)
|
||||
mail(
|
||||
recipient, subject, template, context,
|
||||
self.event, self.order.locale, order=self.order, headers=headers, sender=sender,
|
||||
|
||||
+18
-15
@@ -68,6 +68,7 @@ from pretix.base.signals import register_payment_providers
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.base.templatetags.rich_text import rich_text
|
||||
from pretix.helpers.countries import CachedCountries
|
||||
from pretix.helpers.format import format_map
|
||||
from pretix.helpers.money import DecimalTextInput
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.presale.views import get_cart, get_cart_total
|
||||
@@ -175,7 +176,9 @@ class BasePaymentProvider:
|
||||
"""
|
||||
Set this to ``True`` if your ``execute_payment`` function needs to be triggered by a user request, i.e. either
|
||||
needs the ``request`` object or might require a browser redirect. If this is ``False``, you will not receive
|
||||
a ``request`` and may not redirect since execute_payment might be called server-side.
|
||||
a ``request`` and may not redirect since execute_payment might be called server-side. You should ensure that
|
||||
your ``execute_payment`` method has a limited execution time (i.e. by using ``timeout`` for all external calls)
|
||||
and handles all error cases appropriately.
|
||||
"""
|
||||
return True
|
||||
|
||||
@@ -322,16 +325,6 @@ class BasePaymentProvider:
|
||||
help_text=_('Users will not be able to choose this payment provider after the given date.'),
|
||||
required=False,
|
||||
)),
|
||||
('_invoice_text',
|
||||
I18nFormField(
|
||||
label=_('Text on invoices'),
|
||||
help_text=_('Will be printed just below the payment figures and above the closing text on invoices. '
|
||||
'This will only be used if the invoice is generated before the order is paid. If the '
|
||||
'invoice is generated later, it will show a text stating that it has already been paid.'),
|
||||
required=False,
|
||||
widget=I18nTextarea,
|
||||
widget_kwargs={'attrs': {'rows': '2'}}
|
||||
)),
|
||||
('_total_min',
|
||||
forms.DecimalField(
|
||||
label=_('Minimum order total'),
|
||||
@@ -379,6 +372,16 @@ class BasePaymentProvider:
|
||||
'above!').format(docs_url='https://docs.pretix.eu/en/latest/user/payments/fees.html'),
|
||||
required=False
|
||||
)),
|
||||
('_invoice_text',
|
||||
I18nFormField(
|
||||
label=_('Text on invoices'),
|
||||
help_text=_('Will be printed just below the payment figures and above the closing text on invoices. '
|
||||
'This will only be used if the invoice is generated before the order is paid. If the '
|
||||
'invoice is generated later, it will show a text stating that it has already been paid.'),
|
||||
required=False,
|
||||
widget=I18nTextarea,
|
||||
widget_kwargs={'attrs': {'rows': '2'}}
|
||||
)),
|
||||
('_restricted_countries',
|
||||
forms.MultipleChoiceField(
|
||||
label=_('Restrict to countries'),
|
||||
@@ -1120,12 +1123,12 @@ class ManualPayment(BasePaymentProvider):
|
||||
}
|
||||
|
||||
def order_pending_mail_render(self, order, payment) -> str:
|
||||
msg = str(self.settings.get('email_instructions', as_type=LazyI18nString)).format_map(self.format_map(order, payment))
|
||||
msg = format_map(self.settings.get('email_instructions', as_type=LazyI18nString), self.format_map(order, payment))
|
||||
return msg
|
||||
|
||||
def payment_pending_render(self, request, payment) -> str:
|
||||
return rich_text(
|
||||
str(self.settings.get('pending_description', as_type=LazyI18nString)).format_map(self.format_map(payment.order, payment))
|
||||
format_map(self.settings.get('pending_description', as_type=LazyI18nString), self.format_map(payment.order, payment))
|
||||
)
|
||||
|
||||
|
||||
@@ -1385,7 +1388,7 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
except GiftCard.MultipleObjectsReturned:
|
||||
messages.error(request, _("This gift card can not be redeemed since its code is not unique. Please contact the organizer of this event."))
|
||||
|
||||
def execute_payment(self, request: HttpRequest, payment: OrderPayment) -> str:
|
||||
def execute_payment(self, request: HttpRequest, payment: OrderPayment, is_early_special_case=False) -> str:
|
||||
for p in payment.order.positions.all():
|
||||
if p.item.issue_giftcard:
|
||||
raise PaymentException(_("You cannot pay with gift cards when buying a gift card."))
|
||||
@@ -1421,7 +1424,7 @@ class GiftCardPayment(BasePaymentProvider):
|
||||
'gift_card': gc.pk,
|
||||
'transaction_id': trans.pk,
|
||||
}
|
||||
payment.confirm()
|
||||
payment.confirm(send_mail=not is_early_special_case, generate_invoice=not is_early_special_case)
|
||||
except PaymentException as e:
|
||||
payment.fail(info={'error': str(e)})
|
||||
raise e
|
||||
|
||||
@@ -746,6 +746,8 @@ class Renderer:
|
||||
|
||||
def replace(x):
|
||||
if x.group(1).startswith('itemmeta:'):
|
||||
if op.variation_id:
|
||||
return op.variation.meta_data.get(x.group(1)[9:]) or ''
|
||||
return op.item.meta_data.get(x.group(1)[9:]) or ''
|
||||
elif x.group(1).startswith('meta:'):
|
||||
return ev.meta_data.get(x.group(1)[5:]) or ''
|
||||
@@ -766,6 +768,8 @@ class Renderer:
|
||||
return re.sub(r'\{([a-zA-Z0-9:_]+)\}', replace, text)
|
||||
|
||||
elif o['content'].startswith('itemmeta:'):
|
||||
if op.variation_id:
|
||||
return op.variation.meta_data.get(o['content'][9:]) or ''
|
||||
return op.item.meta_data.get(o['content'][9:]) or ''
|
||||
|
||||
elif o['content'].startswith('meta:'):
|
||||
|
||||
@@ -65,7 +65,14 @@ def get_all_plugins(event=None) -> List[type]:
|
||||
)
|
||||
|
||||
|
||||
class PluginConfig(AppConfig):
|
||||
class PluginConfigMeta(type):
|
||||
def __getattribute__(cls, item):
|
||||
if item == "default" and cls is PluginConfig:
|
||||
return False
|
||||
return super().__getattribute__(item)
|
||||
|
||||
|
||||
class PluginConfig(AppConfig, metaclass=PluginConfigMeta):
|
||||
IGNORE = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
@@ -35,12 +35,13 @@ from pretix.base.models import (
|
||||
SubEvent, User, WaitingListEntry,
|
||||
)
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import SendMailException, TolerantDict, mail
|
||||
from pretix.base.services.mail import SendMailException, mail
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, _cancel_order, _try_auto_refund,
|
||||
)
|
||||
from pretix.base.services.tasks import ProfiledEventTask
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers.format import format_map
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -51,7 +52,7 @@ def _send_wle_mail(wle: WaitingListEntry, subject: LazyI18nString, message: Lazy
|
||||
try:
|
||||
mail(
|
||||
wle.email,
|
||||
str(subject).format_map(TolerantDict(email_context)),
|
||||
format_map(subject, email_context),
|
||||
message,
|
||||
email_context,
|
||||
wle.event,
|
||||
@@ -71,7 +72,7 @@ def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, s
|
||||
|
||||
email_context = get_email_context(event_or_subevent=subevent or order.event, refund_amount=refund_amount,
|
||||
order=order, position_or_address=ia, event=order.event)
|
||||
real_subject = str(subject).format_map(TolerantDict(email_context))
|
||||
real_subject = format_map(subject, email_context)
|
||||
try:
|
||||
order.send_mail(
|
||||
real_subject, message, email_context,
|
||||
@@ -86,7 +87,7 @@ def _send_mail(order: Order, subject: LazyI18nString, message: LazyI18nString, s
|
||||
continue
|
||||
|
||||
if p.addon_to_id is None and p.attendee_email and p.attendee_email != order.email:
|
||||
real_subject = str(subject).format_map(TolerantDict(email_context))
|
||||
real_subject = format_map(subject, email_context)
|
||||
email_context = get_email_context(event_or_subevent=p.subevent or order.event,
|
||||
event=order.event,
|
||||
refund_amount=refund_amount,
|
||||
|
||||
@@ -452,7 +452,7 @@ def build_preview_invoice_pdf(event):
|
||||
|
||||
if event.tax_rules.exists():
|
||||
for i, tr in enumerate(event.tax_rules.all()):
|
||||
for j in range(150):
|
||||
for j in range(5):
|
||||
tax = tr.tax(Decimal('100.00'), base_price_is='gross')
|
||||
InvoiceLine.objects.create(
|
||||
invoice=invoice, description=_("Sample product {}").format(i + 1),
|
||||
@@ -460,7 +460,7 @@ def build_preview_invoice_pdf(event):
|
||||
tax_rate=tax.rate
|
||||
)
|
||||
else:
|
||||
for i in range(150):
|
||||
for i in range(5):
|
||||
InvoiceLine.objects.create(
|
||||
invoice=invoice, description=_("Sample product A"),
|
||||
gross_value=100, tax_value=0, tax_rate=0
|
||||
|
||||
@@ -76,6 +76,7 @@ from pretix.base.services.tasks import TransactionAwareTask
|
||||
from pretix.base.services.tickets import get_tickets_for_order
|
||||
from pretix.base.signals import email_filter, global_email_filter
|
||||
from pretix.celery_app import app
|
||||
from pretix.helpers.format import format_map
|
||||
from pretix.helpers.hierarkey import clean_filename
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.presale.ical import get_private_icals
|
||||
@@ -98,7 +99,8 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
context: Dict[str, Any] = None, event: Event = None, locale: str = None, order: Order = None,
|
||||
position: OrderPosition = None, *, headers: dict = None, sender: str = None, organizer: Organizer = None,
|
||||
customer: Customer = None, invoices: Sequence = None, attach_tickets=False, auto_email=True, user=None,
|
||||
attach_ical=False, attach_cached_files: Sequence = None, attach_other_files: list=None):
|
||||
attach_ical=False, attach_cached_files: Sequence = None, attach_other_files: list=None,
|
||||
plain_text_only=False, no_order_links=False):
|
||||
"""
|
||||
Sends out an email to a user. The mail will be sent synchronously or asynchronously depending on the installation.
|
||||
|
||||
@@ -109,7 +111,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
|
||||
:param template: The filename of a template to be used. It will be rendered with the locale given in the locale
|
||||
argument and the context given in the next argument. Alternatively, you can pass a LazyI18nString and
|
||||
``context`` will be used as the argument to a Python ``.format_map()`` call on the template.
|
||||
``context`` will be used as the argument to a ``pretix.helpers.format.format_map(template, context)`` call on the template.
|
||||
|
||||
:param context: The context for rendering the template (see ``template`` parameter)
|
||||
|
||||
@@ -148,12 +150,21 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
|
||||
:param attach_other_files: A list of file paths on our storage to attach.
|
||||
|
||||
:param plain_text_only: If set to ``True``, rendering a HTML version will be skipped.
|
||||
|
||||
:param no_order_links: If set to ``True``, no link to the order confirmation page will be auto-appended. Currently
|
||||
only allowed to use together with ``plain_text_only`` since HTML renderers add their own
|
||||
links.
|
||||
|
||||
:raises MailOrderException: on obvious, immediate failures. Not raising an exception does not necessarily mean
|
||||
that the email has been sent, just that it has been queued by the email backend.
|
||||
"""
|
||||
if email == INVALID_ADDRESS:
|
||||
return
|
||||
|
||||
if no_order_links and not plain_text_only:
|
||||
raise ValueError('If you set no_order_links, you also need to set plain_text_only.')
|
||||
|
||||
headers = headers or {}
|
||||
if auto_email:
|
||||
headers['X-Auto-Response-Suppress'] = 'OOF, NRN, AutoReply, RN'
|
||||
@@ -242,7 +253,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
if order and order.testmode:
|
||||
subject = "[TESTMODE] " + subject
|
||||
|
||||
if order and position:
|
||||
if order and position and not no_order_links:
|
||||
body_plain += _(
|
||||
"You are receiving this email because someone placed an order for {event} for you."
|
||||
).format(event=event.name)
|
||||
@@ -258,7 +269,7 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
}
|
||||
)
|
||||
)
|
||||
elif order:
|
||||
elif order and not no_order_links:
|
||||
body_plain += _(
|
||||
"You are receiving this email because you placed an order for {event}."
|
||||
).format(event=event.name)
|
||||
@@ -278,7 +289,9 @@ def mail(email: Union[str, Sequence[str]], subject: str, template: Union[str, La
|
||||
|
||||
with override(timezone):
|
||||
try:
|
||||
if 'position' in inspect.signature(renderer.render).parameters:
|
||||
if plain_text_only:
|
||||
body_html = None
|
||||
elif 'position' in inspect.signature(renderer.render).parameters:
|
||||
body_html = renderer.render(content_plain, signature, raw_subject, order, position)
|
||||
else:
|
||||
# Backwards compatibility
|
||||
@@ -608,7 +621,7 @@ def render_mail(template, context):
|
||||
if isinstance(template, LazyI18nString):
|
||||
body = str(template)
|
||||
if context:
|
||||
body = body.format_map(TolerantDict(context))
|
||||
body = format_map(body, context)
|
||||
else:
|
||||
tpl = get_template(template)
|
||||
body = tpl.render(context)
|
||||
|
||||
@@ -74,7 +74,7 @@ from pretix.base.models.orders import (
|
||||
)
|
||||
from pretix.base.models.organizer import TeamAPIToken
|
||||
from pretix.base.models.tax import TAXED_ZERO, TaxedPrice, TaxRule
|
||||
from pretix.base.payment import PaymentException
|
||||
from pretix.base.payment import GiftCardPayment, PaymentException
|
||||
from pretix.base.reldate import RelativeDateWrapper
|
||||
from pretix.base.secrets import assign_ticket_secret
|
||||
from pretix.base.services import tickets
|
||||
@@ -1029,6 +1029,9 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
||||
locked = True
|
||||
lockfn = event.lock
|
||||
|
||||
warnings = []
|
||||
any_payment_failed = False
|
||||
|
||||
with lockfn() as now_dt:
|
||||
positions = list(
|
||||
positions.select_related('item', 'variation', 'subevent', 'seat', 'addon_to').prefetch_related('addons')
|
||||
@@ -1042,25 +1045,52 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
||||
order, payment_objs = _create_order(event, email, positions, now_dt, payment_requests,
|
||||
locale=locale, address=addr, meta_info=meta_info, sales_channel=sales_channel,
|
||||
shown_total=shown_total, customer=customer)
|
||||
try:
|
||||
for p in payment_objs:
|
||||
if p.provider == 'free':
|
||||
p.confirm(send_mail=False, lock=not locked, generate_invoice=False)
|
||||
except Quota.QuotaExceededException:
|
||||
pass
|
||||
|
||||
free_order_flow = (
|
||||
payment_objs and
|
||||
any(p['provider'] == 'free' for p in payment_requests) and
|
||||
order.pending_sum == Decimal('0.00') and
|
||||
not order.require_approval
|
||||
)
|
||||
if free_order_flow:
|
||||
# We give special treatment to GiftCardPayment here because our invoice renderer expects gift cards to already be
|
||||
# processed, and because we historically treat gift card orders like free orders with regards to email texts.
|
||||
# It would be great to give external gift card plugins the same special treatment, but it feels to risky for now, as
|
||||
# (a) there would be no email at all if the plugin fails in a weird way and (b) we'd be able to run into
|
||||
# contradictions when a plugin set both execute_payment_needs_user=False as well as requires_invoice_immediately=True
|
||||
for p in payment_objs:
|
||||
if isinstance(p.payment_provider, GiftCardPayment):
|
||||
try:
|
||||
for p in payment_objs:
|
||||
if p.provider == 'free':
|
||||
p.confirm(send_mail=False, lock=not locked)
|
||||
except Quota.QuotaExceededException:
|
||||
pass
|
||||
p.process_initiated = True
|
||||
p.save(update_fields=['process_initiated'])
|
||||
p.payment_provider.execute_payment(None, p, is_early_special_case=True)
|
||||
except PaymentException as e:
|
||||
warnings.append(str(e))
|
||||
any_payment_failed = True
|
||||
except Exception:
|
||||
logger.exception('Error during payment attempt')
|
||||
|
||||
pending_sum = order.pending_sum
|
||||
free_order_flow = (
|
||||
payment_objs and
|
||||
(
|
||||
any(p['provider'] == 'free' for p in payment_requests) or
|
||||
all(p['provider'] == 'giftcard' for p in payment_requests)
|
||||
) and
|
||||
pending_sum == Decimal('0.00') and
|
||||
not order.require_approval
|
||||
)
|
||||
|
||||
invoice = order.invoices.last() # Might be generated by plugin already
|
||||
if not invoice and invoice_qualified(order):
|
||||
if event.settings.get('invoice_generate') == 'True' or (
|
||||
event.settings.get('invoice_generate') == 'paid' and any(p['pprov'].requires_invoice_immediately for p in payment_requests)):
|
||||
invoice_required = (
|
||||
event.settings.get('invoice_generate') == 'True' or (
|
||||
event.settings.get('invoice_generate') == 'paid' and (
|
||||
any(p['pprov'].requires_invoice_immediately for p in payment_requests) or
|
||||
pending_sum <= Decimal('0.00')
|
||||
)
|
||||
)
|
||||
)
|
||||
if invoice_required:
|
||||
invoice = generate_invoice(
|
||||
order,
|
||||
trigger_pdf=not event.settings.invoice_email_attachment or not order.email
|
||||
@@ -1100,23 +1130,22 @@ def _perform_order(event: Event, payment_requests: List[dict], position_ids: Lis
|
||||
_order_placed_email_attendee(event, order, p, email_attendees_template, subject_attendees_template, log_entry,
|
||||
is_free=free_order_flow)
|
||||
|
||||
warnings = []
|
||||
any_failed = False
|
||||
for p in payment_objs:
|
||||
if not p.payment_provider.execute_payment_needs_user:
|
||||
try:
|
||||
p.process_initiated = True
|
||||
p.save(update_fields=['process_initiated'])
|
||||
resp = p.payment_provider.execute_payment(None, p)
|
||||
if isinstance(resp, str):
|
||||
logger.warning('Payment provider returned URL from execute_payment even though execute_payment_needs_user is not set')
|
||||
except PaymentException as e:
|
||||
warnings.append(str(e))
|
||||
any_failed = True
|
||||
except Exception:
|
||||
logger.exception('Error during payment attempt')
|
||||
if not any_payment_failed:
|
||||
for p in payment_objs:
|
||||
if not p.payment_provider.execute_payment_needs_user and not p.process_initiated:
|
||||
try:
|
||||
p.process_initiated = True
|
||||
p.save(update_fields=['process_initiated'])
|
||||
resp = p.payment_provider.execute_payment(None, p)
|
||||
if isinstance(resp, str):
|
||||
logger.warning('Payment provider returned URL from execute_payment even though execute_payment_needs_user is not set')
|
||||
except PaymentException as e:
|
||||
warnings.append(str(e))
|
||||
any_payment_failed = True
|
||||
except Exception:
|
||||
logger.exception('Error during payment attempt')
|
||||
|
||||
if any_failed:
|
||||
if any_payment_failed:
|
||||
# Cancel all other payments because their amount might be wrong now.
|
||||
for p in payment_objs:
|
||||
if p.state == OrderPayment.PAYMENT_STATE_CREATED:
|
||||
|
||||
@@ -28,7 +28,7 @@ from django.urls import reverse
|
||||
|
||||
|
||||
def _is_samesite_referer(request):
|
||||
referer = request.META.get('HTTP_REFERER')
|
||||
referer = request.headers.get('referer')
|
||||
if referer is None:
|
||||
return False
|
||||
|
||||
|
||||
@@ -77,6 +77,31 @@ def get_all_payment_providers():
|
||||
if PAYMENT_PROVIDERS:
|
||||
return PAYMENT_PROVIDERS
|
||||
|
||||
class FakeSettings:
|
||||
def __init__(self, orig_settings):
|
||||
self.orig_settings = orig_settings
|
||||
|
||||
def set(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self.orig_settings, item)
|
||||
|
||||
class FakeEvent:
|
||||
def __init__(self, orig_event):
|
||||
self.orig_event = orig_event
|
||||
|
||||
@property
|
||||
def settings(self):
|
||||
return FakeSettings(self.orig_event.settings)
|
||||
|
||||
def __getattr__(self, item):
|
||||
return getattr(self.orig_event, item)
|
||||
|
||||
@property
|
||||
def __class__(self): # hackhack
|
||||
return Event
|
||||
|
||||
with rolledback_transaction():
|
||||
event = Event.objects.create(
|
||||
plugins=",".join([app.name for app in apps.get_app_configs()]),
|
||||
@@ -84,6 +109,7 @@ def get_all_payment_providers():
|
||||
date_from=now(),
|
||||
organizer=Organizer.objects.create(name="INTERNAL")
|
||||
)
|
||||
event = FakeEvent(event)
|
||||
provs = register_payment_providers.send(
|
||||
sender=event
|
||||
)
|
||||
@@ -766,6 +792,12 @@ class OrderSearchFilterForm(OrderFilterForm):
|
||||
)
|
||||
)
|
||||
|
||||
def use_query_hack(self):
|
||||
return (
|
||||
self.cleaned_data.get('query') or
|
||||
self.cleaned_data.get('status') in ('overpaid', 'partially_paid', 'underpaid', 'pendingpaid')
|
||||
)
|
||||
|
||||
def filter_qs(self, qs):
|
||||
fdata = self.cleaned_data
|
||||
qs = super().filter_qs(qs)
|
||||
|
||||
@@ -44,6 +44,7 @@ from django.core.files.uploadedfile import UploadedFile
|
||||
from django.db.models import Max
|
||||
from django.forms.formsets import DELETION_FIELD_NAME
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import (
|
||||
@@ -436,12 +437,21 @@ class ItemCreateForm(I18nModelForm):
|
||||
v.pk = None
|
||||
v.item = instance
|
||||
v.save()
|
||||
for mv in variation.meta_values.all():
|
||||
mv.pk = None
|
||||
mv.variation = v
|
||||
mv.save(force_insert=True)
|
||||
else:
|
||||
ItemVariation.objects.create(
|
||||
item=instance, value=__('Standard')
|
||||
)
|
||||
|
||||
if self.cleaned_data.get('copy_from'):
|
||||
for mv in self.cleaned_data['copy_from'].meta_values.all():
|
||||
mv.pk = None
|
||||
mv.item = instance
|
||||
mv.save(force_insert=True)
|
||||
|
||||
for question in self.cleaned_data['copy_from'].questions.all():
|
||||
question.items.add(instance)
|
||||
question.log_action('pretix.event.question.changed', user=self.user, data={
|
||||
@@ -727,6 +737,31 @@ class ItemVariationForm(I18nModelForm):
|
||||
del self.fields['require_membership']
|
||||
del self.fields['require_membership_types']
|
||||
|
||||
self.meta_fields = []
|
||||
meta_defaults = {}
|
||||
if self.instance.pk:
|
||||
for mv in self.instance.meta_values.all():
|
||||
meta_defaults[mv.property_id] = mv.value
|
||||
for p in self.meta_properties:
|
||||
self.initial[f'meta_{p.name}'] = meta_defaults.get(p.pk)
|
||||
self.fields[f'meta_{p.name}'] = forms.CharField(
|
||||
label=p.name,
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
'placeholder': _('Use value from product'),
|
||||
'data-typeahead-url': reverse('control:event.items.meta.typeahead', kwargs={
|
||||
'organizer': self.event.organizer.slug,
|
||||
'event': self.event.slug
|
||||
}) + '?' + urlencode({
|
||||
'property': p.name,
|
||||
}),
|
||||
},
|
||||
),
|
||||
required=False,
|
||||
|
||||
)
|
||||
self.meta_fields.append(f'meta_{p.name}')
|
||||
|
||||
class Meta:
|
||||
model = ItemVariation
|
||||
localized_fields = '__all__'
|
||||
@@ -757,6 +792,26 @@ class ItemVariationForm(I18nModelForm):
|
||||
}),
|
||||
}
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit)
|
||||
self.meta_fields = []
|
||||
current_values = {v.property_id: v for v in instance.meta_values.all()}
|
||||
for p in self.meta_properties:
|
||||
if self.cleaned_data[f'meta_{p.name}']:
|
||||
if p.pk in current_values:
|
||||
current_values[p.pk].value = self.cleaned_data[f'meta_{p.name}']
|
||||
current_values[p.pk].save()
|
||||
else:
|
||||
instance.meta_values.create(property=p, value=self.cleaned_data[f'meta_{p.name}'])
|
||||
elif p.pk in current_values:
|
||||
current_values[p.pk].delete()
|
||||
|
||||
@property
|
||||
def meta_properties(self):
|
||||
if not hasattr(self.event, '_cached_item_meta_properties'):
|
||||
self.event._cached_item_meta_properties = self.event.item_meta_properties.all()
|
||||
return self.event._cached_item_meta_properties
|
||||
|
||||
|
||||
class ItemAddOnsFormSet(I18nFormSet):
|
||||
title = _('Add-ons')
|
||||
@@ -845,6 +900,7 @@ class ItemBundleFormSet(I18nFormSet):
|
||||
def _construct_form(self, i, **kwargs):
|
||||
kwargs['event'] = self.event
|
||||
kwargs['item'] = self.item
|
||||
kwargs['item_qs'] = self.item_qs
|
||||
return super()._construct_form(i, **kwargs)
|
||||
|
||||
@property
|
||||
@@ -856,12 +912,17 @@ class ItemBundleFormSet(I18nFormSet):
|
||||
empty_permitted=True,
|
||||
use_required_attribute=False,
|
||||
locales=self.locales,
|
||||
item_qs=self.item_qs,
|
||||
item=self.item,
|
||||
event=self.event
|
||||
)
|
||||
self.add_fields(form, None)
|
||||
return form
|
||||
|
||||
@cached_property
|
||||
def item_qs(self):
|
||||
return self.event.items.prefetch_related('variations').all()
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
ivs = set()
|
||||
@@ -889,6 +950,7 @@ class ItemBundleForm(I18nModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.item = kwargs.pop('item')
|
||||
self.item_qs = kwargs.pop('item_qs')
|
||||
super().__init__(*args, **kwargs)
|
||||
instance = kwargs.get('instance', None)
|
||||
initial = kwargs.get('initial', {})
|
||||
@@ -906,7 +968,7 @@ class ItemBundleForm(I18nModelForm):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
choices = []
|
||||
for i in self.event.items.prefetch_related('variations').all():
|
||||
for i in self.item_qs:
|
||||
pname = str(i)
|
||||
if not i.is_available():
|
||||
pname += ' ({})'.format(_('inactive'))
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{% load i18n %}
|
||||
{% load bootstrap3 %}
|
||||
{% load formset_tags %}
|
||||
{% load getitem %}
|
||||
<div class="formset" data-formset data-formset-prefix="{{ formset.prefix }}" id="item_variations">
|
||||
{{ formset.management_form }}
|
||||
{% bootstrap_formset_errors formset %}
|
||||
@@ -29,16 +30,20 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6">
|
||||
<span class="fa fa-clock-o fa-fw text-muted variation-timeframe variation-icon-hidden" data-toggle="tooltip" title="{% trans "Only available in a limited timeframe" %}"></span>
|
||||
<span class="fa fa-tags fa-fw text-muted variation-voucher variation-icon-hidden" data-toggle="tooltip"
|
||||
title="{% trans "Only visible with a voucher" %}"></span>
|
||||
<span class="fa fa-id-badge fa-fw text-muted variation-membership variation-icon-hidden" data-toggle="tooltip"
|
||||
title="{% trans "Require a valid membership" %}"></span>
|
||||
<span class="fa fa-clock-o fa-fw text-muted variation-timeframe variation-icon-hidden"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Only available in a limited timeframe" %}"></span>
|
||||
<span class="fa fa-tags fa-fw text-muted variation-voucher variation-icon-hidden"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Only visible with a voucher" %}"></span>
|
||||
<span class="fa fa-id-badge fa-fw text-muted variation-membership variation-icon-hidden"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Require a valid membership" %}"></span>
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6">
|
||||
{% for k, c in sales_channels.items %}
|
||||
<span class="fa fa-fw fa-{{ c.icon }} text-muted variation-channel-{{ k }} variation-icon-hidden"
|
||||
data-toggle="tooltip" title="{% trans c.verbose_name %}"></span>
|
||||
data-toggle="tooltip" title="{% trans c.verbose_name %}"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-md-1 col-xs-6 text-right flip variation-price">
|
||||
@@ -69,6 +74,27 @@
|
||||
{% bootstrap_field form.default_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.original_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field form.description layout="control" %}
|
||||
{% if form.meta_fields %}
|
||||
<div class="form-group metadata-group">
|
||||
<label class="col-md-3 control-label">{% trans "Meta data" %}</label>
|
||||
<div class="col-md-9">
|
||||
{% for fname in form.meta_fields %}
|
||||
{% with form|getitem:fname as field %}
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label for="{{ field.id_for_label }}">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{% bootstrap_field field layout="inline" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% bootstrap_field form.available_from layout="control" %}
|
||||
{% bootstrap_field form.available_until layout="control" %}
|
||||
{% bootstrap_field form.sales_channels layout="control" %}
|
||||
@@ -110,16 +136,20 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6">
|
||||
<span class="fa fa-clock-o fa-fw text-muted variation-timeframe variation-icon-hidden" data-toggle="tooltip" title="{% trans "Only available in a limited timeframe" %}"></span>
|
||||
<span class="fa fa-tags fa-fw text-muted variation-voucher variation-icon-hidden" data-toggle="tooltip"
|
||||
title="{% trans "Only visible with a voucher" %}"></span>
|
||||
<span class="fa fa-id-badge fa-fw text-muted variation-membership variation-icon-hidden" data-toggle="tooltip"
|
||||
title="{% trans "Require a valid membership" %}"></span>
|
||||
<span class="fa fa-clock-o fa-fw text-muted variation-timeframe variation-icon-hidden"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Only available in a limited timeframe" %}"></span>
|
||||
<span class="fa fa-tags fa-fw text-muted variation-voucher variation-icon-hidden"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Only visible with a voucher" %}"></span>
|
||||
<span class="fa fa-id-badge fa-fw text-muted variation-membership variation-icon-hidden"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "Require a valid membership" %}"></span>
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-6">
|
||||
{% for k, c in sales_channels.items %}
|
||||
<span class="fa fa-fw fa-{{ c.icon }} text-muted variation-channel-{{ k }} variation-icon-hidden"
|
||||
data-toggle="tooltip" title="{% trans c.verbose_name %}"></span>
|
||||
data-toggle="tooltip" title="{% trans c.verbose_name %}"></span>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-md-1 col-xs-6 text-right flip variation-price">
|
||||
@@ -141,6 +171,27 @@
|
||||
{% bootstrap_field formset.empty_form.default_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.original_price addon_after=request.event.currency layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.description layout="control" %}
|
||||
{% if formset.empty_form.meta_fields %}
|
||||
<div class="form-group metadata-group">
|
||||
<label class="col-md-3 control-label">{% trans "Meta data" %}</label>
|
||||
<div class="col-md-9">
|
||||
{% for fname in formset.empty_form.meta_fields %}
|
||||
{% with formset.empty_form|getitem:fname as field %}
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<label for="{{ field.id_for_label }}">
|
||||
{{ field.label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{% bootstrap_field field layout="inline" %}
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% bootstrap_field formset.empty_form.available_from layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.available_until layout="control" %}
|
||||
{% bootstrap_field formset.empty_form.sales_channels layout="control" %}
|
||||
|
||||
@@ -22,54 +22,72 @@
|
||||
</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form action="" method="post">
|
||||
<form action="" method="post" class="row">
|
||||
{% csrf_token %}
|
||||
<dl class="dl-horizontal">
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>#{{ customer.identifier }}</dd>
|
||||
{% if customer.provider %}
|
||||
<dt>{% trans "SSO provider" %}</dt>
|
||||
<dd>{{ customer.provider.name }}</dd>
|
||||
{% endif %}
|
||||
{% if customer.external_identifier %}
|
||||
<dt>{% trans "External identifier" %}</dt>
|
||||
<dd>{{ customer.external_identifier }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>
|
||||
{% if not customer.is_active %}
|
||||
{% trans "disabled" %}
|
||||
{% elif not customer.is_verified %}
|
||||
{% trans "not yet activated" %}
|
||||
{% else %}
|
||||
{% trans "active" %}
|
||||
<dl class="dl-horizontal col-lg-6 col-sm-12">
|
||||
<dt>{% trans "Customer ID" %}</dt>
|
||||
<dd>#{{ customer.identifier }}</dd>
|
||||
{% if customer.provider %}
|
||||
<dt>{% trans "SSO provider" %}</dt>
|
||||
<dd>{{ customer.provider.name }}</dd>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "E-mail" %}</dt>
|
||||
<dd>
|
||||
{{ customer.email|default_if_none:"" }}
|
||||
{% if customer.email and not customer.provider %}
|
||||
<button type="submit" name="action" value="pwreset" class="btn btn-xs btn-default">
|
||||
{% trans "Send password reset link" %}
|
||||
</button>
|
||||
{% if customer.external_identifier %}
|
||||
<dt>{% trans "External identifier" %}</dt>
|
||||
<dd>{{ customer.external_identifier }}</dd>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ customer.name }}</dd>
|
||||
{% if customer.phone %}
|
||||
<dt>{% trans "Phone" %}</dt>
|
||||
<dd>{{ customer.phone }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Locale" %}</dt>
|
||||
<dd>{{ display_locale }}</dd>
|
||||
<dt>{% trans "Registration date" %}</dt>
|
||||
<dd>{{ customer.date_joined|date:"SHORT_DATETIME_FORMAT" }}</dd>
|
||||
<dt>{% trans "Last login" %}</dt>
|
||||
<dd>{% if customer.last_login %}{{ customer.last_login|date:"SHORT_DATETIME_FORMAT" }}{% else %}
|
||||
–{% endif %}</dd>
|
||||
{% if customer.notes %}
|
||||
<dt>{% trans "Notes" %}</dt>
|
||||
<dd>{{ customer.notes|linebreaks }}</dd>
|
||||
<dt>{% trans "Status" %}</dt>
|
||||
<dd>
|
||||
{% if not customer.is_active %}
|
||||
{% trans "disabled" %}
|
||||
{% elif not customer.is_verified %}
|
||||
{% trans "not yet activated" %}
|
||||
{% else %}
|
||||
{% trans "active" %}
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "E-mail" %}</dt>
|
||||
<dd>
|
||||
{{ customer.email|default_if_none:"" }}
|
||||
{% if customer.email and not customer.provider %}
|
||||
<button type="submit" name="action" value="pwreset" class="btn btn-xs btn-default">
|
||||
{% trans "Send password reset link" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</dd>
|
||||
<dt>{% trans "Name" %}</dt>
|
||||
<dd>{{ customer.name }}</dd>
|
||||
{% if customer.phone %}
|
||||
<dt>{% trans "Phone" %}</dt>
|
||||
<dd>{{ customer.phone }}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Locale" %}</dt>
|
||||
<dd>{{ display_locale }}</dd>
|
||||
<dt>{% trans "Registration date" %}</dt>
|
||||
<dd>{{ customer.date_joined|date:"SHORT_DATETIME_FORMAT" }}</dd>
|
||||
<dt>{% trans "Last login" %}</dt>
|
||||
<dd>{% if customer.last_login %}{{ customer.last_login|date:"SHORT_DATETIME_FORMAT" }}{% else %}
|
||||
–{% endif %}</dd>
|
||||
{% if customer.notes %}
|
||||
<dt>{% trans "Notes" %}</dt>
|
||||
<dd>{{ customer.notes|linebreaks }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<dl class="col-lg-6 col-sm-12 text-right">
|
||||
<dt class="text-muted"
|
||||
data-toggle="tooltip"
|
||||
title="{% trans "This includes all paid orders by this customer across all your events." %}">
|
||||
{% trans "Lifetime spending" %}
|
||||
</dt>
|
||||
{% if lifetime_spending %}
|
||||
{% for s in lifetime_spending %}
|
||||
{% if s.spending >= 0 %}
|
||||
<dd class="text-success text-h3">{{ s.spending|money:s.currency }}</dd>
|
||||
{% elif s.spending < 0 %}
|
||||
<dd class="text-error text-h3">{{ s.spending|money:s.currency }}</dd>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<dd class="text-muted text-h3">{{ 0|floatformat:2 }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</form>
|
||||
|
||||
@@ -33,7 +33,8 @@
|
||||
# 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.
|
||||
|
||||
from django.conf.urls import include, re_path
|
||||
from django.conf.urls import re_path
|
||||
from django.urls import include
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from pretix.control.views import (
|
||||
|
||||
@@ -51,7 +51,7 @@ from django.utils import formats
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, pgettext, ungettext
|
||||
from django.utils.translation import gettext_lazy as _, ngettext, pgettext
|
||||
|
||||
from pretix.base.decimal import round_decimal
|
||||
from pretix.base.models import (
|
||||
@@ -555,7 +555,7 @@ def widgets_for_event_qs(request, qs, user, nmax, lazy=False):
|
||||
'event': event.slug,
|
||||
'organizer': event.organizer.slug
|
||||
}),
|
||||
orders_text=ungettext('{num} order', '{num} orders', event.order_count or 0).format(
|
||||
orders_text=ngettext('{num} order', '{num} orders', event.order_count or 0).format(
|
||||
num=event.order_count or 0
|
||||
)
|
||||
) if user.has_active_staff_session(request.session.session_key) or event.pk in events_with_orders else ''
|
||||
|
||||
@@ -93,8 +93,8 @@ from ...base.i18n import language
|
||||
from ...base.models.items import (
|
||||
Item, ItemCategory, ItemMetaProperty, Question, Quota,
|
||||
)
|
||||
from ...base.services.mail import TolerantDict
|
||||
from ...base.settings import SETTINGS_AFFECTING_CSS, LazyI18nStringList
|
||||
from ...helpers.format import format_map
|
||||
from ..logdisplay import OVERVIEW_BANLIST
|
||||
from . import CreateView, PaginationMixin, UpdateView
|
||||
|
||||
@@ -734,10 +734,10 @@ class MailSettingsPreview(EventPermissionRequiredMixin, View):
|
||||
if idx in self.supported_locale:
|
||||
with language(self.supported_locale[idx], self.request.event.settings.region):
|
||||
if k.startswith('mail_subject_'):
|
||||
msgs[self.supported_locale[idx]] = bleach.clean(v).format_map(self.placeholders(preview_item))
|
||||
msgs[self.supported_locale[idx]] = format_map(bleach.clean(v), self.placeholders(preview_item))
|
||||
else:
|
||||
msgs[self.supported_locale[idx]] = markdown_compile_email(
|
||||
v.format_map(self.placeholders(preview_item))
|
||||
format_map(v, self.placeholders(preview_item))
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
@@ -761,7 +761,7 @@ class MailSettingsRendererPreview(MailSettingsPreview):
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
v = str(request.event.settings.mail_text_order_placed)
|
||||
v = v.format_map(TolerantDict(self.placeholders('mail_text_order_placed')))
|
||||
v = format_map(v, self.placeholders('mail_text_order_placed'))
|
||||
renderers = request.event.get_html_mail_renderers()
|
||||
if request.GET.get('renderer') in renderers:
|
||||
with rolledback_transaction():
|
||||
|
||||
@@ -1426,7 +1426,7 @@ class ItemUpdateGeneral(ItemDetailMixin, EventPermissionRequiredMixin, MetaDataE
|
||||
can_order=True, can_delete=True, extra=0
|
||||
)(
|
||||
self.request.POST if self.request.method == "POST" else None,
|
||||
queryset=ItemVariation.objects.filter(item=self.get_object()),
|
||||
queryset=ItemVariation.objects.filter(item=self.get_object()).prefetch_related('meta_values', 'require_membership_types'),
|
||||
event=self.request.event, prefix="variations"
|
||||
)),
|
||||
('addons', inlineformset_factory(
|
||||
|
||||
@@ -65,6 +65,10 @@ class OAuthApplicationRegistrationView(ApplicationRegistration):
|
||||
def form_valid(self, form):
|
||||
form.instance.client_type = 'confidential'
|
||||
form.instance.authorization_grant_type = 'authorization-code'
|
||||
secret = generate_client_secret()
|
||||
messages.success(self.request, _('Your application has been created and an application secret has been generated. '
|
||||
'Please copy and save it right now as it will not be shown again: {secret}').format(secret=secret))
|
||||
form.instance.client_secret = secret
|
||||
oauth_application_registered.send(
|
||||
sender=self.request, user=self.request.user, application=form.instance
|
||||
)
|
||||
@@ -74,18 +78,14 @@ class OAuthApplicationRegistrationView(ApplicationRegistration):
|
||||
class ApplicationUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = OAuthApplication
|
||||
fields = ("name", "client_id", "client_secret", "redirect_uris")
|
||||
fields = ("name", "client_id", "redirect_uris")
|
||||
|
||||
def clean_client_id(self):
|
||||
return self.instance.client_id
|
||||
|
||||
def clean_client_secret(self):
|
||||
return self.instance.client_secret
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['client_id'].widget.attrs['readonly'] = True
|
||||
self.fields['client_secret'].widget.attrs['readonly'] = True
|
||||
|
||||
|
||||
class OAuthApplicationUpdateView(ApplicationUpdate):
|
||||
@@ -103,8 +103,10 @@ class OAuthApplicationRollView(ApplicationDetail):
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
messages.success(request, _('A new client secret has been generated and is now effective.'))
|
||||
self.object.client_secret = generate_client_secret()
|
||||
secret = generate_client_secret()
|
||||
messages.success(request, _('A new client secret has been generated. Please copy and save it right now as '
|
||||
'it will not be shown again: {secret}').format(secret=secret))
|
||||
self.object.client_secret = secret
|
||||
self.object.save()
|
||||
return HttpResponseRedirect(self.object.get_absolute_url())
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ from django.urls import reverse
|
||||
from django.utils import formats
|
||||
from django.utils.formats import date_format, get_format
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import is_safe_url
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.timezone import make_aware, now
|
||||
from django.utils.translation import gettext, gettext_lazy as _, ngettext
|
||||
from django.views.generic import (
|
||||
@@ -93,9 +93,7 @@ from pretix.base.services.invoices import (
|
||||
invoice_qualified, regenerate_invoice,
|
||||
)
|
||||
from pretix.base.services.locking import LockTimeoutException
|
||||
from pretix.base.services.mail import (
|
||||
SendMailException, TolerantDict, render_mail,
|
||||
)
|
||||
from pretix.base.services.mail import SendMailException, render_mail
|
||||
from pretix.base.services.orders import (
|
||||
OrderChangeManager, OrderError, approve_order, cancel_order, deny_order,
|
||||
extend_order, mark_order_expired, mark_order_refunded,
|
||||
@@ -127,6 +125,7 @@ from pretix.control.forms.orders import (
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.control.signals import order_search_forms
|
||||
from pretix.control.views import PaginationMixin
|
||||
from pretix.helpers.format import format_map
|
||||
from pretix.helpers.safedownload import check_token
|
||||
from pretix.presale.signals import question_form_fields
|
||||
|
||||
@@ -681,7 +680,7 @@ class OrderRefundCancel(OrderView):
|
||||
messages.success(self.request, _('The refund has been canceled.'))
|
||||
else:
|
||||
messages.error(self.request, _('This refund can not be canceled at the moment.'))
|
||||
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
|
||||
if "next" in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
return redirect(self.request.GET.get("next"))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
@@ -717,7 +716,7 @@ class OrderRefundProcess(OrderView):
|
||||
messages.success(self.request, _('The refund has been processed.'))
|
||||
else:
|
||||
messages.error(self.request, _('This refund can not be processed at the moment.'))
|
||||
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
|
||||
if "next" in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
return redirect(self.request.GET.get("next"))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
@@ -743,7 +742,7 @@ class OrderRefundDone(OrderView):
|
||||
messages.success(self.request, _('The refund has been marked as done.'))
|
||||
else:
|
||||
messages.error(self.request, _('This refund can not be processed at the moment.'))
|
||||
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
|
||||
if "next" in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
return redirect(self.request.GET.get("next"))
|
||||
return redirect(self.get_order_url())
|
||||
|
||||
@@ -2032,7 +2031,7 @@ class OrderSendMail(EventPermissionRequiredMixin, OrderViewMixin, FormView):
|
||||
with language(order.locale, self.request.event.settings.region):
|
||||
email_context = get_email_context(event=order.event, order=order)
|
||||
email_template = LazyI18nString(form.cleaned_data['message'])
|
||||
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
|
||||
email_subject = format_map(str(form.cleaned_data['subject']), email_context)
|
||||
email_content = render_mail(email_template, email_context)
|
||||
if self.request.POST.get('action') == 'preview':
|
||||
self.preview_output = {
|
||||
@@ -2097,7 +2096,7 @@ class OrderPositionSendMail(OrderSendMail):
|
||||
with language(position.order.locale, self.request.event.settings.region):
|
||||
email_context = get_email_context(event=position.order.event, order=position.order, position=position)
|
||||
email_template = LazyI18nString(form.cleaned_data['message'])
|
||||
email_subject = str(form.cleaned_data['subject']).format_map(TolerantDict(email_context))
|
||||
email_subject = format_map(str(form.cleaned_data['subject']), email_context)
|
||||
email_content = render_mail(email_template, email_context)
|
||||
if self.request.POST.get('action') == 'preview':
|
||||
self.preview_output = {
|
||||
|
||||
@@ -45,8 +45,8 @@ from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.core.files import File
|
||||
from django.db import connections, transaction
|
||||
from django.db.models import (
|
||||
Count, Exists, IntegerField, Max, Min, OuterRef, Prefetch, ProtectedError,
|
||||
Q, Subquery, Sum,
|
||||
Count, Exists, F, IntegerField, Max, Min, OuterRef, Prefetch,
|
||||
ProtectedError, Q, Subquery, Sum,
|
||||
)
|
||||
from django.db.models.functions import Coalesce, Greatest
|
||||
from django.forms import DecimalField
|
||||
@@ -109,6 +109,7 @@ from pretix.control.views import PaginationMixin
|
||||
from pretix.control.views.mailsetup import MailSettingsSetupView
|
||||
from pretix.helpers import GroupConcat
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
from pretix.helpers.format import format_map
|
||||
from pretix.helpers.urls import build_absolute_uri as build_global_uri
|
||||
from pretix.multidomain.urlreverse import build_absolute_uri
|
||||
from pretix.presale.forms.customer import TokenGenerator
|
||||
@@ -335,10 +336,10 @@ class MailSettingsPreview(OrganizerPermissionRequiredMixin, View):
|
||||
if idx in self.supported_locale:
|
||||
with language(self.supported_locale[idx], self.request.organizer.settings.region):
|
||||
if k.startswith('mail_subject_'):
|
||||
msgs[self.supported_locale[idx]] = bleach.clean(v).format_map(self.placeholders(preview_item))
|
||||
msgs[self.supported_locale[idx]] = format_map(bleach.clean(v), self.placeholders(preview_item))
|
||||
else:
|
||||
msgs[self.supported_locale[idx]] = markdown_compile_email(
|
||||
v.format_map(self.placeholders(preview_item))
|
||||
format_map(v, self.placeholders(preview_item))
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
@@ -1403,7 +1404,7 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
messages.error(request, _('The transaction could not be reversed.'))
|
||||
else:
|
||||
messages.success(request, _('The transaction has been reversed.'))
|
||||
elif 'value' in request.POST:
|
||||
elif request.POST.get('value'):
|
||||
try:
|
||||
value = DecimalField(localize=True).to_python(request.POST.get('value'))
|
||||
except ValidationError:
|
||||
@@ -2311,6 +2312,14 @@ class CustomerDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
o.icnt = annotated.get(o.pk)['icnt']
|
||||
o.sales_channel_obj = scs[o.sales_channel]
|
||||
|
||||
ctx["lifetime_spending"] = (
|
||||
self.get_queryset()
|
||||
.filter(status=Order.STATUS_PAID)
|
||||
.values(currency=F("event__currency"))
|
||||
.order_by("currency")
|
||||
.annotate(spending=Sum("total"))
|
||||
)
|
||||
|
||||
return ctx
|
||||
|
||||
|
||||
|
||||
@@ -156,8 +156,8 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
p = PdfWriter()
|
||||
try:
|
||||
p.add_blank_page(
|
||||
width=float(request.POST.get('width')) * mm,
|
||||
height=float(request.POST.get('height')) * mm,
|
||||
width=Decimal('%.5f' % (float(request.POST.get('width')) * mm)),
|
||||
height=Decimal('%.5f' % (float(request.POST.get('height')) * mm)),
|
||||
)
|
||||
except ValueError:
|
||||
return JsonResponse({
|
||||
|
||||
@@ -49,7 +49,7 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
self.filter_form[k] for k in self.filter_form.fields if k.startswith('meta_')
|
||||
]
|
||||
|
||||
# Only compute this annotations for this page (query optimization)
|
||||
# Only compute these annotations for this page (query optimization)
|
||||
s = OrderPosition.objects.filter(
|
||||
order=OuterRef('pk')
|
||||
).order_by().values('order').annotate(k=Count('id')).values('k')
|
||||
@@ -91,7 +91,7 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
if self.filter_form.is_valid():
|
||||
qs = self.filter_form.filter_qs(qs)
|
||||
|
||||
if self.filter_form.cleaned_data.get('query'):
|
||||
if self.filter_form.use_query_hack():
|
||||
"""
|
||||
We need to work around a bug in PostgreSQL's (and likely MySQL's) query plan optimizer here.
|
||||
The database lacks statistical data to predict how common our search filter is and therefore
|
||||
@@ -100,8 +100,14 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
look for something rare (such as an email address used once within hundreds of thousands of
|
||||
orders, this ends up to be pathologically slow.
|
||||
|
||||
Generally, PostgreSQL tries to make these decisions on statistical data and generally, they *can*
|
||||
only be made on statistical data, so it's a little bit of a stretch that we try to do it better
|
||||
than PostgreSQL here. However, experience suggests applying this tricks works specifically in the
|
||||
cases where the WHERE part of the statement is very hard to compute, e.g. uses a complicated
|
||||
condition that can't utilize indices well.
|
||||
|
||||
For some search queries on pretix.eu, we see search times of >30s, just due to the ORDER BY and
|
||||
LIMIT clause. Without them. the query runs in roughly 0.6s. This heuristical approach tries to
|
||||
LIMIT clause. Without them. the query runs in roughly 0.6s. This heuristic approach tries to
|
||||
detect these cases and rewrite the query as a nested subquery that strongly suggests sorting
|
||||
before filtering. However, since even that fails in some cases because PostgreSQL thinks it knows
|
||||
better, we literally force it by evaluating the subquery explicitly. We only do this for n<=200,
|
||||
@@ -111,15 +117,8 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
|
||||
Phew.
|
||||
"""
|
||||
|
||||
page = self.kwargs.get(self.page_kwarg) or self.request.GET.get(self.page_kwarg) or 1
|
||||
limit = self.get_paginate_by(None)
|
||||
try:
|
||||
offset = (int(page) - 1) * limit
|
||||
except ValueError:
|
||||
offset = 0
|
||||
resultids = list(qs.order_by().values_list('id', flat=True)[:201])
|
||||
if len(resultids) <= 200 and len(resultids) <= offset + limit:
|
||||
if len(resultids) <= 200:
|
||||
qs = Order.objects.using(settings.DATABASE_REPLICA).filter(
|
||||
id__in=resultids
|
||||
)
|
||||
@@ -134,7 +133,7 @@ class OrderSearch(PaginationMixin, ListView):
|
||||
"""
|
||||
return qs.only(
|
||||
'id', 'invoice_address__name_cached', 'invoice_address__name_parts', 'code', 'event', 'email',
|
||||
'datetime', 'total', 'status', 'require_approval', 'testmode'
|
||||
'datetime', 'total', 'status', 'require_approval', 'testmode', 'custom_followup_at', 'expires'
|
||||
).prefetch_related(
|
||||
'event', 'event__organizer'
|
||||
).select_related('invoice_address')
|
||||
|
||||
@@ -48,7 +48,7 @@ from django.utils.translation import gettext as _, pgettext
|
||||
|
||||
from pretix.base.models import (
|
||||
EventMetaProperty, EventMetaValue, ItemMetaProperty, ItemMetaValue,
|
||||
ItemVariation, Order, Organizer, User, Voucher,
|
||||
ItemVariation, ItemVariationMetaValue, Order, Organizer, User, Voucher,
|
||||
)
|
||||
from pretix.control.forms.event import EventWizardCopyForm
|
||||
from pretix.control.permissions import (
|
||||
@@ -747,6 +747,10 @@ def item_meta_values(request, organizer, event):
|
||||
value__icontains=q,
|
||||
property__name=propname
|
||||
)
|
||||
var_matches = ItemVariationMetaValue.objects.filter(
|
||||
value__icontains=q,
|
||||
property__name=propname
|
||||
)
|
||||
defaults = ItemMetaProperty.objects.filter(
|
||||
name=propname,
|
||||
default__icontains=q
|
||||
@@ -758,6 +762,7 @@ def item_meta_values(request, organizer, event):
|
||||
|
||||
defaults = defaults.filter(event__organizer_id=organizer.pk)
|
||||
matches = matches.filter(item__event__organizer_id=organizer.pk)
|
||||
var_matches = var_matches.filter(variation__item__event__organizer_id=organizer.pk)
|
||||
all_access = (
|
||||
request.user.has_active_staff_session(request.session.session_key)
|
||||
or request.user.teams.filter(all_events=True, organizer=organizer, can_change_items=True).exists()
|
||||
@@ -773,10 +778,19 @@ def item_meta_values(request, organizer, event):
|
||||
'limit_events__id', flat=True
|
||||
)
|
||||
)
|
||||
var_matches = matches.filter(
|
||||
variation__item__event__id__in=request.user.teams.filter(can_change_items=True).values_list(
|
||||
'limit_events__id', flat=True
|
||||
)
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
'results': [
|
||||
{'name': v, 'id': v}
|
||||
for v in sorted(set(defaults.values_list('default', flat=True)[:10]) | set(matches.values_list('value', flat=True)[:10]))
|
||||
for v in sorted(
|
||||
set(defaults.values_list('default', flat=True)[:10]) |
|
||||
set(matches.values_list('value', flat=True)[:10]) |
|
||||
set(var_matches.values_list('value', flat=True)[:10])
|
||||
)
|
||||
]
|
||||
})
|
||||
|
||||
@@ -43,7 +43,7 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import is_safe_url
|
||||
from django.utils.http import url_has_allowed_host_and_scheme
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext_lazy as _, pgettext
|
||||
from django.views import View
|
||||
@@ -144,7 +144,7 @@ class WaitingListActionView(EventPermissionRequiredMixin, WaitingListQuerySetMix
|
||||
permission = 'can_change_orders'
|
||||
|
||||
def _redirect_back(self):
|
||||
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
|
||||
if "next" in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
return redirect(self.request.GET.get("next"))
|
||||
return redirect(reverse('control:event.orders.waitinglist', kwargs={
|
||||
'event': self.request.event.slug,
|
||||
@@ -360,7 +360,7 @@ class EntryDelete(EventPermissionRequiredMixin, DeleteView):
|
||||
self.object.log_action('pretix.event.orders.waitinglist.deleted', user=self.request.user)
|
||||
self.object.delete()
|
||||
messages.success(self.request, _('The selected entry has been deleted.'))
|
||||
if "next" in self.request.GET and is_safe_url(self.request.GET.get("next"), allowed_hosts=None):
|
||||
if "next" in self.request.GET and url_has_allowed_host_and_scheme(self.request.GET.get("next"), allowed_hosts=None):
|
||||
return redirect(self.request.GET.get("next"))
|
||||
return HttpResponseRedirect(success_url)
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
import logging
|
||||
from string import Formatter
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SafeFormatter(Formatter):
|
||||
"""
|
||||
Customized version of ``str.format`` that (a) behaves just like ``str.format_map`` and
|
||||
(b) does not allow any unwanted shenanigans like attribute access or format specifiers.
|
||||
"""
|
||||
def __init__(self, context):
|
||||
self.context = context
|
||||
|
||||
def get_field(self, field_name, args, kwargs):
|
||||
if '.' in field_name or '[' in field_name:
|
||||
logger.warning(f'Ignored invalid field name "{field_name}"')
|
||||
return ('{' + str(field_name) + '}', field_name)
|
||||
return super().get_field(field_name, args, kwargs)
|
||||
|
||||
def get_value(self, key, args, kwargs):
|
||||
if key not in self.context:
|
||||
return '{' + str(key) + '}'
|
||||
return self.context[key]
|
||||
|
||||
def format_field(self, value, format_spec):
|
||||
# Ignore format _spec
|
||||
return super().format_field(value, '')
|
||||
|
||||
|
||||
def format_map(template, context):
|
||||
if not isinstance(template, str):
|
||||
template = str(template)
|
||||
return SafeFormatter(context).format(template)
|
||||
@@ -38,7 +38,7 @@ class ChunkBasedFileResponse(StreamingHttpResponse):
|
||||
def get_client_ip(request):
|
||||
ip = request.META.get('REMOTE_ADDR')
|
||||
if settings.TRUST_X_FORWARDED_FOR:
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
x_forwarded_for = request.headers.get('x-forwarded-for')
|
||||
if x_forwarded_for:
|
||||
ip = x_forwarded_for.split(',')[0]
|
||||
return ip
|
||||
|
||||
@@ -24,8 +24,7 @@ from inspect import isgenerator
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.cell.cell import (
|
||||
ILLEGAL_CHARACTERS_RE, KNOWN_TYPES, TIME_TYPES, TYPE_FORMULA, TYPE_STRING,
|
||||
Cell,
|
||||
KNOWN_TYPES, TIME_TYPES, TYPE_FORMULA, TYPE_STRING, Cell,
|
||||
)
|
||||
from openpyxl.compat import NUMERIC_TYPES
|
||||
from openpyxl.utils import column_index_from_string
|
||||
@@ -49,6 +48,12 @@ There are mainly two problems this solves:
|
||||
- It removes characters considered invalid by Excel to avoid exporter crashes.
|
||||
"""
|
||||
|
||||
ILLEGAL_CHARACTERS_RE = re.compile(
|
||||
# From the XML specification
|
||||
# Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
|
||||
r'[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]'
|
||||
)
|
||||
|
||||
|
||||
def remove_invalid_excel_chars(val):
|
||||
if isinstance(val, Cell):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2021-09-15 11:22+0000\n"
|
||||
"Last-Translator: Mohamed Tawfiq <mtawfiq@wafyapp.com>\n"
|
||||
"Language-Team: Arabic <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -573,15 +573,15 @@ msgstr "البحث في الاستفسارات"
|
||||
msgid "Selected only"
|
||||
msgstr "المختارة فقط"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "قم باستخدم اسم مختلف داخليا"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "اضغط لاغلاق الصفحة"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "لم تقم بحفظ التعديلات!"
|
||||
|
||||
@@ -655,20 +655,20 @@ msgstr "ستسترد %(currency)%(amount)"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "الرجاء إدخال المبلغ الذي يمكن للمنظم الاحتفاظ به."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "الرجاء إدخال عدد لأحد أنواع التذاكر."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "مطلوب"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "المنطقة الزمنية:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "التوقيت المحلي:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2020-12-19 07:00+0000\n"
|
||||
"Last-Translator: albert <albert.serra.monner@gmail.com>\n"
|
||||
"Language-Team: Catalan <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -556,15 +556,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -628,22 +628,22 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Cistella expirada"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2021-12-06 23:00+0000\n"
|
||||
"Last-Translator: Ondřej Sokol <osokol@treesoft.cz>\n"
|
||||
"Language-Team: Czech <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -573,15 +573,15 @@ msgstr "Hledaný výraz"
|
||||
msgid "Selected only"
|
||||
msgstr "Pouze vybrané"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Interně používat jiný název"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Kliknutím zavřete"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Máte neuložené změny!"
|
||||
|
||||
@@ -648,20 +648,20 @@ msgstr "Dostanete %(currency)s %(amount)s zpět"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Zadejte částku, kterou si organizátor může ponechat."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Zadejte prosím množství pro jeden z typů vstupenek."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "povinný"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Časové pásmo:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Místní čas:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"PO-Revision-Date: 2022-04-01 13:36+0000\n"
|
||||
"Last-Translator: Anna-itk <abc@aarhus.dk>\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-12-01 17:00+0000\n"
|
||||
"Last-Translator: Mie Frydensbjerg <mif@aarhus.dk>\n"
|
||||
"Language-Team: Danish <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
"da/>\n"
|
||||
"Language: da\n"
|
||||
@@ -16,7 +16,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 4.11.2\n"
|
||||
"X-Generator: Weblate 4.14.2\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
@@ -30,51 +30,51 @@ msgstr "Kommentar:"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:34
|
||||
msgid "PayPal"
|
||||
msgstr ""
|
||||
msgstr "PayPal"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35
|
||||
msgid "Venmo"
|
||||
msgstr ""
|
||||
msgstr "Venmo"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
msgstr "Apple Pay"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:37
|
||||
msgid "Itaú"
|
||||
msgstr ""
|
||||
msgstr "Itaú"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:38
|
||||
msgid "PayPal Credit"
|
||||
msgstr ""
|
||||
msgstr "PayPal Credit"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39
|
||||
msgid "Credit Card"
|
||||
msgstr ""
|
||||
msgstr "Kreditkort"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40
|
||||
msgid "PayPal Pay Later"
|
||||
msgstr ""
|
||||
msgstr "PayPal Pay Later"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:41
|
||||
msgid "iDEAL"
|
||||
msgstr ""
|
||||
msgstr "iDEAL"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
|
||||
msgid "SEPA Direct Debit"
|
||||
msgstr ""
|
||||
msgstr "SEPA Direct Debit"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
|
||||
msgid "Bancontact"
|
||||
msgstr ""
|
||||
msgstr "Bancontact"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:44
|
||||
msgid "giropay"
|
||||
msgstr ""
|
||||
msgstr "giropay"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:45
|
||||
msgid "SOFORT"
|
||||
msgstr ""
|
||||
msgstr "SOFORT"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:46
|
||||
#, fuzzy
|
||||
@@ -84,11 +84,11 @@ msgstr "Ja"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:47
|
||||
msgid "MyBank"
|
||||
msgstr ""
|
||||
msgstr "MyBank"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:48
|
||||
msgid "Przelewy24"
|
||||
msgstr ""
|
||||
msgstr "Przelewy24"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:49
|
||||
msgid "Verkkopankki"
|
||||
@@ -146,7 +146,7 @@ msgstr "Bekræfter din betaling …"
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:246
|
||||
msgid "Payment method unavailable"
|
||||
msgstr ""
|
||||
msgstr "Betalingsmetode er ikke tilgængelig"
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
@@ -176,67 +176,64 @@ msgstr "Kontakter din bank …"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:30
|
||||
msgid "Select a check-in list"
|
||||
msgstr ""
|
||||
msgstr "Vælg en check-in liste"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:31
|
||||
msgid "No active check-in lists found."
|
||||
msgstr ""
|
||||
msgstr "Der blev ikke fundet nogen aktive check-in lister."
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:32
|
||||
msgid "Switch check-in list"
|
||||
msgstr ""
|
||||
msgstr "Skift check-in liste"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:33
|
||||
msgid "Search results"
|
||||
msgstr ""
|
||||
msgstr "Søgeresultater"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:34
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "Close ticket shop"
|
||||
msgid "No tickets found"
|
||||
msgstr "Luk billetbutik"
|
||||
msgstr "Ingen billetter fundet"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:35
|
||||
msgid "Result"
|
||||
msgstr ""
|
||||
msgstr "Resultat"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:36
|
||||
msgid "This ticket requires special attention"
|
||||
msgstr ""
|
||||
msgstr "Denne billet kræver særlig opmærksomhed"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:37
|
||||
msgid "Switch direction"
|
||||
msgstr ""
|
||||
msgstr "Skift retning"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:38
|
||||
msgid "Entry"
|
||||
msgstr ""
|
||||
msgstr "Indgang"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:39
|
||||
msgid "Exit"
|
||||
msgstr ""
|
||||
msgstr "Udgang"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:40
|
||||
msgid "Scan a ticket or search and press return…"
|
||||
msgstr ""
|
||||
msgstr "Scan en billet eller søg og tryk på retur…"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:41
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
msgstr "Hent flere"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:42
|
||||
msgid "Valid"
|
||||
msgstr ""
|
||||
msgstr "Gyldig"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:43
|
||||
msgid "Unpaid"
|
||||
msgstr ""
|
||||
msgstr "Ubetalt"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:44
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:45
|
||||
msgid "Canceled"
|
||||
msgstr ""
|
||||
msgstr "Annulleret"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
|
||||
#, fuzzy
|
||||
@@ -264,7 +261,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
|
||||
msgid "Valid ticket"
|
||||
msgstr ""
|
||||
msgstr "Gyldig billet"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:53
|
||||
msgid "Exit recorded"
|
||||
@@ -279,18 +276,16 @@ msgid "Information required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
|
||||
#, fuzzy
|
||||
#| msgid "Unknown error."
|
||||
msgid "Unknown ticket"
|
||||
msgstr "Ukendt fejl."
|
||||
msgstr "Ukendt billet"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
|
||||
msgid "Ticket type not allowed here"
|
||||
msgstr ""
|
||||
msgstr "Billettype er ikke tilladt her"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
|
||||
msgid "Entry not allowed"
|
||||
msgstr ""
|
||||
msgstr "Adgang ikke tilladt"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
|
||||
msgid "Ticket code revoked/changed"
|
||||
@@ -298,7 +293,7 @@ msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
||||
msgid "Order canceled"
|
||||
msgstr ""
|
||||
msgstr "Bestilling annulleret"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
|
||||
msgid "Ticket code is ambiguous on list"
|
||||
@@ -312,11 +307,11 @@ msgstr "Check-in QR"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
|
||||
msgid "Valid Tickets"
|
||||
msgstr ""
|
||||
msgstr "Gyldige billetter"
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
|
||||
msgid "Currently inside"
|
||||
msgstr ""
|
||||
msgstr "Inde i øjeblikket"
|
||||
|
||||
#: pretix/static/lightbox/js/lightbox.js:96
|
||||
#, fuzzy
|
||||
@@ -376,17 +371,15 @@ msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
#, fuzzy
|
||||
#| msgid "The request took to long. Please try again."
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr "Forespørgselen tog for lang tid. Prøv venligst igen."
|
||||
msgstr "Forespørgslen tog for lang tid. Prøv igen."
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
msgstr ""
|
||||
"Vi kan ikke komme i kontakt med serveren. Prøv venligst igen. Fejlkode: "
|
||||
"Vi kan i øjeblikket ikke komme i kontakt med serveren. Prøv igen. Fejlkode: "
|
||||
"{code}"
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:208
|
||||
@@ -433,23 +426,20 @@ msgid "Product"
|
||||
msgstr "Produkt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:63
|
||||
#, fuzzy
|
||||
#| msgctxt "widget"
|
||||
#| msgid "See variations"
|
||||
msgid "Product variation"
|
||||
msgstr "Vis varianter"
|
||||
msgstr "Produktvariation"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:67
|
||||
msgid "Current date and time"
|
||||
msgstr ""
|
||||
msgstr "Aktuel dato og klokkeslæt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:71
|
||||
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||
msgstr ""
|
||||
msgstr "Aktuel ugedag (1 = mandag, 7 = søndag)"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:75
|
||||
msgid "Number of previous entries"
|
||||
msgstr ""
|
||||
msgstr "Antal tidligere poster"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:79
|
||||
msgid "Number of previous entries since midnight"
|
||||
@@ -601,15 +591,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Klik for at lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du har ændringer, der ikke er gemt!"
|
||||
|
||||
@@ -683,22 +673,22 @@ msgstr "fra %(currency)s %(price)s"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Kurv udløbet"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Tidszone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Din lokaltid:"
|
||||
|
||||
@@ -901,7 +891,7 @@ msgstr "Åbn sædevalg"
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
msgstr "Hent flere"
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgid "Mo"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-07-26 08:58+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -577,15 +577,15 @@ msgstr "Suchbegriff"
|
||||
msgid "Selected only"
|
||||
msgstr "Nur ausgewählte"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sie haben ungespeicherte Änderungen!"
|
||||
|
||||
@@ -650,20 +650,20 @@ msgstr "Sie erhalten %(currency)s %(amount)s zurück"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Bitte geben Sie den Betrag ein, den der Veranstalter einbehalten darf."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Bitte tragen Sie eine Menge für eines der Produkte ein."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "verpflichtend"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Zeitzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Deine lokale Zeit:"
|
||||
|
||||
|
||||
@@ -337,6 +337,9 @@ WeChat
|
||||
WhatsApp
|
||||
Widget
|
||||
xlsx
|
||||
XXX
|
||||
XXXX
|
||||
XXXXX
|
||||
Yubikey
|
||||
Zahlungs
|
||||
Zahlungsbestätigungs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-07-26 08:58+0000\n"
|
||||
"Last-Translator: Raphael Michel <michel@rami.io>\n"
|
||||
"Language-Team: German (informal) <https://translate.pretix.eu/projects/"
|
||||
@@ -576,15 +576,15 @@ msgstr "Suchbegriff"
|
||||
msgid "Selected only"
|
||||
msgstr "Nur ausgewählte"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Intern einen anderen Namen verwenden"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Klicken zum Schließen"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du hast ungespeicherte Änderungen!"
|
||||
|
||||
@@ -649,20 +649,20 @@ msgstr "Du erhältst %(currency)s %(amount)s zurück"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Bitte gib den Betrag ein, den der Veranstalter einbehalten darf."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Bitte trage eine Menge für eines der Produkte ein."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "verpflichtend"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Zeitzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Deine lokale Zeit:"
|
||||
|
||||
|
||||
@@ -337,6 +337,9 @@ WeChat
|
||||
WhatsApp
|
||||
Widget
|
||||
xlsx
|
||||
XXX
|
||||
XXXX
|
||||
XXXXX
|
||||
Yubikey
|
||||
Zahlungs
|
||||
Zahlungsbestätigungs
|
||||
|
||||
+934
-846
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -555,15 +555,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -623,20 +623,20 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2019-10-03 19:00+0000\n"
|
||||
"Last-Translator: Chris Spy <chrispiropoulou@hotmail.com>\n"
|
||||
"Language-Team: Greek <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -615,15 +615,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Χρησιμοποιήστε διαφορετικό όνομα εσωτερικά"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Κάντε κλικ για να κλείσετε"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -695,22 +695,22 @@ msgstr "απο %(currency)s %(price)s"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Εισαγάγετε μια ποσότητα για έναν από τους τύπους εισιτηρίων."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Το καλάθι έληξε"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2021-11-25 21:00+0000\n"
|
||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||
"Language-Team: Spanish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -577,15 +577,15 @@ msgstr "Consultar búsqueda"
|
||||
msgid "Selected only"
|
||||
msgstr "Solamente seleccionados"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Usar un nombre diferente internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Click para cerrar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "¡Tienes cambios sin guardar!"
|
||||
|
||||
@@ -649,20 +649,20 @@ msgstr "Obtienes %(currency)s %(price)s de vuelta"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Por favor, ingrese el monto que el organizador puede quedarse."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Por favor, introduzca un valor para cada tipo de entrada."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "campo requerido"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Zona horaria:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Su hora local:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2021-11-10 05:00+0000\n"
|
||||
"Last-Translator: Jaakko Rinta-Filppula <jaakko@r-f.fi>\n"
|
||||
"Language-Team: Finnish <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -576,15 +576,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Käytä toista nimeä sisäisesti"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Sulje klikkaamalla"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Sinulla on tallentamattomia muutoksia!"
|
||||
|
||||
@@ -648,22 +648,22 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Ostoskori on vanhentunut"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Aikavyöhyke:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: French\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-04-07 10:40+0000\n"
|
||||
"Last-Translator: Eva-Maria Obermann <obermann@rami.io>\n"
|
||||
"Language-Team: French <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -607,15 +607,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utiliser un nom différent en interne"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Cliquez pour fermer"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Vous avez des modifications non sauvegardées !"
|
||||
|
||||
@@ -683,22 +683,22 @@ msgstr "de %(currency)s %(price)s"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "SVP entrez une quantité pour un de vos types de billets."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "Panier expiré"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-02-22 22:00+0000\n"
|
||||
"Last-Translator: Ismael Menéndez Fernández <ismael.menendez@balidea.com>\n"
|
||||
"Language-Team: Galician <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -575,15 +575,15 @@ msgstr "Consultar unha procura"
|
||||
msgid "Selected only"
|
||||
msgstr "Soamente seleccionados"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Usar un nome diferente internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Click para cerrar"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Tes cambios sen gardar!"
|
||||
|
||||
@@ -646,20 +646,20 @@ msgstr "Obtés %(currency)s %(price)s de volta"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Por favor, ingrese a cantidade que pode conservar o organizador."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Por favor, introduza un valor para cada tipo de entrada."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "campo requirido"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Zona horaria:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "A súa hora local:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2021-09-24 13:54+0000\n"
|
||||
"Last-Translator: ofirtro <ofir.tro@gmail.com>\n"
|
||||
"Language-Team: Hebrew <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -559,15 +559,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
@@ -631,20 +631,20 @@ msgstr ""
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,910 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Automatically generated\n"
|
||||
"Language-Team: none\n"
|
||||
"Language: hr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:56
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:62
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:68
|
||||
msgid "Marked as paid"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/banktransfer/static/pretixplugins/banktransfer/ui.js:76
|
||||
msgid "Comment:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:34
|
||||
msgid "PayPal"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:35
|
||||
msgid "Venmo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:36
|
||||
msgid "Apple Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:37
|
||||
msgid "Itaú"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:38
|
||||
msgid "PayPal Credit"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:39
|
||||
msgid "Credit Card"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:40
|
||||
msgid "PayPal Pay Later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:41
|
||||
msgid "iDEAL"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:42
|
||||
msgid "SEPA Direct Debit"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:43
|
||||
msgid "Bancontact"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:44
|
||||
msgid "giropay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:45
|
||||
msgid "SOFORT"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:46
|
||||
msgid "eps"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:47
|
||||
msgid "MyBank"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:48
|
||||
msgid "Przelewy24"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:49
|
||||
msgid "Verkkopankki"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:50
|
||||
msgid "PayU"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:51
|
||||
msgid "BLIK"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:52
|
||||
msgid "Trustly"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:53
|
||||
msgid "Zimpler"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:54
|
||||
msgid "Maxima"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:55
|
||||
msgid "OXXO"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:56
|
||||
msgid "Boleto"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:57
|
||||
msgid "WeChat Pay"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:58
|
||||
msgid "Mercado Pago"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:163
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:48
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:221
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:152
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:183
|
||||
msgid "Confirming your payment …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/paypal2/static/pretixplugins/paypal2/pretix-paypal.js:246
|
||||
msgid "Payment method unavailable"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Placed orders"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:15
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:39
|
||||
msgid "Paid orders"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/statistics/static/pretixplugins/statistics/statistics.js:27
|
||||
msgid "Total revenue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:12
|
||||
msgid "Contacting Stripe …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:60
|
||||
msgid "Total"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/stripe/static/pretixplugins/stripe/pretix-stripe.js:159
|
||||
msgid "Contacting your bank …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:30
|
||||
msgid "Select a check-in list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:31
|
||||
msgid "No active check-in lists found."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:32
|
||||
msgid "Switch check-in list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:33
|
||||
msgid "Search results"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:34
|
||||
msgid "No tickets found"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:35
|
||||
msgid "Result"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:36
|
||||
msgid "This ticket requires special attention"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:37
|
||||
msgid "Switch direction"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:38
|
||||
msgid "Entry"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:39
|
||||
msgid "Exit"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:40
|
||||
msgid "Scan a ticket or search and press return…"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:41
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:42
|
||||
msgid "Valid"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:43
|
||||
msgid "Unpaid"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:44
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:45
|
||||
msgid "Canceled"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:46
|
||||
msgid "Redeemed"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:47
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:49
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:58
|
||||
msgid "Ticket not paid"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:50
|
||||
msgid "This ticket is not yet paid. Do you want to continue anyways?"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:51
|
||||
msgid "Additional information required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:52
|
||||
msgid "Valid ticket"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:53
|
||||
msgid "Exit recorded"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:54
|
||||
msgid "Ticket already used"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:55
|
||||
msgid "Information required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:56
|
||||
msgid "Unknown ticket"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:57
|
||||
msgid "Ticket type not allowed here"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:59
|
||||
msgid "Entry not allowed"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:60
|
||||
msgid "Ticket code revoked/changed"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:61
|
||||
msgid "Order canceled"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:62
|
||||
msgid "Ticket code is ambiguous on list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:63
|
||||
msgid "Checked-in Tickets"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:64
|
||||
msgid "Valid Tickets"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.js:65
|
||||
msgid "Currently inside"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/lightbox/js/lightbox.js:96
|
||||
msgid "close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:43
|
||||
#: pretix/static/pretixbase/js/asynctask.js:120
|
||||
msgid ""
|
||||
"Your request is currently being processed. Depending on the size of your "
|
||||
"event, this might take up to a few minutes."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:48
|
||||
#: pretix/static/pretixbase/js/asynctask.js:125
|
||||
msgid "Your request has been queued on the server and will soon be processed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:54
|
||||
#: pretix/static/pretixbase/js/asynctask.js:131
|
||||
msgid ""
|
||||
"Your request arrived on the server but we still wait for it to be processed. "
|
||||
"If this takes longer than two minutes, please contact us or go back in your "
|
||||
"browser and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:90
|
||||
#: pretix/static/pretixbase/js/asynctask.js:178
|
||||
#: pretix/static/pretixbase/js/asynctask.js:183
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:24
|
||||
msgid "An error of type {code} occurred."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:93
|
||||
msgid ""
|
||||
"We currently cannot reach the server, but we keep trying. Last error code: "
|
||||
"{code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:145
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:21
|
||||
msgid "The request took too long. Please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:186
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:26
|
||||
msgid ""
|
||||
"We currently cannot reach the server. Please try again. Error code: {code}"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:208
|
||||
msgid "We are processing your request …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:216
|
||||
msgid ""
|
||||
"We are currently sending your request to the server. If this takes longer "
|
||||
"than one minute, please check your internet connection and then reload this "
|
||||
"page and try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixbase/js/asynctask.js:276
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:71
|
||||
msgid "Close message"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/clipboard.js:23
|
||||
msgid "Copied!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/clipboard.js:29
|
||||
msgid "Press Ctrl-C to copy!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:10
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:16
|
||||
msgid "is one of"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:22
|
||||
msgid "is before"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:26
|
||||
msgid "is after"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:59
|
||||
msgid "Product"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:63
|
||||
msgid "Product variation"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:67
|
||||
msgid "Current date and time"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:71
|
||||
msgid "Current day of the week (1 = Monday, 7 = Sunday)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:75
|
||||
msgid "Number of previous entries"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:79
|
||||
msgid "Number of previous entries since midnight"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:83
|
||||
msgid "Number of days with a previous entry"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:87
|
||||
msgid "Minutes since last entry (-1 on first entry)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:91
|
||||
msgid "Minutes since first entry (-1 on first entry)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:112
|
||||
msgid "All of the conditions below (AND)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:113
|
||||
msgid "At least one of the conditions below (OR)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:114
|
||||
msgid "Event start"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:115
|
||||
msgid "Event end"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:116
|
||||
msgid "Event admission"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:117
|
||||
msgid "custom date and time"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:118
|
||||
msgid "custom time"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:119
|
||||
msgid "Tolerance (minutes)"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:120
|
||||
msgid "Add condition"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/checkinrules.js:121
|
||||
msgid "minutes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:71
|
||||
msgid "Check-in QR"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:384
|
||||
msgid "The PDF background file could not be loaded for the following reason:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:653
|
||||
msgid "Group of objects"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:659
|
||||
msgid "Text object"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:661
|
||||
msgid "Barcode area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:663
|
||||
msgid "Image area"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:665
|
||||
msgid "Powered by pretix"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:667
|
||||
msgid "Object"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:671
|
||||
msgid "Ticket design"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:961
|
||||
msgid "Saving failed."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1011
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1050
|
||||
msgid "Error while uploading your PDF file, please try again."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/editor.js:1035
|
||||
msgid "Do you really want to leave the editor without saving your changes?"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:19
|
||||
msgid "An error has occurred."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/mail.js:54
|
||||
msgid "Generating messages …"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:107
|
||||
msgid "Unknown error."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:308
|
||||
msgid "Your color has great contrast and is very easy to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:312
|
||||
msgid "Your color has decent contrast and is probably good-enough to read!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:316
|
||||
msgid ""
|
||||
"Your color has bad contrast for text on white background, please choose a "
|
||||
"darker shade."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:454
|
||||
msgid "All"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:455
|
||||
msgid "None"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:456
|
||||
msgid "Search query"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:459
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/orderchange.js:25
|
||||
msgid "Calculating default price…"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:42
|
||||
msgid "Others"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:82
|
||||
msgid "Count"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:136
|
||||
#: pretix/static/pretixpresale/js/ui/questions.js:269
|
||||
msgid "Yes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/question.js:137
|
||||
#: pretix/static/pretixpresale/js/ui/questions.js:269
|
||||
msgid "No"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/subevent.js:111
|
||||
msgid "(one more date)"
|
||||
msgid_plural "({num} more dates)"
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:43
|
||||
msgid ""
|
||||
"The items in your cart are no longer reserved for you. You can still "
|
||||
"complete your order as long as they’re available."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:45
|
||||
msgid "Cart expired"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/cart.js:50
|
||||
msgid "The items in your cart are reserved for you for one minute."
|
||||
msgid_plural "The items in your cart are reserved for you for {num} minutes."
|
||||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:152
|
||||
msgid "The organizer keeps %(currency)s %(amount)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:160
|
||||
msgid "You get %(currency)s %(amount)s back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:176
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:17
|
||||
msgctxt "widget"
|
||||
msgid "Sold out"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:18
|
||||
msgctxt "widget"
|
||||
msgid "Buy"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:19
|
||||
msgctxt "widget"
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:20
|
||||
msgctxt "widget"
|
||||
msgid "Reserved"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:21
|
||||
msgctxt "widget"
|
||||
msgid "FREE"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:22
|
||||
msgctxt "widget"
|
||||
msgid "from %(currency)s %(price)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:23
|
||||
msgctxt "widget"
|
||||
msgid "incl. %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:24
|
||||
msgctxt "widget"
|
||||
msgid "plus %(rate)s% %(taxname)s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:25
|
||||
msgctxt "widget"
|
||||
msgid "incl. taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:26
|
||||
msgctxt "widget"
|
||||
msgid "plus taxes"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:27
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "currently available: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:28
|
||||
msgctxt "widget"
|
||||
msgid "Only available with a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:29
|
||||
#, javascript-format
|
||||
msgctxt "widget"
|
||||
msgid "minimum amount to order: %s"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:30
|
||||
msgctxt "widget"
|
||||
msgid "Close ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:31
|
||||
msgctxt "widget"
|
||||
msgid "The ticket shop could not be loaded."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:32
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"There are currently a lot of users in this ticket shop. Please open the shop "
|
||||
"in a new tab to continue."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:34
|
||||
msgctxt "widget"
|
||||
msgid "Open ticket shop"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:35
|
||||
msgctxt "widget"
|
||||
msgid "The cart could not be created. Please try again later"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:36
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"We could not create your cart, since there are currently too many users in "
|
||||
"this ticket shop. Please click \"Continue\" to retry in a new tab."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:38
|
||||
msgctxt "widget"
|
||||
msgid "Waiting list"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:39
|
||||
msgctxt "widget"
|
||||
msgid ""
|
||||
"You currently have an active cart for this event. If you select more "
|
||||
"products, they will be added to your existing cart."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:41
|
||||
msgctxt "widget"
|
||||
msgid "Resume checkout"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:42
|
||||
msgctxt "widget"
|
||||
msgid "Redeem a voucher"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:43
|
||||
msgctxt "widget"
|
||||
msgid "Redeem"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:44
|
||||
msgctxt "widget"
|
||||
msgid "Voucher code"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:45
|
||||
msgctxt "widget"
|
||||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:46
|
||||
msgctxt "widget"
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:47
|
||||
msgctxt "widget"
|
||||
msgid "See variations"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:48
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different event"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:49
|
||||
msgctxt "widget"
|
||||
msgid "Choose a different date"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:50
|
||||
msgctxt "widget"
|
||||
msgid "Back"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:51
|
||||
msgctxt "widget"
|
||||
msgid "Next month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:52
|
||||
msgctxt "widget"
|
||||
msgid "Previous month"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:53
|
||||
msgctxt "widget"
|
||||
msgid "Next week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:54
|
||||
msgctxt "widget"
|
||||
msgid "Previous week"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:55
|
||||
msgctxt "widget"
|
||||
msgid "Open seat selection"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:56
|
||||
msgctxt "widget"
|
||||
msgid "Load more"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:58
|
||||
msgid "Mo"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:59
|
||||
msgid "Tu"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:60
|
||||
msgid "We"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:61
|
||||
msgid "Th"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:62
|
||||
msgid "Fr"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:63
|
||||
msgid "Sa"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:64
|
||||
msgid "Su"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:67
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:68
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:69
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:70
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:71
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:72
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:73
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:74
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:75
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:76
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:77
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/widget/widget.js:78
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2020-01-24 08:00+0000\n"
|
||||
"Last-Translator: Prokaj Miklós <mixolid0@gmail.com>\n"
|
||||
"Language-Team: Hungarian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -603,15 +603,15 @@ msgstr ""
|
||||
msgid "Selected only"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Használj másik nevet"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Bezárásért kattints"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Mentetlen változtatások!"
|
||||
|
||||
@@ -683,22 +683,22 @@ msgstr "%(currency) %(price)-tól"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Adjon meg egy mennyiséget az egyik jegytípusból."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
#, fuzzy
|
||||
#| msgid "Cart expired"
|
||||
msgid "required"
|
||||
msgstr "A kosár lejárt"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr ""
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr ""
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-05-08 19:00+0000\n"
|
||||
"Last-Translator: Emanuele Signoretta <signorettae@gmail.com>\n"
|
||||
"Language-Team: Italian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -574,15 +574,15 @@ msgstr "Chiave di ricerca"
|
||||
msgid "Selected only"
|
||||
msgstr "Solo i selezionati"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Utilizza un nome diverso internamente"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Clicca per chiudere"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Hai cambiamenti non salvati!"
|
||||
|
||||
@@ -644,20 +644,20 @@ msgstr "Ricevi indietro %(currency)s %(amount)s"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Inserisci l'importo che l'organizzatore può trattenere."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Inserisci la quantità per una tipologia di biglietto."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "richiesto"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Fuso orario:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Ora locale:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-03-15 00:00+0000\n"
|
||||
"Last-Translator: Yuriko Matsunami <y.matsunami@enobyte.com>\n"
|
||||
"Language-Team: Japanese <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -571,15 +571,15 @@ msgstr "検索ワード"
|
||||
msgid "Selected only"
|
||||
msgstr "選択したもののみ"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "内部で別の名前を使用してください"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "クリックして閉じる"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "保存されていない変更があります!"
|
||||
|
||||
@@ -639,20 +639,20 @@ msgstr "%(currency)s %(amount)s が払い戻されます"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "イベント開催者が受け取る料金を入力してください。"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "商品の総数を入力してください。"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "必須"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "タイムゾーン:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "現地時間:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-04-06 03:00+0000\n"
|
||||
"Last-Translator: Liga V <lerning_by_dreaming@gmx.de>\n"
|
||||
"Language-Team: Latvian <https://translate.pretix.eu/projects/pretix/pretix-"
|
||||
@@ -578,15 +578,15 @@ msgstr "Meklēšanas pieprasījums"
|
||||
msgid "Selected only"
|
||||
msgstr "Tikai atzīmētos"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Izmantojiet citu nosaukumu iekšēji"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Noklikšķiniet, lai aizvērtu"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Jums ir nesaglabātas izmaiņas!"
|
||||
|
||||
@@ -650,20 +650,20 @@ msgstr "Jūs saņemsiet %(valūta)s %(cena)s atpakaļ"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Lūdzu ievadiet skaitu (summu), ko pasākuma organizators var paturēt."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Lūdzu, ievadiet nepieciešamo daudzumu izvēlētajam biļešu veidam."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "obligāts"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Laika zona:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Vietējais laiks:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2022-06-20 02:00+0000\n"
|
||||
"Last-Translator: fyksen <fredrik@fyksen.me>\n"
|
||||
"Language-Team: Norwegian Bokmål <https://translate.pretix.eu/projects/pretix/"
|
||||
@@ -566,15 +566,15 @@ msgstr "Søkeord"
|
||||
msgid "Selected only"
|
||||
msgstr "Kun valgte"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Bruk et annet navn internt"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Klikk for å lukke"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "Du har ikke-lagrede endringer!"
|
||||
|
||||
@@ -636,20 +636,20 @@ msgstr "Du mottar %(currency)s %(amount)s tilbake"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Vennligst skriv inn beløpet arrangøren kan beholde."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Vennligst skriv inn et antall for en av billetttypene."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "nødvendig"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Tidssone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Din lokale tid:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-11-29 20:31+0000\n"
|
||||
"POT-Creation-Date: 2022-12-14 13:09+0000\n"
|
||||
"PO-Revision-Date: 2021-10-29 02:00+0000\n"
|
||||
"Last-Translator: Maarten van den Berg <maartenberg1@gmail.com>\n"
|
||||
"Language-Team: Dutch <https://translate.pretix.eu/projects/pretix/pretix-js/"
|
||||
@@ -571,15 +571,15 @@ msgstr "Zoekopdracht"
|
||||
msgid "Selected only"
|
||||
msgstr "Alleen geselecteerde"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:858
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:862
|
||||
msgid "Use a different name internally"
|
||||
msgstr "Gebruik intern een andere naam"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:894
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:898
|
||||
msgid "Click to close"
|
||||
msgstr "Klik om te sluiten"
|
||||
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:966
|
||||
#: pretix/static/pretixcontrol/js/ui/main.js:970
|
||||
msgid "You have unsaved changes!"
|
||||
msgstr "U heeft nog niet opgeslagen wijzigingen!"
|
||||
|
||||
@@ -642,20 +642,20 @@ msgstr "U krijgt %(currency)s %(amount)s terug"
|
||||
msgid "Please enter the amount the organizer can keep."
|
||||
msgstr "Voer het bedrag in dat de organisator mag houden."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:388
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:393
|
||||
msgid "Please enter a quantity for one of the ticket types."
|
||||
msgstr "Voer een hoeveelheid voor een van de producten in."
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:424
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:429
|
||||
msgid "required"
|
||||
msgstr "verplicht"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:527
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:546
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:532
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:551
|
||||
msgid "Time zone:"
|
||||
msgstr "Tijdzone:"
|
||||
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:537
|
||||
#: pretix/static/pretixpresale/js/ui/main.js:542
|
||||
msgid "Your local time:"
|
||||
msgstr "Uw lokale tijd:"
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user