Files
pretix_cgo/src/pretix/control/forms/global_settings.py
rash f04df7a6ee Migrate vue2 control components and widget to vue3 and vite (#5989)
* setup vite and integrate fully with django

- vite starts with `python manage.py runserver`
- add templatetags to simply load vite hmr and entry points
- add eslint (recheck rules)
- enable non-strict ts

* better syntax for cors header setting

* migrate checkin rules editor to vue3

- move constants to a module
- move reading from and writing to non-vue html to django interop module
- switch to composition api and script setup sfc with pug
- use optional chaining operators a lot to simplify code

* migrate webcheckin plugin to vite+vue3

- migrate vue sfcs to script setup and pug
- move fetch calls into a api.ts module
- move common formatting and i18n strings into module

* fix migration error

* first draft migrating widget to vue3/vite

* first couple widget e2e tests

courtesy of claude
most of the tests don't work yet

* test file is not actually used

* drop widget_ prefix from e2e test fixtures

* add test for complete widget journey for simple event

* switch timezone in e2e tests to Europe/Berlin

* make dates in e2e tests relative

* migrate widget bugfix #5886

* start testing event series widget

* working vite widget setup for prod (untested), local dev (with or without dev server) and pytests, with flags for running the old version or the vite version

* simplify e2e test iframe check

* less flaky e2e tests

* top level await in iife build mode is not supported, so let's do import.meta.glob instead (we just need the build step not to see await, the code doesn't actually ever get loaded because it's DEV only)

* fix inconsistencies from automatic migration

* Allow gradual rollout of new vite-based widget by adding urls to an allowlist that gets checked against the "Origin" http header of request fetching the widget js

* add e2e tests for widget button, testing empty cart, adding specific items, and subevents

* remove janky claude testts again

* resolve migration TODOs: properly refocus parent on navigations

* use `npm run dev:control` for the vite dev server for admin components

* upgrade npm dependencies

* fix js linter errors

* fix python linter errors

* build all control vue components

* add new js config files to check-manifest ignore

* working prod build

acutal serving of built assets not tested yet

* fix templatetag paths to match what's in the vite mantifest

* add missing quotes around 'unsafe-eval' cors value

* remove now unused old vue2 tooling

* try fixing e2e test ci

* fix flake8 error

* check if vite build artefacts are in the wheel

* add license headers

* remove dom manipilation code necessary for `div.pretix-widget-compat` to work. No longer needed for vue3

* remove superfluous `createElement` calls

They might have been there because of IE, which is no longer relevant

* make widget dev mode parametizable through query params and document the usage and those params

* fix rst syntax

* remove migration todos file

Co-authored-by: luelista <mira@teamwiki.de>

* rearrange dockerfile commands for smaller image, thanks @luelista

* Update .gitignore, adding .vite

Co-authored-by: luelista <mira@teamwiki.de>

* add eslint CI

* make vue dev work in plugins

* fix docker build

* rebuild vite setup to support static prod plugins and dynamic hmr plugin development

* use toml for vite plugin config instead of standalone json file

* Add widget changes from #6047, #6149

* Allow buttons to reuse cart (Z#23226853)

* Always keep cart of buttons with items set

* widget: handle cart if not same-site (#6149)

---------

Co-authored-by: luelista <mira@teamwiki.de>
Co-authored-by: Kara Engelhardt <engelhardt@pretix.eu>
2026-05-11 15:05:06 +02:00

222 lines
11 KiB
Python

#
# This file is part of pretix (Community Edition).
#
# Copyright (C) 2014-2020 Raphael Michel and contributors
# Copyright (C) 2020-today pretix GmbH and contributors
#
# This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General
# Public License as published by the Free Software Foundation in version 3 of the License.
#
# ADDITIONAL TERMS APPLY: Pursuant to Section 7 of the GNU Affero General Public License, additional terms are
# applicable granting you additional permissions and placing additional restrictions on your usage of this software.
# Please refer to the pretix LICENSE file to obtain the full terms applicable to this work. If you did not receive
# this file, see <https://pretix.eu/about/en/license>.
#
# This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
# <https://www.gnu.org/licenses/>.
#
# This file is based on an earlier version of pretix which was released under the Apache License 2.0. The full text of
# the Apache License 2.0 can be obtained at <http://www.apache.org/licenses/LICENSE-2.0>.
#
# This file may have since been changed and any changes are released under the terms of AGPLv3 as described above. A
# full history of changes and contributors is available at <https://github.com/pretix/pretix>.
#
# This file contains Apache-licensed contributions copyrighted by: Jakob Schnell, Tobias Kunze
#
# Unless required by applicable law or agreed to in writing, software distributed under the Apache License 2.0 is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under the License.
from collections import OrderedDict
from django import forms
from django.utils.translation import gettext_lazy as _
from i18nfield.forms import I18nFormField, I18nTextInput
from pretix import settings
from pretix.base.forms import (
I18nMarkdownTextarea, SecretKeySettingsField, SettingsForm,
)
from pretix.base.settings import GlobalSettingsObject
from pretix.base.signals import register_global_settings
class GlobalSettingsForm(SettingsForm):
auto_fields = [
'region'
]
def __init__(self, *args, **kwargs):
self.obj = GlobalSettingsObject()
super().__init__(*args, obj=self.obj, **kwargs)
self.fields = OrderedDict(list(self.fields.items()) + [
('footer_text', I18nFormField(
widget=I18nTextInput,
required=False,
label=_("Additional footer text"),
help_text=_("Will be included as additional text in the footer, site-wide.")
)),
('footer_link', I18nFormField(
widget=I18nTextInput,
required=False,
label=_("Additional footer link"),
help_text=_("Will be included as the link in the additional footer text.")
)),
('banner_message', I18nFormField(
widget=I18nMarkdownTextarea,
required=False,
label=_("Global message banner"),
)),
('banner_message_detail', I18nFormField(
widget=I18nMarkdownTextarea,
required=False,
label=_("Global message banner detail text"),
)),
('opencagedata_apikey', SecretKeySettingsField(
required=False,
label=_("OpenCage API key for geocoding"),
)),
('mapquest_apikey', SecretKeySettingsField(
required=False,
label=_("MapQuest API key for geocoding"),
)),
('leaflet_tiles', forms.CharField(
required=False,
label=_("Leaflet tiles URL pattern"),
help_text=_("e.g. {sample}").format(sample="https://tile.openstreetmap.org/{z}/{x}/{y}.png")
)),
('leaflet_tiles_attribution', forms.CharField(
required=False,
label=_("Leaflet tiles attribution"),
help_text=_("e.g. {sample}").format(
sample='&copy; &lt;a href=&quot;https://www.openstreetmap.org/copyright&quot;&gt;OpenStreetMap&lt;/a&gt; contributors'
)
)),
('apple_domain_association', forms.CharField(
required=False,
label=_("ApplePay MerchantID Domain Association"),
help_text=_("Will be served at {domain}/.well-known/apple-developer-merchantid-domain-association").format(
domain=settings.SITE_URL
)
)),
('widget_vite_origins', forms.CharField(
widget=forms.Textarea(attrs={'rows': '3'}),
required=False,
label=_("Vite widget origins"),
help_text=_("One origin per line (e.g. https://example.com). Requests from these origins will be served the new vite-based widget."),
))
])
responses = register_global_settings.send(self)
for r, response in sorted(responses, key=lambda r: str(r[0])):
for key, value in response.items():
# We need to be this explicit, since OrderedDict.update does not retain ordering
self.fields[key] = value
self.fields['banner_message'].widget.attrs['rows'] = '2'
self.fields['banner_message_detail'].widget.attrs['rows'] = '3'
class UpdateSettingsForm(SettingsForm):
update_check_perform = forms.BooleanField(
required=False,
label=_("Perform update checks"),
help_text=_("During the update check, pretix will report an anonymous, unique installation ID, "
"the current version of pretix and your installed plugins and the number of active and "
"inactive events in your installation to servers operated by the pretix developers. We "
"will only store anonymous data, never any IP addresses and we will not know who you are "
"or where to find your instance. You can disable this behavior here at any time.")
)
update_check_email = forms.EmailField(
required=False,
label=_("Email notifications"),
help_text=_("We will notify you at this address if we detect that a new update is available. This "
"address will not be transmitted to pretix.eu, the emails will be sent by this server "
"locally.")
)
def __init__(self, *args, **kwargs):
self.obj = GlobalSettingsObject()
super().__init__(*args, obj=self.obj, **kwargs)
class LicenseCheckForm(forms.Form):
base_changes = forms.ChoiceField(
required=True,
label=_("Changes to pretix"),
widget=forms.RadioSelect,
choices=(
("no", _('This installation of pretix is running without any custom modifications or extensions '
'(except for installed plugins).')),
("yes", _('This installation of pretix includes changes or extensions made to the source code.')),
)
)
usage = forms.ChoiceField(
required=True,
label=_("Usage of pretix"),
widget=forms.RadioSelect,
choices=(
("internally", _('I only use pretix to organize events which are executed by my own company or its '
'affiliated companies, or to sell products sold by my own company.')),
("saas", _('I use pretix to sell tickets of other event organizers (e.g. a ticketing company) or I offer '
'the functionality of pretix to others (e.g. a Software-as-a-Service company).')),
("unsure", _('I\'m not sure which option applies.')),
)
)
base_license = forms.ChoiceField(
required=True,
label=_("License choice"),
widget=forms.RadioSelect,
choices=(
("agpl_addperm", _('I want to use pretix under the additional permission granted to everyone by the '
'copyright holders which allows me to not share modifications if I only use pretix '
'internally.')),
("agpl", _('I want to use pretix under the terms of the AGPLv3 license without restriction on the scope '
'of usage and therefore without making use of any additional permission.')),
("enterprise", _('I have obtained a paid pretix Enterprise license which is currently valid.'))
)
)
plugins_free = forms.BooleanField(
required=False,
label=_("This installation of pretix has installed plugins which are available freely under a non-copyleft "
"license (Apache License, MIT License, BSD license, …)."),
)
plugins_copyleft = forms.BooleanField(
required=False,
label=_("This installation of pretix has installed plugins which are available freely under a license with "
"strong copyleft (GPL, AGPL, …)."),
)
plugins_own = forms.BooleanField(
required=False,
label=_("This installation of pretix has installed plugins which have been created internally or obtained under "
"a proprietary license by a third party."),
)
plugins_enterprise = forms.BooleanField(
required=False,
label=_("This installation of pretix has installed pretix Enterprise plugins with a valid license."),
)
poweredby_name = forms.CharField(
required=False,
label=_('Footer: "powered by" name (optional)'),
help_text=_('If you want the "powered by" message in the page footer to include the name of your company or '
'organization (if you made any changes to pretix), set the name here.')
)
poweredby_url = forms.URLField(
required=False,
label=_('Link for powered by name'),
help_text=_('If you used the previous option, you can set an URL to link to in the footer.'),
)
source_notice = forms.CharField(
required=False,
label=_('Source code instructions'),
widget=forms.Textarea(attrs={'rows': '6'}),
help_text=_('If you use pretix under AGPLv3 terms, describe exactly how to download the current source code '
'of the site including all modifications and installed plugins. This will be publicly available. '
'Make sure to keep it up to date!'),
)