From f0a06cd9fea51799ecfe8bc9f29607990c34e5ed Mon Sep 17 00:00:00 2001 From: Raphael Michel Date: Tue, 25 Jun 2024 13:01:20 +0200 Subject: [PATCH] Replace SCSS compilation with CSS variables (#4191) * Replace SCSS compilation with CSS variables * Update tests * Update src/pretix/presale/style.py Co-authored-by: Mira * Update src/pretix/presale/context.py Co-authored-by: Mira * Update src/pretix/presale/views/widget.py Co-authored-by: Mira * Update src/pretix/presale/context.py Co-authored-by: Mira * Update src/pretix/static/pretixbase/scss/_variables.scss Co-authored-by: Richard Schreiber * Last minor changes * Rename file --------- Co-authored-by: Mira Co-authored-by: Richard Schreiber --- deployment/docker/pretix.bash | 2 +- doc/admin/installation/dev_version.rst | 2 +- doc/admin/installation/manual_smallscale.rst | 4 +- doc/development/api/general.rst | 2 +- doc/development/setup.rst | 2 +- src/pretix/api/views/event.py | 4 - src/pretix/api/views/organizer.py | 4 - src/pretix/base/models/event.py | 15 +- src/pretix/base/settings.py | 20 -- src/pretix/control/views/event.py | 15 +- src/pretix/control/views/organizer.py | 14 +- .../pretixplugins/webcheckin/scss/main.scss | 3 +- src/pretix/presale/context.py | 22 +- .../management/commands/updateassets.py | 61 ++++ .../management/commands/updatestyles.py | 49 +-- src/pretix/presale/signals.py | 27 -- src/pretix/presale/style.py | 236 ++++---------- .../presale/templates/pretixpresale/base.html | 11 +- .../pretixpresale/event/seatingplan.html | 11 +- src/pretix/presale/urls.py | 4 + src/pretix/presale/views/theme.py | 33 ++ src/pretix/presale/views/widget.py | 45 ++- src/pretix/static/bootstrap/README.md | 133 +------- .../fonts/glyphicons-halflings-regular.eot | Bin 20127 -> 0 bytes .../fonts/glyphicons-halflings-regular.svg | 288 ---------------- .../fonts/glyphicons-halflings-regular.ttf | Bin 45404 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff | Bin 23424 -> 0 bytes .../fonts/glyphicons-halflings-regular.woff2 | Bin 18028 -> 0 bytes .../static/bootstrap/scss/_bootstrap.scss | 1 - .../bootstrap/scss/_bootstrap_reduced.scss | 1 - .../bootstrap/scss/bootstrap/_alerts.scss | 8 +- .../bootstrap/scss/bootstrap/_buttons.scss | 12 +- .../bootstrap/scss/bootstrap/_forms.scss | 6 +- .../bootstrap/scss/bootstrap/_glyphicons.scss | 307 ------------------ .../bootstrap/scss/bootstrap/_labels.scss | 12 +- .../bootstrap/scss/bootstrap/_list-group.scss | 8 +- .../bootstrap/scss/bootstrap/_tables.scss | 10 +- .../bootstrap/scss/bootstrap/_type.scss | 20 +- .../scss/bootstrap/mixins/_alerts.scss | 6 +- .../bootstrap/mixins/_background-variant.scss | 4 +- .../scss/bootstrap/mixins/_buttons.scss | 19 +- .../scss/bootstrap/mixins/_forms.scss | 6 +- .../scss/bootstrap/mixins/_labels.scss | 4 +- .../scss/bootstrap/mixins/_list-group.scss | 4 +- .../scss/bootstrap/mixins/_table-row.scss | 4 +- .../scss/bootstrap/mixins/_text-emphasis.scss | 4 +- .../pretixbase/scss/_bootstrap_vars.scss | 159 +++++++++ .../static/pretixbase/scss/_contrast.scss | 18 +- src/pretix/static/pretixbase/scss/_theme.scss | 38 +-- .../pretixbase/scss/_theme_variables.scss | 134 ++++++++ .../static/pretixbase/scss/_variables.scss | 75 ----- .../static/pretixbase/scss/cachedfiles.scss | 3 +- src/pretix/static/pretixbase/scss/error.scss | 3 +- .../static/pretixcontrol/scss/_forms.scss | 2 +- .../pretixcontrol/scss/_sb-admin-2.scss | 6 +- .../static/pretixcontrol/scss/auth.scss | 3 +- .../static/pretixcontrol/scss/main.scss | 5 +- .../static/pretixpresale/scss/_calendar.scss | 50 +-- .../static/pretixpresale/scss/_event.scss | 2 +- .../static/pretixpresale/scss/_forms.scss | 4 +- .../static/pretixpresale/scss/_theme.scss | 8 +- .../static/pretixpresale/scss/main.scss | 13 +- .../static/pretixpresale/scss/waiting.scss | 3 +- .../static/pretixpresale/scss/widget.scss | 9 +- .../static/select2/_select2_bootstrap.scss | 16 +- .../static/slider/_bootstrap-slider.scss | 6 +- src/tests/api/test_events.py | 211 ++++++------ src/tests/api/test_organizers.py | 101 +++--- src/tests/control/test_events.py | 21 +- src/tests/control/test_organizer.py | 8 - src/tests/presale/test_style.py | 115 +------ src/tests/presale/test_widget.py | 11 +- 72 files changed, 867 insertions(+), 1600 deletions(-) create mode 100644 src/pretix/presale/management/commands/updateassets.py delete mode 100644 src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.eot delete mode 100644 src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.svg delete mode 100644 src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.ttf delete mode 100644 src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff delete mode 100644 src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 delete mode 100644 src/pretix/static/bootstrap/scss/bootstrap/_glyphicons.scss create mode 100644 src/pretix/static/pretixbase/scss/_bootstrap_vars.scss create mode 100644 src/pretix/static/pretixbase/scss/_theme_variables.scss delete mode 100644 src/pretix/static/pretixbase/scss/_variables.scss diff --git a/deployment/docker/pretix.bash b/deployment/docker/pretix.bash index cacd6aafd3..76a52e0631 100644 --- a/deployment/docker/pretix.bash +++ b/deployment/docker/pretix.bash @@ -47,7 +47,7 @@ if [ "$1" == "taskworker" ]; then fi if [ "$1" == "upgrade" ]; then - exec python3 -m pretix updatestyles + exec python3 -m pretix updateassets fi exec python3 -m pretix "$@" diff --git a/doc/admin/installation/dev_version.rst b/doc/admin/installation/dev_version.rst index d56d5d96e4..3cb4dbd1b1 100644 --- a/doc/admin/installation/dev_version.rst +++ b/doc/admin/installation/dev_version.rst @@ -19,7 +19,7 @@ You can use ``pip`` to update pretix directly to the development branch. Then, u (venv)$ pip3 install -U "git+https://github.com/pretix/pretix.git#egg=pretix" (venv)$ python -m pretix migrate (venv)$ python -m pretix rebuild - (venv)$ python -m pretix updatestyles + (venv)$ python -m pretix updateassets # systemctl restart pretix-web pretix-worker Docker installation diff --git a/doc/admin/installation/manual_smallscale.rst b/doc/admin/installation/manual_smallscale.rst index 804dcd2db8..a0d3c16ad2 100644 --- a/doc/admin/installation/manual_smallscale.rst +++ b/doc/admin/installation/manual_smallscale.rst @@ -285,7 +285,7 @@ To upgrade to a new pretix release, pull the latest code changes and run the fol (venv)$ pip3 install -U --upgrade-strategy eager pretix gunicorn (venv)$ python -m pretix migrate (venv)$ python -m pretix rebuild - (venv)$ python -m pretix updatestyles + (venv)$ python -m pretix updateassets # systemctl restart pretix-web pretix-worker Make sure to also read :ref:`update_notes` and the release notes of the version you are updating to. Pay special @@ -325,7 +325,7 @@ Then, proceed like after any plugin installation:: (venv)$ python -m pretix migrate (venv)$ python -m pretix rebuild - (venv)$ python -m pretix updatestyles + (venv)$ python -m pretix updateassets # 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-22-04 diff --git a/doc/development/api/general.rst b/doc/development/api/general.rst index dab1cbc78e..4eb26229c8 100644 --- a/doc/development/api/general.rst +++ b/doc/development/api/general.rst @@ -35,7 +35,7 @@ Frontend -------- .. automodule:: pretix.presale.signals - :members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, sass_preamble, sass_postamble, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header + :members: html_head, html_footer, footer_link, global_footer_link, front_page_top, front_page_bottom, front_page_bottom_widget, fee_calculation_for_cart, contact_form_fields, question_form_fields, contact_form_fields_overrides, question_form_fields_overrides, checkout_confirm_messages, checkout_confirm_page_content, checkout_all_optional, html_page_header, render_seating_plan, checkout_flow_steps, position_info, position_info_top, item_description, global_html_head, global_html_footer, global_html_page_header .. automodule:: pretix.presale.signals diff --git a/doc/development/setup.rst b/doc/development/setup.rst index fc83e80cb9..ae166d18cf 100644 --- a/doc/development/setup.rst +++ b/doc/development/setup.rst @@ -218,7 +218,7 @@ To update the frontend styles of shops with a custom styling, run the following your virtual environment.:: python -m pretix collectstatic --noinput - python -m pretix updatestyles + python -m pretix updateassets .. _Django's documentation: https://docs.djangoproject.com/en/1.11/ref/django-admin/#runserver diff --git a/src/pretix/api/views/event.py b/src/pretix/api/views/event.py index 25bcf12da3..f98122dc25 100644 --- a/src/pretix/api/views/event.py +++ b/src/pretix/api/views/event.py @@ -57,10 +57,8 @@ from pretix.base.models import ( ) from pretix.base.models.event import SubEvent from pretix.base.services.quotas import QuotaAvailability -from pretix.base.settings import SETTINGS_AFFECTING_CSS from pretix.helpers.dicts import merge_dicts from pretix.helpers.i18n import i18ncomp -from pretix.presale.style import regenerate_css from pretix.presale.views.organizer import filter_qs_by_attr with scopes_disabled(): @@ -636,8 +634,6 @@ class EventSettingsView(views.APIView): k: v for k, v in s.validated_data.items() } ) - if any(p in s.changed_data for p in SETTINGS_AFFECTING_CSS): - regenerate_css.apply_async(args=(request.event.pk,)) s = EventSettingsSerializer( instance=request.event.settings, event=request.event, context={ 'request': request diff --git a/src/pretix/api/views/organizer.py b/src/pretix/api/views/organizer.py index ddeaf96c1f..a83ffcd434 100644 --- a/src/pretix/api/views/organizer.py +++ b/src/pretix/api/views/organizer.py @@ -51,10 +51,8 @@ from pretix.base.models import ( MembershipType, Organizer, SeatingPlan, Team, TeamAPIToken, TeamInvite, User, ) -from pretix.base.settings import SETTINGS_AFFECTING_CSS from pretix.helpers import OF_SELF from pretix.helpers.dicts import merge_dicts -from pretix.presale.style import regenerate_organizer_css class OrganizerViewSet(viewsets.ReadOnlyModelViewSet): @@ -504,8 +502,6 @@ class OrganizerSettingsView(views.APIView): k: v for k, v in s.validated_data.items() } ) - if any(p in s.changed_data for p in SETTINGS_AFFECTING_CSS): - regenerate_organizer_css.apply_async(args=(request.organizer.pk,)) s = OrganizerSettingsSerializer(instance=request.organizer.settings, organizer=request.organizer, context={ 'request': request }) diff --git a/src/pretix/base/models/event.py b/src/pretix/base/models/event.py index 5528338ac6..4493e2b575 100644 --- a/src/pretix/base/models/event.py +++ b/src/pretix/base/models/event.py @@ -789,8 +789,6 @@ class Event(EventMixin, LoggedModel): ), tz) def copy_data_from(self, other, skip_meta_data=False): - from pretix.presale.style import regenerate_css - from ..signals import event_copy_data from . import ( Discount, Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaValue, @@ -1011,10 +1009,10 @@ class Event(EventMixin, LoggedModel): s.product = item_map[s.product_id] s.save(force_insert=True) - has_custom_style = other.settings.presale_css_file or other.settings.presale_widget_css_file skip_settings = ( 'ticket_secrets_pretix_sig1_pubkey', 'ticket_secrets_pretix_sig1_privkey', + # no longer used, but we still don't need to copy them 'presale_css_file', 'presale_css_checksum', 'presale_widget_css_file', @@ -1057,9 +1055,6 @@ class Event(EventMixin, LoggedModel): question_map=question_map, checkin_list_map=checkin_list_map, quota_map=quota_map, ) - if has_custom_style: - regenerate_css.apply_async(args=(self.pk,)) - def get_payment_providers(self, cached=False) -> dict: """ Returns a dictionary of initialized payment providers mapped by their identifiers. @@ -1337,18 +1332,12 @@ class Event(EventMixin, LoggedModel): def enable_plugin(self, module, allow_restricted=frozenset()): plugins_active = self.get_plugins() - from pretix.presale.style import regenerate_css - if module not in plugins_active: plugins_active.append(module) self.set_active_plugins(plugins_active, allow_restricted=allow_restricted) - regenerate_css.apply_async(args=(self.pk,)) - def disable_plugin(self, module): plugins_active = self.get_plugins() - from pretix.presale.style import regenerate_css - if module in plugins_active: plugins_active.remove(module) self.set_active_plugins(plugins_active) @@ -1357,8 +1346,6 @@ class Event(EventMixin, LoggedModel): if module in plugins_available and hasattr(plugins_available[module].app, 'uninstalled'): getattr(plugins_available[module].app, 'uninstalled')(self) - regenerate_css.apply_async(args=(self.pk,)) - @staticmethod def clean_has_subevents(event, has_subevents): if event is not None and event.has_subevents is not None: diff --git a/src/pretix/base/settings.py b/src/pretix/base/settings.py index 48af6f0597..8bd6af3a29 100644 --- a/src/pretix/base/settings.py +++ b/src/pretix/base/settings.py @@ -2851,22 +2851,6 @@ Your {organizer} team""")) # noqa: W291 **primary_font_kwargs() ), }, - 'presale_css_file': { - 'default': None, - 'type': str - }, - 'presale_css_checksum': { - 'default': None, - 'type': str - }, - 'presale_widget_css_file': { - 'default': None, - 'type': str - }, - 'presale_widget_css_checksum': { - 'default': None, - 'type': str - }, 'logo_image': { 'default': None, 'type': File, @@ -3396,10 +3380,6 @@ Your {organizer} team""")) # noqa: W291 'type': str, } } -SETTINGS_AFFECTING_CSS = { - 'primary_color', 'theme_color_success', 'theme_color_danger', 'primary_font', - 'theme_color_background', 'theme_round_borders' -} PERSON_NAME_TITLE_GROUPS = OrderedDict([ ('english_common', (_('Most common English titles'), ( 'Mr', diff --git a/src/pretix/control/views/event.py b/src/pretix/control/views/event.py index 15f5f68018..8558eed2a0 100644 --- a/src/pretix/control/views/event.py +++ b/src/pretix/control/views/event.py @@ -94,13 +94,12 @@ from pretix.control.views.user import RecentAuthenticationRequiredMixin from pretix.helpers.database import rolledback_transaction from pretix.multidomain.urlreverse import build_absolute_uri, get_event_domain from pretix.plugins.stripe.payment import StripeSettingsHolder -from pretix.presale.style import regenerate_css from ...base.i18n import language from ...base.models.items import ( Item, ItemCategory, ItemMetaProperty, Question, Quota, ) -from ...base.settings import SETTINGS_AFFECTING_CSS, LazyI18nStringList +from ...base.settings import LazyI18nStringList from ...helpers.compat import CompatDeleteView from ...helpers.format import format_map from ..logdisplay import OVERVIEW_BANLIST @@ -202,19 +201,17 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired def form_valid(self, form): self._save_decoupled(self.sform) self.sform.save() + self.object.cache.clear() self.save_meta() self.save_item_meta_property_formset(self.object) self.save_confirm_texts_formset(self.object) self.save_footer_links_formset(self.object) - change_css = False if self.sform.has_changed() or self.confirm_texts_formset.has_changed(): data = {k: self.request.event.settings.get(k) for k in self.sform.changed_data} if self.confirm_texts_formset.has_changed(): data.update(confirm_texts=self.confirm_texts_formset.cleaned_data) self.request.event.log_action('pretix.event.settings', user=self.request.user, data=data) - if any(p in self.sform.changed_data for p in SETTINGS_AFFECTING_CSS): - change_css = True if self.footer_links_formset.has_changed(): self.request.event.log_action('pretix.event.footerlinks.changed', user=self.request.user, data={ 'data': self.footer_links_formset.cleaned_data @@ -228,13 +225,7 @@ class EventUpdate(DecoupleMixin, EventSettingsViewMixin, EventPermissionRequired }) tickets.invalidate_cache.apply_async(kwargs={'event': self.request.event.pk}) - if change_css: - regenerate_css.apply_async(args=(self.request.event.pk,)) - messages.success(self.request, _('Your changes have been saved. Please note that it can ' - 'take a short period of time until your changes become ' - 'active.')) - else: - messages.success(self.request, _('Your changes have been saved.')) + messages.success(self.request, _('Your changes have been saved.')) return super().form_valid(form) def get_success_url(self) -> str: diff --git a/src/pretix/control/views/organizer.py b/src/pretix/control/views/organizer.py index e817128dec..852721336a 100644 --- a/src/pretix/control/views/organizer.py +++ b/src/pretix/control/views/organizer.py @@ -91,7 +91,6 @@ from pretix.base.models.organizer import TeamAPIToken from pretix.base.payment import PaymentException from pretix.base.services.export import multiexport, scheduled_organizer_export from pretix.base.services.mail import SendMailException, mail -from pretix.base.settings import SETTINGS_AFFECTING_CSS from pretix.base.signals import register_multievent_data_exporters from pretix.base.templatetags.rich_text import markdown_compile_email from pretix.base.views.tasks import AsyncAction @@ -127,7 +126,6 @@ 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 -from pretix.presale.style import regenerate_organizer_css class OrganizerList(PaginationMixin, ListView): @@ -466,7 +464,7 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView): def form_valid(self, form): self.sform.save() self.save_footer_links_formset(self.object) - change_css = False + self.object.cache.clear() if self.sform.has_changed(): self.request.organizer.log_action( 'pretix.organizer.settings', @@ -478,8 +476,6 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView): for k in self.sform.changed_data } ) - if any(p in self.sform.changed_data for p in SETTINGS_AFFECTING_CSS): - change_css = True if self.footer_links_formset.has_changed(): self.request.organizer.log_action('pretix.organizer.footerlinks.changed', user=self.request.user, data={ 'data': self.footer_links_formset.cleaned_data @@ -491,13 +487,7 @@ class OrganizerUpdate(OrganizerPermissionRequiredMixin, UpdateView): data={k: form.cleaned_data.get(k) for k in form.changed_data} ) - if change_css: - regenerate_organizer_css.apply_async(args=(self.request.organizer.pk,)) - messages.success(self.request, _('Your changes have been saved. Please note that it can ' - 'take a short period of time until your changes become ' - 'active.')) - else: - messages.success(self.request, _('Your changes have been saved.')) + messages.success(self.request, _('Your changes have been saved.')) return super().form_valid(form) def get_form_kwargs(self): diff --git a/src/pretix/plugins/webcheckin/static/pretixplugins/webcheckin/scss/main.scss b/src/pretix/plugins/webcheckin/static/pretixplugins/webcheckin/scss/main.scss index 7697d02996..1ba7b37a34 100644 --- a/src/pretix/plugins/webcheckin/static/pretixplugins/webcheckin/scss/main.scss +++ b/src/pretix/plugins/webcheckin/static/pretixplugins/webcheckin/scss/main.scss @@ -1,4 +1,5 @@ -@import "pretixbase/scss/_variables.scss"; +@import "pretixbase/scss/_theme_variables.scss"; +@import "pretixbase/scss/_bootstrap_vars.scss"; @import "bootstrap/scss/_bootstrap.scss"; @import "pretixbase/scss/_theme.scss"; @import "fontawesome/scss/font-awesome.scss"; diff --git a/src/pretix/presale/context.py b/src/pretix/presale/context.py index 80050a4e50..ebb939b9e8 100644 --- a/src/pretix/presale/context.py +++ b/src/pretix/presale/context.py @@ -32,9 +32,9 @@ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under the License. import logging +import time from django.conf import settings -from django.core.files.storage import default_storage from django.utils import translation from django.utils.translation import get_language_info from django_scopes import get_scope @@ -46,12 +46,14 @@ from pretix.helpers.i18n import ( ) from ..base.i18n import get_language_without_region +from ..multidomain.urlreverse import eventreverse from .cookies import get_cookie_providers from .signals import ( footer_link, global_footer_link, global_html_footer, global_html_head, global_html_page_header, html_footer, html_head, html_page_header, ) from .views.cart import cart_session, get_or_create_cart_id +from .views.theme import _get_source_cache_key logger = logging.getLogger(__name__) @@ -70,7 +72,6 @@ def _default_context(request): return {} ctx = { - 'css_file': None, 'DEBUG': settings.DEBUG, } _html_head = [] @@ -80,10 +81,22 @@ def _default_context(request): if hasattr(request, 'event') and request.event: pretix_settings = request.event.settings + + # This makes sure a new version of the theme is loaded whenever settings or the source files have changed + theme_css_version = (f'{_get_source_cache_key()}-' + f'{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}-' + f'{request.event.cache.get_or_set("css_version", default=lambda: int(time.time()))}') + ctx['css_theme'] = eventreverse(request.event, "presale:event.theme.css") + "?version=" + theme_css_version + elif hasattr(request, 'organizer') and request.organizer: pretix_settings = request.organizer.settings + + # This makes sure a new version of the theme is loaded whenever settings or the source files have changed + theme_css_version = f'{_get_source_cache_key()}-{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}' + ctx['css_theme'] = eventreverse(request.organizer, "presale:organizer.theme.css") + "?version=" + theme_css_version else: pretix_settings = GlobalSettingsObject().settings + ctx['css_theme'] = None text = pretix_settings.get('footer_text', as_type=LazyI18nString) link = pretix_settings.get('footer_link', as_type=LazyI18nString) @@ -124,9 +137,6 @@ def _default_context(request): for fl in request.event.footer_links.all() ], timeout=300) - if request.event.settings.presale_css_file: - ctx['css_file'] = default_storage.url(request.event.settings.presale_css_file) - ctx['event_logo'] = request.event.settings.get('logo_image', as_type=str, default='')[7:] ctx['event_logo_image_large'] = request.event.settings.logo_image_large ctx['event_logo_show_title'] = request.event.settings.logo_show_title @@ -158,8 +168,6 @@ def _default_context(request): ctx['languages'] = [get_language_info(code) for code in request.organizer.settings.locales] if request.resolver_match and hasattr(request, 'organizer'): - if request.organizer.settings.presale_css_file and not hasattr(request, 'event'): - ctx['css_file'] = default_storage.url(request.organizer.settings.presale_css_file) ctx['organizer_logo'] = request.organizer.settings.get('organizer_logo_image', as_type=str, default='')[7:] ctx['organizer_homepage_text'] = request.organizer.settings.get('organizer_homepage_text', as_type=LazyI18nString) ctx['organizer'] = request.organizer diff --git a/src/pretix/presale/management/commands/updateassets.py b/src/pretix/presale/management/commands/updateassets.py new file mode 100644 index 0000000000..8acfefecff --- /dev/null +++ b/src/pretix/presale/management/commands/updateassets.py @@ -0,0 +1,61 @@ +# +# 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 . +# +# 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 +# . +# +import hashlib + +from django.conf import settings +from django.core.cache import cache +from django.core.files.base import ContentFile, File +from django.core.files.storage import default_storage +from django.core.management.base import BaseCommand +from django_scopes import scopes_disabled + +from pretix.base.settings import GlobalSettingsObject +from pretix.presale.views.widget import generate_widget_js + + +class Command(BaseCommand): + help = "Re-generate runtime-generated assets and scripts" + + def add_arguments(self, parser): + parser.add_argument('--organizer', action='store', type=str) + parser.add_argument('--event', action='store', type=str) + + @scopes_disabled() + def handle(self, *args, **options): + gs = GlobalSettingsObject() + for lc, ll in settings.LANGUAGES: + data = generate_widget_js(lc).encode() + checksum = hashlib.sha1(data).hexdigest() + fname = gs.settings.get('widget_file_{}'.format(lc)) + if not fname or gs.settings.get('widget_checksum_{}'.format(lc), '') != checksum: + newname = default_storage.save( + 'pub/widget/widget.{}.{}.js'.format(lc, checksum), + ContentFile(data) + ) + gs.settings.set('widget_file_{}'.format(lc), 'file://' + newname) + gs.settings.set('widget_checksum_{}'.format(lc), checksum) + cache.delete('widget_js_data_{}'.format(lc)) + if fname: + if isinstance(fname, File): + default_storage.delete(fname.name) + else: + default_storage.delete(fname) diff --git a/src/pretix/presale/management/commands/updatestyles.py b/src/pretix/presale/management/commands/updatestyles.py index b6be8066a5..f6bdf0b59a 100644 --- a/src/pretix/presale/management/commands/updatestyles.py +++ b/src/pretix/presale/management/commands/updatestyles.py @@ -19,22 +19,10 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -import hashlib -from django.conf import settings -from django.core.cache import cache -from django.core.files.base import ContentFile, File -from django.core.files.storage import default_storage from django.core.management.base import BaseCommand -from django.utils.timezone import now from django_scopes import scopes_disabled -from pretix.base.models import Event_SettingsStore, Organizer_SettingsStore -from pretix.base.settings import GlobalSettingsObject -from pretix.presale.views.widget import generate_widget_js - -from ...style import regenerate_css, regenerate_organizer_css - class Command(BaseCommand): help = "Re-generate all custom stylesheets and scripts" @@ -45,38 +33,7 @@ class Command(BaseCommand): @scopes_disabled() def handle(self, *args, **options): - # Reset compile cache - cache.set('sass_compile_prefix', now().isoformat()) + from .updateassets import Command as UCommand - ostore = Organizer_SettingsStore.objects.filter(key="presale_css_file") - if options.get('organizer'): - ostore = ostore.filter(object__slug=options['organizer']) - for es in ostore: - regenerate_organizer_css.apply_async(args=(es.object_id,), kwargs={'regenerate_events': False}) - - estore = Event_SettingsStore.objects.filter(key="presale_css_file").order_by('-object__date_from') - if options.get('event'): - estore = estore.filter(object__slug=options['event']) - if options.get('organizer'): - estore = estore.filter(object__organizer__slug=options['event']) - for es in estore: - regenerate_css.apply_async(args=(es.object_id,)) - - gs = GlobalSettingsObject() - for lc, ll in settings.LANGUAGES: - data = generate_widget_js(lc).encode() - checksum = hashlib.sha1(data).hexdigest() - fname = gs.settings.get('widget_file_{}'.format(lc)) - if not fname or gs.settings.get('widget_checksum_{}'.format(lc), '') != checksum: - newname = default_storage.save( - 'pub/widget/widget.{}.{}.js'.format(lc, checksum), - ContentFile(data) - ) - gs.settings.set('widget_file_{}'.format(lc), 'file://' + newname) - gs.settings.set('widget_checksum_{}'.format(lc), checksum) - cache.delete('widget_js_data_{}'.format(lc)) - if fname: - if isinstance(fname, File): - default_storage.delete(fname.name) - else: - default_storage.delete(fname) + self.stdout.write(self.style.WARNING("Command 'updatestyles' is deprecated, use 'updateassets' instead.")) + UCommand().handle(*args, **options) diff --git a/src/pretix/presale/signals.py b/src/pretix/presale/signals.py index cfff7aa68c..bc1af19fe4 100644 --- a/src/pretix/presale/signals.py +++ b/src/pretix/presale/signals.py @@ -102,33 +102,6 @@ of every page in the frontend. You will get the request as the keyword argument As with all plugin signals, the ``sender`` keyword argument will contain the event. """ -sass_preamble = EventPluginSignal() -""" -Arguments: ``filename`` - -This signal allows you to put SASS code at the beginning of the event-specific -stylesheet. Keep in mind that this will only be called/rebuilt when the user changes -display settings or pretix gets updated. You will get the filename that is being -generated (usually "main.scss" or "widget.scss"). This SASS code will be loaded *after* -setting of user-defined variables like colors and fonts but *before* pretix' SASS -code. - -As with all plugin signals, the ``sender`` keyword argument will contain the event. -""" - -sass_postamble = EventPluginSignal() -""" -Arguments: ``filename`` - -This signal allows you to put SASS code at the end of the event-specific -stylesheet. Keep in mind that this will only be called/rebuilt when the user changes -display settings or pretix gets updated. You will get the filename that is being -generated (usually "main.scss" or "widget.scss"). This SASS code will be loaded *after* -all of pretix' SASS code. - -As with all plugin signals, the ``sender`` keyword argument will contain the event. -""" - footer_link = EventPluginSignal() """ Arguments: ``request`` diff --git a/src/pretix/presale/style.py b/src/pretix/presale/style.py index ccf73f4349..9c0d27ce89 100644 --- a/src/pretix/presale/style.py +++ b/src/pretix/presale/style.py @@ -19,186 +19,23 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # -import gzip -import hashlib import logging import os from urllib.parse import urljoin, urlsplit -import django_libsass import sass -from compressor.filters.cssmin import CSSMinFilter from django.conf import settings -from django.core.cache import cache -from django.core.files.base import ContentFile, File -from django.core.files.storage import default_storage +from django.contrib.staticfiles import finders from django.dispatch import Signal from django.templatetags.static import static as _static -from django.utils.timezone import now -from django_scopes import scope -from pretix.base.models import Event, Event_SettingsStore, Organizer -from pretix.base.services.tasks import ( - TransactionAwareProfiledEventTask, TransactionAwareTask, -) +from pretix.base.models import Event, Organizer from pretix.base.signals import EventPluginSignal -from pretix.celery_app import app from pretix.multidomain.urlreverse import ( get_event_domain, get_organizer_domain, ) -from pretix.presale.signals import sass_postamble, sass_preamble logger = logging.getLogger('pretix.presale.style') -affected_keys = ['primary_font', 'primary_color', 'theme_color_success', 'theme_color_danger', 'theme_color_background', 'theme_round_borders'] - - -def compile_scss(object, file="main.scss", fonts=True): - sassdir = os.path.join(settings.STATIC_ROOT, 'pretixpresale/scss') - - def static(path): - sp = _static(path) - if not settings.MEDIA_URL.startswith("/") and sp.startswith("/"): - if isinstance(object, Event): - domain = get_event_domain(object, fallback=True) - else: - domain = get_organizer_domain(object) - if domain: - siteurlsplit = urlsplit(settings.SITE_URL) - if siteurlsplit.port and siteurlsplit.port not in (80, 443): - domain = '%s:%d' % (domain, siteurlsplit.port) - sp = urljoin('%s://%s' % (siteurlsplit.scheme, domain), sp) - else: - sp = urljoin(settings.SITE_URL, sp) - return '"{}"'.format(sp) - - sassrules = [] - if object.settings.get('primary_color'): - sassrules.append('$brand-primary: {};'.format(object.settings.get('primary_color'))) - if object.settings.get('theme_color_success'): - sassrules.append('$brand-success: {};'.format(object.settings.get('theme_color_success'))) - if object.settings.get('theme_color_danger'): - sassrules.append('$brand-danger: {};'.format(object.settings.get('theme_color_danger'))) - if object.settings.get('theme_color_background'): - sassrules.append('$body-bg: {};'.format(object.settings.get('theme_color_background'))) - if not object.settings.get('theme_round_borders'): - sassrules.append('$border-radius-base: 0;') - sassrules.append('$border-radius-large: 0;') - sassrules.append('$border-radius-small: 0;') - - font = object.settings.get('primary_font') - if font != 'Open Sans' and fonts: - sassrules.append(get_font_stylesheet(font, event=object if isinstance(object, Event) else None)) - sassrules.append( - '$font-family-sans-serif: "{}", "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif ' - '!default'.format( - font - )) - - if isinstance(object, Event): - for recv, resp in sass_preamble.send(object, filename=file): - sassrules.append(resp) - - sassrules.append('@import "{}";'.format(file)) - - if isinstance(object, Event): - for recv, resp in sass_postamble.send(object, filename=file): - sassrules.append(resp) - - sasssrc = "\n".join(sassrules) - srcchecksum = hashlib.sha1(sasssrc.encode('utf-8')).hexdigest() - - cp = cache.get_or_set('sass_compile_prefix', now().isoformat()) - css = cache.get('sass_compile_{}_{}'.format(cp, srcchecksum)) - if css: - if isinstance(css, bytes) and css[0:2] == b'\x1f\x8b': - css = gzip.decompress(css).decode() - else: - cf = dict(django_libsass.CUSTOM_FUNCTIONS) - cf['static'] = static - css = sass.compile( - string=sasssrc, - include_paths=[sassdir], output_style='nested', - custom_functions=cf - ) - cssf = CSSMinFilter(css) - css = cssf.output() - cache.set('sass_compile_{}_{}'.format(cp, srcchecksum), gzip.compress(css.encode()), 600) - - checksum = hashlib.sha1(css.encode('utf-8')).hexdigest() - return css, checksum - - -def delete_old_file(fname): - if fname: - if isinstance(fname, File): - default_storage.delete(fname.name) - else: - default_storage.delete(fname) - - -@app.task(base=TransactionAwareProfiledEventTask) -def regenerate_css(event): - settings = event.settings._cache() # ignore organizer settings - - # main.scss - css, checksum = compile_scss(event) - fname = 'pub/{}/{}/presale.{}.css'.format(event.organizer.slug, event.slug, checksum[:16]) - - if settings.get('presale_css_checksum', '') != checksum: - old_fname = settings.get('presale_css_file') - newname = default_storage.save(fname, ContentFile(css.encode('utf-8'))) - event.settings.set('presale_css_file', newname) - event.settings.set('presale_css_checksum', checksum) - if old_fname and old_fname != newname and f'/{event.slug}/' in old_fname: - delete_old_file(old_fname) - - # widget.scss - css, checksum = compile_scss(event, file='widget.scss', fonts=False) - fname = 'pub/{}/{}/widget.{}.css'.format(event.organizer.slug, event.slug, checksum[:16]) - - if settings.get('presale_widget_css_checksum', '') != checksum: - old_fname = settings.get('presale_widget_css_file') - newname = default_storage.save(fname, ContentFile(css.encode('utf-8'))) - event.settings.set('presale_widget_css_file', newname) - event.settings.set('presale_widget_css_checksum', checksum) - if old_fname and old_fname != newname and f'/{event.slug}/' in old_fname: - delete_old_file(old_fname) - - -@app.task(base=TransactionAwareTask) -def regenerate_organizer_css(organizer_id: int, regenerate_events=True): - organizer = Organizer.objects.get(pk=organizer_id) - - with scope(organizer=organizer): - # main.scss - css, checksum = compile_scss(organizer) - fname = 'pub/{}/presale.{}.css'.format(organizer.slug, checksum[:16]) - if organizer.settings.get('presale_css_checksum', '') != checksum: - old_fname = organizer.settings.get('presale_css_file') - newname = default_storage.save(fname, ContentFile(css.encode('utf-8'))) - organizer.settings.set('presale_css_file', newname) - organizer.settings.set('presale_css_checksum', checksum) - if old_fname != newname: - delete_old_file(old_fname) - - # widget.scss - css, checksum = compile_scss(organizer, file='widget.scss', fonts=False) - fname = 'pub/{}/widget.{}.css'.format(organizer.slug, checksum[:16]) - if organizer.settings.get('presale_widget_css_checksum', '') != checksum: - old_fname = organizer.settings.get('presale_widget_css_file') - newname = default_storage.save(fname, ContentFile(css.encode('utf-8'))) - organizer.settings.set('presale_widget_css_file', newname) - organizer.settings.set('presale_widget_css_checksum', checksum) - if old_fname != newname: - delete_old_file(old_fname) - - if regenerate_events: - non_inherited_events = set(Event_SettingsStore.objects.filter( - object__organizer=organizer, key__in=affected_keys - ).values_list('object_id', flat=True)) - for event in organizer.events.all(): - if event.pk not in non_inherited_events: - regenerate_css.apply_async(args=(event.pk,)) register_fonts = Signal() @@ -289,7 +126,25 @@ def get_fonts(event: Event = None, pdf_support_required=False): return f -def get_font_stylesheet(font_name, event: Event = None): +def get_font_stylesheet(font_name, organizer: Organizer = None, event: Event = None, absolute=True): + def static(path): + sp = _static(path) + if sp.startswith("/") and absolute: + if event: + domain = get_event_domain(event, fallback=True) + elif organizer: + domain = get_organizer_domain(organizer) + else: + domain = None + if domain: + siteurlsplit = urlsplit(settings.SITE_URL) + if siteurlsplit.port and siteurlsplit.port not in (80, 443): + domain = '%s:%d' % (domain, siteurlsplit.port) + sp = urljoin('%s://%s' % (siteurlsplit.scheme, domain), sp) + else: + sp = urljoin(settings.SITE_URL, sp) + return sp + stylesheet = [] font = get_fonts(event)[font_name] for sty, formats in font.items(): @@ -312,8 +167,55 @@ def get_font_stylesheet(font_name, event: Event = None): if formats[f].startswith('https'): srcs.append(f"url('{formats[f]}') format('{f}')") else: - srcs.append(f"url(static('{formats[f]}')) format('{f}')") + srcs.append(f"url('{static(formats[f])}') format('{f}')") stylesheet.append("src: {};".format(", ".join(srcs))) stylesheet.append("font-display: swap;") stylesheet.append("}") return "\n".join(stylesheet) + + +def get_theme_vars_css(obj, widget=False): + sassrules = [] + if obj.settings.get("primary_color"): + sassrules.append("$in-brand-primary: {};".format(obj.settings.get("primary_color"))) + if obj.settings.get("theme_color_success"): + sassrules.append("$in-brand-success: {};".format(obj.settings.get("theme_color_success"))) + if obj.settings.get("theme_color_danger"): + sassrules.append("$in-brand-danger: {};".format(obj.settings.get("theme_color_danger"))) + if obj.settings.get("theme_color_background"): + sassrules.append("$in-body-bg: {};".format(obj.settings.get("theme_color_background"))) + if not obj.settings.get("theme_round_borders"): + sassrules.append("$in-border-radius-base: 0;") + sassrules.append("$in-border-radius-large: 0;") + sassrules.append("$in-border-radius-small: 0;") + + font = obj.settings.get("primary_font") + if font != "Open Sans" and not widget: + sassrules.append(get_font_stylesheet( + font, + event=obj if isinstance(obj, Event) else None, + organizer=obj.organizer if isinstance(obj, Event) else obj, + absolute=False, + )) + sassrules.append( + '$in-font-family-sans-serif: "{}", "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif'.format( + font + ) + ) + + if widget: + sassrules.append("$widget: true;") + + with open(finders.find("pretixbase/scss/_theme_variables.scss"), "r") as f: + source_scss = f.read() + sassrules.append(source_scss) + + sassdir = os.path.join(settings.STATIC_ROOT, "pretixbase/scss") + sassrule = "\n".join(sassrules) + if not sassrule.strip(): + return "" + css = sass.compile( + string=sassrule, + include_paths=[sassdir] + ) + return css diff --git a/src/pretix/presale/templates/pretixpresale/base.html b/src/pretix/presale/templates/pretixpresale/base.html index a301d4b237..4c86ec626b 100644 --- a/src/pretix/presale/templates/pretixpresale/base.html +++ b/src/pretix/presale/templates/pretixpresale/base.html @@ -8,12 +8,11 @@ {% block thetitle %}{% endblock %} - {% if css_file %} - - {% else %} - {% compress css %} - - {% endcompress %} + {% compress css %} + + {% endcompress %} + {% if css_theme %} + {% endif %} {% include "pretixpresale/fragment_js.html" %} diff --git a/src/pretix/presale/templates/pretixpresale/event/seatingplan.html b/src/pretix/presale/templates/pretixpresale/event/seatingplan.html index 73970dc0d4..0716e82f55 100644 --- a/src/pretix/presale/templates/pretixpresale/event/seatingplan.html +++ b/src/pretix/presale/templates/pretixpresale/event/seatingplan.html @@ -7,12 +7,11 @@ - {% if css_file %} - - {% else %} - {% compress css %} - - {% endcompress %} + {% compress css %} + + {% endcompress %} + {% if css_theme %} + {% endif %} {% include "pretixpresale/fragment_js.html" %} diff --git a/src/pretix/presale/urls.py b/src/pretix/presale/urls.py index fcc18b6482..10df32c113 100644 --- a/src/pretix/presale/urls.py +++ b/src/pretix/presale/urls.py @@ -176,6 +176,8 @@ event_patterns = [ re_path(r'^(?P\d+)/widget/product_list$', pretix.presale.views.widget.WidgetAPIProductList.as_view(), name='event.widget.productlist'), + re_path(r'^theme.css$', pretix.presale.views.theme.theme_css, name='event.theme.css'), + re_path(r'timemachine/$', pretix.presale.views.event.EventTimeMachine.as_view(), name='event.timemachine'), # Account management is done on org level, but we at least need a logout @@ -195,6 +197,8 @@ organizer_patterns = [ name='organizer.widget.productlist'), re_path(r'^widget/v1.css$', pretix.presale.views.widget.widget_css, name='organizer.widget.css'), + re_path(r'^theme.css$', pretix.presale.views.theme.theme_css, name='organizer.theme.css'), + re_path(r'^account/login/(?P[0-9]+)/$', pretix.presale.views.customer.SSOLoginView.as_view(), name='organizer.customer.login'), re_path(r'^account/login/(?P[0-9]+)/return$', pretix.presale.views.customer.SSOLoginReturnView.as_view(), name='organizer.customer.login.return'), re_path(r'^account/login$', pretix.presale.views.customer.LoginView.as_view(), name='organizer.customer.login'), diff --git a/src/pretix/presale/views/theme.py b/src/pretix/presale/views/theme.py index 594250da3c..341a1773fd 100644 --- a/src/pretix/presale/views/theme.py +++ b/src/pretix/presale/views/theme.py @@ -19,9 +19,29 @@ # You should have received a copy of the GNU Affero General Public License along with this program. If not, see # . # +import hashlib +import time + +from django.contrib.staticfiles import finders from django.http import HttpResponse from django.templatetags.static import static +from django.utils.http import http_date from django.views.decorators.cache import cache_page +from django.views.decorators.gzip import gzip_page +from django.views.decorators.http import condition + +from pretix.presale.style import get_theme_vars_css + +# we never change static source without restart, so we can cache this thread-wise +_source_cache_key = None + + +def _get_source_cache_key(): + global _source_cache_key + if not _source_cache_key: + with open(finders.find("pretixbase/scss/_theme_variables.scss"), "r") as f: + _source_cache_key = hashlib.sha256(f.read().encode()).hexdigest()[:12] + return _source_cache_key @cache_page(3600) @@ -69,3 +89,16 @@ def webmanifest(request): static('pretixbase/img/icons/android-chrome-512x512.png'), ), content_type='text/json' ) + + +@gzip_page +@condition(etag_func=lambda request, **kwargs: request.GET.get("version")) +def theme_css(request, **kwargs): + obj = getattr(request, "event", request.organizer) + css = get_theme_vars_css(obj, widget=False) + resp = HttpResponse(css, content_type="text/css") + resp._csp_ignore = True + resp["Access-Control-Allow-Origin"] = "*" + if "version" in request.GET: + resp["Expires"] = http_date(time.time() + 3600 * 24 * 30) + return resp diff --git a/src/pretix/presale/views/widget.py b/src/pretix/presale/views/widget.py index 78bb110f7c..cfd3b842e5 100644 --- a/src/pretix/presale/views/widget.py +++ b/src/pretix/presale/views/widget.py @@ -23,6 +23,7 @@ import calendar import hashlib import json import logging +import time from collections import defaultdict from datetime import date, datetime, timedelta from urllib.parse import urljoin @@ -65,6 +66,7 @@ from pretix.helpers.daterange import daterange from pretix.helpers.thumb import get_thumbnail from pretix.multidomain.urlreverse import build_absolute_uri from pretix.presale.forms.organizer import meta_filtersets +from pretix.presale.style import get_theme_vars_css from pretix.presale.views.cart import get_or_create_cart_id from pretix.presale.views.event import ( get_grouped_items, item_group_by_category, @@ -76,14 +78,35 @@ from pretix.presale.views.organizer import ( logger = logging.getLogger(__name__) +# we never change static source without restart, so we can cache this thread-wise +_source_cache_key = None + + +def _get_source_cache_key(): + global _source_cache_key + checksum = hashlib.sha256() + if not _source_cache_key: + with open(finders.find("pretixbase/scss/_theme_variables.scss"), "r") as f: + checksum.update(f.read().encode()) + tpl = get_template('pretixpresale/widget_dummy.html') + et = html.fromstring(tpl.render({})).xpath('/html/head/link')[0].attrib['href'].replace(settings.STATIC_URL, '') + checksum.update(et.encode()) + _source_cache_key = checksum.hexdigest()[:12] + return _source_cache_key + def indent(s): return s.replace('\n', '\n ') def widget_css_etag(request, **kwargs): - o = getattr(request, 'event', request.organizer) - return o.settings.presale_widget_css_checksum or o.settings.presale_widget_css_checksum + # This makes sure a new version of the theme is loaded whenever settings or the source files have changed + if hasattr(request, 'event'): + return (f'{_get_source_cache_key()}-' + f'{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}-' + f'{request.event.cache.get_or_set("css_version", default=lambda: int(time.time()))}') + else: + return f'{_get_source_cache_key()}-{request.organizer.cache.get_or_set("css_version", default=lambda: int(time.time()))}' def widget_js_etag(request, lang, **kwargs): @@ -96,18 +119,16 @@ def widget_js_etag(request, lang, **kwargs): @cache_page(60) def widget_css(request, **kwargs): o = getattr(request, 'event', request.organizer) - if o.settings.presale_widget_css_file: - try: - resp = FileResponse(default_storage.open(o.settings.presale_widget_css_file), - content_type='text/css') - resp['Access-Control-Allow-Origin'] = '*' - return resp - except FileNotFoundError: - pass + tpl = get_template('pretixpresale/widget_dummy.html') et = html.fromstring(tpl.render({})).xpath('/html/head/link')[0].attrib['href'].replace(settings.STATIC_URL, '') - f = finders.find(et) - resp = FileResponse(open(f, 'rb'), content_type='text/css') + with open(finders.find(et), 'r') as f: + widget_css = f.read() + + theme_css = get_theme_vars_css(o, widget=True) + css = theme_css + widget_css + + resp = FileResponse(css, content_type='text/css') resp._csp_ignore = True resp['Access-Control-Allow-Origin'] = '*' return resp diff --git a/src/pretix/static/bootstrap/README.md b/src/pretix/static/bootstrap/README.md index 9b0729f741..095a1d023c 100644 --- a/src/pretix/static/bootstrap/README.md +++ b/src/pretix/static/bootstrap/README.md @@ -1,132 +1 @@ -# [Bootstrap](http://getbootstrap.com) -![Bower version](https://img.shields.io/bower/v/bootstrap.svg?style=flat) -[![npm version](https://img.shields.io/npm/v/bootstrap.svg?style=flat)](https://www.npmjs.com/package/bootstrap) -[![Build Status](https://img.shields.io/travis/twbs/bootstrap/master.svg?style=flat)](https://travis-ci.org/twbs/bootstrap) -[![devDependency Status](https://img.shields.io/david/dev/twbs/bootstrap.svg?style=flat)](https://david-dm.org/twbs/bootstrap#info=devDependencies) -[![Selenium Test Status](https://saucelabs.com/browser-matrix/bootstrap.svg)](https://saucelabs.com/u/bootstrap) - -Bootstrap is a sleek, intuitive, and powerful front-end framework for faster and easier web development, created by [Mark Otto](https://twitter.com/mdo) and [Jacob Thornton](https://twitter.com/fat), and maintained by the [core team](https://github.com/orgs/twbs/people) with the massive support and involvement of the community. - -To get started, check out ! - -## Table of contents - -- [Quick start](#quick-start) -- [Bugs and feature requests](#bugs-and-feature-requests) -- [Documentation](#documentation) -- [Contributing](#contributing) -- [Community](#community) -- [Versioning](#versioning) -- [Creators](#creators) -- [Copyright and license](#copyright-and-license) - -## Quick start - -Four quick start options are available: - -- [Download the latest release](https://github.com/twbs/bootstrap/archive/v3.3.2.zip). -- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`. -- Install with [Bower](http://bower.io): `bower install bootstrap`. -- Install with [npm](https://www.npmjs.org): `npm install bootstrap`. - -Read the [Getting started page](http://getbootstrap.com/getting-started/) for information on the framework contents, templates and examples, and more. - -### What's included - -Within the download you'll find the following directories and files, logically grouping common assets and providing both compiled and minified variations. You'll see something like this: - -``` -bootstrap/ -├── css/ -│ ├── bootstrap.css -│ ├── bootstrap.css.map -│ ├── bootstrap.min.css -│ ├── bootstrap-theme.css -│ ├── bootstrap-theme.css.map -│ └── bootstrap-theme.min.css -├── js/ -│ ├── bootstrap.js -│ └── bootstrap.min.js -└── fonts/ - ├── glyphicons-halflings-regular.eot - ├── glyphicons-halflings-regular.svg - ├── glyphicons-halflings-regular.ttf - ├── glyphicons-halflings-regular.woff - └── glyphicons-halflings-regular.woff2 -``` - -We provide compiled CSS and JS (`bootstrap.*`), as well as compiled and minified CSS and JS (`bootstrap.min.*`). CSS [source maps](https://developers.google.com/chrome-developer-tools/docs/css-preprocessors) (`bootstrap.*.map`) are available for use with certain browsers' developer tools. Fonts from Glyphicons are included, as is the optional Bootstrap theme. - - - -## Bugs and feature requests - -Have a bug or a feature request? Please first read the [issue guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md#using-the-issue-tracker) and search for existing and closed issues. If your problem or idea is not addressed yet, [please open a new issue](https://github.com/twbs/bootstrap/issues/new). - - -## Documentation - -Bootstrap's documentation, included in this repo in the root directory, is built with [Jekyll](http://jekyllrb.com) and publicly hosted on GitHub Pages at . The docs may also be run locally. - -### Running documentation locally - -1. If necessary, [install Jekyll](http://jekyllrb.com/docs/installation) (requires v2.5.x). - - **Windows users:** Read [this unofficial guide](http://jekyll-windows.juthilo.com/) to get Jekyll up and running without problems. -2. Install the Ruby-based syntax highlighter, [Rouge](https://github.com/jneen/rouge), with `gem install rouge`. -3. From the root `/bootstrap` directory, run `jekyll serve` in the command line. -4. Open in your browser, and voilà. - -Learn more about using Jekyll by reading its [documentation](http://jekyllrb.com/docs/home/). - -### Documentation for previous releases - -Documentation for v2.3.2 has been made available for the time being at while folks transition to Bootstrap 3. - -[Previous releases](https://github.com/twbs/bootstrap/releases) and their documentation are also available for download. - - - -## Contributing - -Please read through our [contributing guidelines](https://github.com/twbs/bootstrap/blob/master/CONTRIBUTING.md). Included are directions for opening issues, coding standards, and notes on development. - -Moreover, if your pull request contains JavaScript patches or features, you must include relevant unit tests. All HTML and CSS should conform to the [Code Guide](https://github.com/mdo/code-guide), maintained by [Mark Otto](https://github.com/mdo). - -Editor preferences are available in the [editor config](https://github.com/twbs/bootstrap/blob/master/.editorconfig) for easy use in common text editors. Read more and download plugins at . - - - -## Community - -Keep track of development and community news. - -- Follow [@twbootstrap on Twitter](https://twitter.com/twbootstrap). -- Read and subscribe to [The Official Bootstrap Blog](http://blog.getbootstrap.com). -- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel. -- Implementation help may be found at Stack Overflow (tagged [`twitter-bootstrap-3`](http://stackoverflow.com/questions/tagged/twitter-bootstrap-3)). - - - -## Versioning - -For transparency into our release cycle and in striving to maintain backward compatibility, Bootstrap is maintained under [the Semantic Versioning guidelines](http://semver.org/). Sometimes we screw up, but we'll adhere to those rules whenever possible. - - - -## Creators - -**Mark Otto** - -- -- - -**Jacob Thornton** - -- -- - - - -## Copyright and license - -Code and documentation copyright 2011-2015 Twitter, Inc. Code released under [the MIT license](https://github.com/twbs/bootstrap/blob/master/LICENSE). Docs released under [Creative Commons](https://github.com/twbs/bootstrap/blob/master/docs/LICENSE). +This is a modified version of Bootstrap 3.x. We removed stuff we did not need and migrated stuff to CSS variables instead of SCSS variables. \ No newline at end of file diff --git a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.eot b/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.eot deleted file mode 100644 index b93a4953fff68df523aa7656497ee339d6026d64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20127 zcma%hV{j!vx9y2-`@~L8?1^pLwlPU2wr$&<*tR|KBoo`2;LUg6eW-eW-tKDb)vH%` z^`A!Vd<6hNSRMcX|Cb;E|1qflDggj6Kmr)xA10^t-vIc3*Z+F{r%|K(GyE^?|I{=9 zNq`(c8=wS`0!RZy0g3{M(8^tv41d}oRU?8#IBFtJy*9zAN5dcxqGlMZGL>GG%R#)4J zDJ2;)4*E1pyHia%>lMv3X7Q`UoFyoB@|xvh^)kOE3)IL&0(G&i;g08s>c%~pHkN&6 z($7!kyv|A2DsV2mq-5Ku)D#$Kn$CzqD-wm5Q*OtEOEZe^&T$xIb0NUL}$)W)Ck`6oter6KcQG9Zcy>lXip)%e&!lQgtQ*N`#abOlytt!&i3fo)cKV zP0BWmLxS1gQv(r_r|?9>rR0ZeEJPx;Vi|h1!Eo*dohr&^lJgqJZns>&vexP@fs zkPv93Nyw$-kM5Mw^{@wPU47Y1dSkiHyl3dtHLwV&6Tm1iv{ve;sYA}Z&kmH802s9Z zyJEn+cfl7yFu#1^#DbtP7k&aR06|n{LnYFYEphKd@dJEq@)s#S)UA&8VJY@S2+{~> z(4?M();zvayyd^j`@4>xCqH|Au>Sfzb$mEOcD7e4z8pPVRTiMUWiw;|gXHw7LS#U< zsT(}Z5SJ)CRMXloh$qPnK77w_)ctHmgh}QAe<2S{DU^`!uwptCoq!Owz$u6bF)vnb zL`bM$%>baN7l#)vtS3y6h*2?xCk z>w+s)@`O4(4_I{L-!+b%)NZcQ&ND=2lyP+xI#9OzsiY8$c)ys-MI?TG6 zEP6f=vuLo!G>J7F4v|s#lJ+7A`^nEQScH3e?B_jC&{sj>m zYD?!1z4nDG_Afi$!J(<{>z{~Q)$SaXWjj~%ZvF152Hd^VoG14rFykR=_TO)mCn&K$ z-TfZ!vMBvnToyBoKRkD{3=&=qD|L!vb#jf1f}2338z)e)g>7#NPe!FoaY*jY{f)Bf>ohk-K z4{>fVS}ZCicCqgLuYR_fYx2;*-4k>kffuywghn?15s1dIOOYfl+XLf5w?wtU2Og*f z%X5x`H55F6g1>m~%F`655-W1wFJtY>>qNSdVT`M`1Mlh!5Q6#3j={n5#za;!X&^OJ zgq;d4UJV-F>gg?c3Y?d=kvn3eV)Jb^ zO5vg0G0yN0%}xy#(6oTDSVw8l=_*2k;zTP?+N=*18H5wp`s90K-C67q{W3d8vQGmr zhpW^>1HEQV2TG#8_P_0q91h8QgHT~8=-Ij5snJ3cj?Jn5_66uV=*pq(j}yHnf$Ft;5VVC?bz%9X31asJeQF2jEa47H#j` zk&uxf3t?g!tltVP|B#G_UfDD}`<#B#iY^i>oDd-LGF}A@Fno~dR72c&hs6bR z2F}9(i8+PR%R|~FV$;Ke^Q_E_Bc;$)xN4Ti>Lgg4vaip!%M z06oxAF_*)LH57w|gCW3SwoEHwjO{}}U=pKhjKSZ{u!K?1zm1q? zXyA6y@)}_sONiJopF}_}(~}d4FDyp|(@w}Vb;Fl5bZL%{1`}gdw#i{KMjp2@Fb9pg ziO|u7qP{$kxH$qh8%L+)AvwZNgUT6^zsZq-MRyZid{D?t`f|KzSAD~C?WT3d0rO`0 z=qQ6{)&UXXuHY{9g|P7l_nd-%eh}4%VVaK#Nik*tOu9lBM$<%FS@`NwGEbP0&;Xbo zObCq=y%a`jSJmx_uTLa{@2@}^&F4c%z6oe-TN&idjv+8E|$FHOvBqg5hT zMB=7SHq`_-E?5g=()*!V>rIa&LcX(RU}aLm*38U_V$C_g4)7GrW5$GnvTwJZdBmy6 z*X)wi3=R8L=esOhY0a&eH`^fSpUHV8h$J1|o^3fKO|9QzaiKu>yZ9wmRkW?HTkc<*v7i*ylJ#u#j zD1-n&{B`04oG>0Jn{5PKP*4Qsz{~`VVA3578gA+JUkiPc$Iq!^K|}*p_z3(-c&5z@ zKxmdNpp2&wg&%xL3xZNzG-5Xt7jnI@{?c z25=M>-VF|;an2Os$Nn%HgQz7m(ujC}Ii0Oesa(y#8>D+P*_m^X##E|h$M6tJr%#=P zWP*)Px>7z`E~U^2LNCNiy%Z7!!6RI%6fF@#ZY3z`CK91}^J$F!EB0YF1je9hJKU7!S5MnXV{+#K;y zF~s*H%p@vj&-ru7#(F2L+_;IH46X(z{~HTfcThqD%b{>~u@lSc<+f5#xgt9L7$gSK ziDJ6D*R%4&YeUB@yu@4+&70MBNTnjRyqMRd+@&lU#rV%0t3OmouhC`mkN}pL>tXin zY*p)mt=}$EGT2E<4Q>E2`6)gZ`QJhGDNpI}bZL9}m+R>q?l`OzFjW?)Y)P`fUH(_4 zCb?sm1=DD0+Q5v}BW#0n5;Nm(@RTEa3(Y17H2H67La+>ptQHJ@WMy2xRQT$|7l`8c zYHCxYw2o-rI?(fR2-%}pbs$I%w_&LPYE{4bo}vRoAW>3!SY_zH3`ofx3F1PsQ?&iq z*BRG>?<6%z=x#`NhlEq{K~&rU7Kc7Y-90aRnoj~rVoKae)L$3^z*Utppk?I`)CX&& zZ^@Go9fm&fN`b`XY zt0xE5aw4t@qTg_k=!-5LXU+_~DlW?53!afv6W(k@FPPX-`nA!FBMp7b!ODbL1zh58 z*69I}P_-?qSLKj}JW7gP!la}K@M}L>v?rDD!DY-tu+onu9kLoJz20M4urX_xf2dfZ zORd9Zp&28_ff=wdMpXi%IiTTNegC}~RLkdYjA39kWqlA?jO~o1`*B&85Hd%VPkYZT z48MPe62;TOq#c%H(`wX5(Bu>nlh4Fbd*Npasdhh?oRy8a;NB2(eb}6DgwXtx=n}fE zx67rYw=(s0r?EsPjaya}^Qc-_UT5|*@|$Q}*|>V3O~USkIe6a0_>vd~6kHuP8=m}_ zo2IGKbv;yA+TBtlCpnw)8hDn&eq?26gN$Bh;SdxaS04Fsaih_Cfb98s39xbv)=mS0 z6M<@pM2#pe32w*lYSWG>DYqB95XhgAA)*9dOxHr{t)er0Xugoy)!Vz#2C3FaUMzYl zCxy{igFB901*R2*F4>grPF}+G`;Yh zGi@nRjWyG3mR(BVOeBPOF=_&}2IWT%)pqdNAcL{eP`L*^FDv#Rzql5U&Suq_X%JfR_lC!S|y|xd5mQ0{0!G#9hV46S~A` z0B!{yI-4FZEtol5)mNWXcX(`x&Pc*&gh4k{w%0S#EI>rqqlH2xv7mR=9XNCI$V#NG z4wb-@u{PfQP;tTbzK>(DF(~bKp3;L1-A*HS!VB)Ae>Acnvde15Anb`h;I&0)aZBS6 z55ZS7mL5Wp!LCt45^{2_70YiI_Py=X{I3>$Px5Ez0ahLQ+ z9EWUWSyzA|+g-Axp*Lx-M{!ReQO07EG7r4^)K(xbj@%ZU=0tBC5shl)1a!ifM5OkF z0w2xQ-<+r-h1fi7B6waX15|*GGqfva)S)dVcgea`lQ~SQ$KXPR+(3Tn2I2R<0 z9tK`L*pa^+*n%>tZPiqt{_`%v?Bb7CR-!GhMON_Fbs0$#|H}G?rW|{q5fQhvw!FxI zs-5ZK>hAbnCS#ZQVi5K0X3PjL1JRdQO+&)*!oRCqB{wen60P6!7bGiWn@vD|+E@Xq zb!!_WiU^I|@1M}Hz6fN-m04x=>Exm{b@>UCW|c8vC`aNbtA@KCHujh^2RWZC}iYhL^<*Z93chIBJYU&w>$CGZDRcHuIgF&oyesDZ#&mA;?wxx4Cm#c0V$xYG?9OL(Smh}#fFuX(K;otJmvRP{h ze^f-qv;)HKC7geB92_@3a9@MGijS(hNNVd%-rZ;%@F_f7?Fjinbe1( zn#jQ*jKZTqE+AUTEd3y6t>*=;AO##cmdwU4gc2&rT8l`rtKW2JF<`_M#p>cj+)yCG zgKF)y8jrfxTjGO&ccm8RU>qn|HxQ7Z#sUo$q)P5H%8iBF$({0Ya51-rA@!It#NHN8MxqK zrYyl_&=}WVfQ?+ykV4*@F6)=u_~3BebR2G2>>mKaEBPmSW3(qYGGXj??m3L zHec{@jWCsSD8`xUy0pqT?Sw0oD?AUK*WxZn#D>-$`eI+IT)6ki>ic}W)t$V32^ITD zR497@LO}S|re%A+#vdv-?fXsQGVnP?QB_d0cGE+U84Q=aM=XrOwGFN3`Lpl@P0fL$ zKN1PqOwojH*($uaQFh8_)H#>Acl&UBSZ>!2W1Dinei`R4dJGX$;~60X=|SG6#jci} z&t4*dVDR*;+6Y(G{KGj1B2!qjvDYOyPC}%hnPbJ@g(4yBJrViG1#$$X75y+Ul1{%x zBAuD}Q@w?MFNqF-m39FGpq7RGI?%Bvyyig&oGv)lR>d<`Bqh=p>urib5DE;u$c|$J zwim~nPb19t?LJZsm{<(Iyyt@~H!a4yywmHKW&=1r5+oj*Fx6c89heW@(2R`i!Uiy* zp)=`Vr8sR!)KChE-6SEIyi(dvG3<1KoVt>kGV=zZiG7LGonH1+~yOK-`g0)r#+O|Q>)a`I2FVW%wr3lhO(P{ksNQuR!G_d zeTx(M!%brW_vS9?IF>bzZ2A3mWX-MEaOk^V|4d38{1D|KOlZSjBKrj7Fgf^>JyL0k zLoI$adZJ0T+8i_Idsuj}C;6jgx9LY#Ukh;!8eJ^B1N}q=Gn4onF*a2vY7~`x$r@rJ z`*hi&Z2lazgu{&nz>gjd>#eq*IFlXed(%$s5!HRXKNm zDZld+DwDI`O6hyn2uJ)F^{^;ESf9sjJ)wMSKD~R=DqPBHyP!?cGAvL<1|7K-(=?VO zGcKcF1spUa+ki<`6K#@QxOTsd847N8WSWztG~?~ z!gUJn>z0O=_)VCE|56hkT~n5xXTp}Ucx$Ii%bQ{5;-a4~I2e|{l9ur#*ghd*hSqO= z)GD@ev^w&5%k}YYB~!A%3*XbPPU-N6&3Lp1LxyP@|C<{qcn&?l54+zyMk&I3YDT|E z{lXH-e?C{huu<@~li+73lMOk&k)3s7Asn$t6!PtXJV!RkA`qdo4|OC_a?vR!kE_}k zK5R9KB%V@R7gt@9=TGL{=#r2gl!@3G;k-6sXp&E4u20DgvbY$iE**Xqj3TyxK>3AU z!b9}NXuINqt>Htt6fXIy5mj7oZ{A&$XJ&thR5ySE{mkxq_YooME#VCHm2+3D!f`{) zvR^WSjy_h4v^|!RJV-RaIT2Ctv=)UMMn@fAgjQV$2G+4?&dGA8vK35c-8r)z9Qqa=%k(FU)?iec14<^olkOU3p zF-6`zHiDKPafKK^USUU+D01>C&Wh{{q?>5m zGQp|z*+#>IIo=|ae8CtrN@@t~uLFOeT{}vX(IY*;>wAU=u1Qo4c+a&R);$^VCr>;! zv4L{`lHgc9$BeM)pQ#XA_(Q#=_iSZL4>L~8Hx}NmOC$&*Q*bq|9Aq}rWgFnMDl~d*;7c44GipcpH9PWaBy-G$*MI^F0 z?Tdxir1D<2ui+Q#^c4?uKvq=p>)lq56=Eb|N^qz~w7rsZu)@E4$;~snz+wIxi+980O6M#RmtgLYh@|2}9BiHSpTs zacjGKvwkUwR3lwTSsCHlwb&*(onU;)$yvdhikonn|B44JMgs*&Lo!jn`6AE>XvBiO z*LKNX3FVz9yLcsnmL!cRVO_qv=yIM#X|u&}#f%_?Tj0>8)8P_0r0!AjWNw;S44tst zv+NXY1{zRLf9OYMr6H-z?4CF$Y%MdbpFIN@a-LEnmkcOF>h16cH_;A|e)pJTuCJ4O zY7!4FxT4>4aFT8a92}84>q0&?46h>&0Vv0p>u~k&qd5$C1A6Q$I4V(5X~6{15;PD@ ze6!s9xh#^QI`J+%8*=^(-!P!@9%~buBmN2VSAp@TOo6}C?az+ALP8~&a0FWZk*F5N z^8P8IREnN`N0i@>O0?{i-FoFShYbUB`D7O4HB`Im2{yzXmyrg$k>cY6A@>bf7i3n0 z5y&cf2#`zctT>dz+hNF&+d3g;2)U!#vsb-%LC+pqKRTiiSn#FH#e!bVwR1nAf*TG^ z!RKcCy$P>?Sfq6n<%M{T0I8?p@HlgwC!HoWO>~mT+X<{Ylm+$Vtj9};H3$EB}P2wR$3y!TO#$iY8eO-!}+F&jMu4%E6S>m zB(N4w9O@2=<`WNJay5PwP8javDp~o~xkSbd4t4t8)9jqu@bHmJHq=MV~Pt|(TghCA}fhMS?s-{klV>~=VrT$nsp7mf{?cze~KKOD4 z_1Y!F)*7^W+BBTt1R2h4f1X4Oy2%?=IMhZU8c{qk3xI1=!na*Sg<=A$?K=Y=GUR9@ zQ(ylIm4Lgm>pt#%p`zHxok%vx_=8Fap1|?OM02|N%X-g5_#S~sT@A!x&8k#wVI2lo z1Uyj{tDQRpb*>c}mjU^gYA9{7mNhFAlM=wZkXcA#MHXWMEs^3>p9X)Oa?dx7b%N*y zLz@K^%1JaArjgri;8ptNHwz1<0y8tcURSbHsm=26^@CYJ3hwMaEvC7 z3Wi-@AaXIQ)%F6#i@%M>?Mw7$6(kW@?et@wbk-APcvMCC{>iew#vkZej8%9h0JSc? zCb~K|!9cBU+))^q*co(E^9jRl7gR4Jihyqa(Z(P&ID#TPyysVNL7(^;?Gan!OU>au zN}miBc&XX-M$mSv%3xs)bh>Jq9#aD_l|zO?I+p4_5qI0Ms*OZyyxA`sXcyiy>-{YN zA70%HmibZYcHW&YOHk6S&PQ+$rJ3(utuUra3V0~@=_~QZy&nc~)AS>v&<6$gErZC3 zcbC=eVkV4Vu0#}E*r=&{X)Kgq|8MGCh(wsH4geLj@#8EGYa})K2;n z{1~=ghoz=9TSCxgzr5x3@sQZZ0FZ+t{?klSI_IZa16pSx6*;=O%n!uXVZ@1IL;JEV zfOS&yyfE9dtS*^jmgt6>jQDOIJM5Gx#Y2eAcC3l^lmoJ{o0T>IHpECTbfYgPI4#LZq0PKqnPCD}_ zyKxz;(`fE0z~nA1s?d{X2!#ZP8wUHzFSOoTWQrk%;wCnBV_3D%3@EC|u$Ao)tO|AO z$4&aa!wbf}rbNcP{6=ajgg(`p5kTeu$ji20`zw)X1SH*x zN?T36{d9TY*S896Ijc^!35LLUByY4QO=ARCQ#MMCjudFc7s!z%P$6DESz%zZ#>H|i zw3Mc@v4~{Eke;FWs`5i@ifeYPh-Sb#vCa#qJPL|&quSKF%sp8*n#t?vIE7kFWjNFh zJC@u^bRQ^?ra|%39Ux^Dn4I}QICyDKF0mpe+Bk}!lFlqS^WpYm&xwIYxUoS-rJ)N9 z1Tz*6Rl9;x`4lwS1cgW^H_M*)Dt*DX*W?ArBf?-t|1~ge&S}xM0K;U9Ibf{okZHf~ z#4v4qc6s6Zgm8iKch5VMbQc~_V-ZviirnKCi*ouN^c_2lo&-M;YSA>W>>^5tlXObg zacX$k0=9Tf$Eg+#9k6yV(R5-&F{=DHP8!yvSQ`Y~XRnUx@{O$-bGCksk~3&qH^dqX zkf+ZZ?Nv5u>LBM@2?k%k&_aUb5Xjqf#!&7%zN#VZwmv65ezo^Y4S#(ed0yUn4tFOB zh1f1SJ6_s?a{)u6VdwUC!Hv=8`%T9(^c`2hc9nt$(q{Dm2X)dK49ba+KEheQ;7^0) ziFKw$%EHy_B1)M>=yK^=Z$U-LT36yX>EKT zvD8IAom2&2?bTmX@_PBR4W|p?6?LQ+&UMzXxqHC5VHzf@Eb1u)kwyfy+NOM8Wa2y@ zNNDL0PE$F;yFyf^jy&RGwDXQwYw6yz>OMWvJt98X@;yr!*RQDBE- zE*l*u=($Zi1}0-Y4lGaK?J$yQjgb+*ljUvNQ!;QYAoCq@>70=sJ{o{^21^?zT@r~hhf&O;Qiq+ ziGQQLG*D@5;LZ%09mwMiE4Q{IPUx-emo*;a6#DrmWr(zY27d@ezre)Z1BGZdo&pXn z+);gOFelKDmnjq#8dL7CTiVH)dHOqWi~uE|NM^QI3EqxE6+_n>IW67~UB#J==QOGF zp_S)c8TJ}uiaEiaER}MyB(grNn=2m&0yztA=!%3xUREyuG_jmadN*D&1nxvjZ6^+2 zORi7iX1iPi$tKasppaR9$a3IUmrrX)m*)fg1>H+$KpqeB*G>AQV((-G{}h=qItj|d zz~{5@{?&Dab6;0c7!!%Se>w($RmlG7Jlv_zV3Ru8b2rugY0MVPOOYGlokI7%nhIy& z-B&wE=lh2dtD!F?noD{z^O1~Tq4MhxvchzuT_oF3-t4YyA*MJ*n&+1X3~6quEN z@m~aEp=b2~mP+}TUP^FmkRS_PDMA{B zaSy(P=$T~R!yc^Ye0*pl5xcpm_JWI;@-di+nruhqZ4gy7cq-)I&s&Bt3BkgT(Zdjf zTvvv0)8xzntEtp4iXm}~cT+pi5k{w{(Z@l2XU9lHr4Vy~3ycA_T?V(QS{qwt?v|}k z_ST!s;C4!jyV5)^6xC#v!o*uS%a-jQ6< z)>o?z7=+zNNtIz1*F_HJ(w@=`E+T|9TqhC(g7kKDc8z~?RbKQ)LRMn7A1p*PcX2YR zUAr{);~c7I#3Ssv<0i-Woj0&Z4a!u|@Xt2J1>N-|ED<3$o2V?OwL4oQ%$@!zLamVz zB)K&Ik^~GOmDAa143{I4?XUk1<3-k{<%?&OID&>Ud%z*Rkt*)mko0RwC2=qFf-^OV z=d@47?tY=A;=2VAh0mF(3x;!#X!%{|vn;U2XW{(nu5b&8kOr)Kop3-5_xnK5oO_3y z!EaIb{r%D{7zwtGgFVri4_!yUIGwR(xEV3YWSI_+E}Gdl>TINWsIrfj+7DE?xp+5^ zlr3pM-Cbse*WGKOd3+*Qen^*uHk)+EpH-{u@i%y}Z!YSid<}~kA*IRSk|nf+I1N=2 zIKi+&ej%Al-M5`cP^XU>9A(m7G>58>o|}j0ZWbMg&x`*$B9j#Rnyo0#=BMLdo%=ks zLa3(2EinQLXQ(3zDe7Bce%Oszu%?8PO648TNst4SMFvj=+{b%)ELyB!0`B?9R6aO{i-63|s@|raSQGL~s)9R#J#duFaTSZ2M{X z1?YuM*a!!|jP^QJ(hAisJuPOM`8Y-Hzl~%d@latwj}t&0{DNNC+zJARnuQfiN`HQ# z?boY_2?*q;Qk)LUB)s8(Lz5elaW56p&fDH*AWAq7Zrbeq1!?FBGYHCnFgRu5y1jwD zc|yBz+UW|X`zDsc{W~8m$sh@VVnZD$lLnKlq@Hg^;ky!}ZuPdKNi2BI70;hrpvaA4+Q_+K)I@|)q1N-H zrycZU`*YUW``Qi^`bDX-j7j^&bO+-Xg$cz2#i##($uyW{Nl&{DK{=lLWV3|=<&si||2)l=8^8_z+Vho-#5LB0EqQ3v5U#*DF7 zxT)1j^`m+lW}p$>WSIG1eZ>L|YR-@Feu!YNWiw*IZYh03mq+2QVtQ}1ezRJM?0PA< z;mK(J5@N8>u@<6Y$QAHWNE};rR|)U_&bv8dsnsza7{=zD1VBcxrALqnOf-qW(zzTn zTAp|pEo#FsQ$~*$j|~Q;$Zy&Liu9OM;VF@#_&*nL!N2hH!Q6l*OeTxq!l>dEc{;Hw zCQni{iN%jHU*C;?M-VUaXxf0FEJ_G=C8)C-wD!DvhY+qQ#FT3}Th8;GgV&AV94F`D ztT6=w_Xm8)*)dBnDkZd~UWL|W=Glu!$hc|1w7_7l!3MAt95oIp4Xp{M%clu&TXehO z+L-1#{mjkpTF@?|w1P98OCky~S%@OR&o75P&ZHvC}Y=(2_{ib(-Al_7aZ^U?s34#H}= zGfFi5%KnFVCKtdO^>Htpb07#BeCXMDO8U}crpe1Gm`>Q=6qB4i=nLoLZ%p$TY=OcP z)r}Et-Ed??u~f09d3Nx3bS@ja!fV(Dfa5lXxRs#;8?Y8G+Qvz+iv7fiRkL3liip}) z&G0u8RdEC9c$$rdU53=MH`p!Jn|DHjhOxHK$tW_pw9wCTf0Eo<){HoN=zG!!Gq4z4 z7PwGh)VNPXW-cE#MtofE`-$9~nmmj}m zlzZscQ2+Jq%gaB9rMgVJkbhup0Ggpb)&L01T=%>n7-?v@I8!Q(p&+!fd+Y^Pu9l+u zek(_$^HYFVRRIFt@0Fp52g5Q#I`tC3li`;UtDLP*rA{-#Yoa5qp{cD)QYhldihWe+ zG~zuaqLY~$-1sjh2lkbXCX;lq+p~!2Z=76cvuQe*Fl>IFwpUBP+d^&E4BGc{m#l%Kuo6#{XGoRyFc%Hqhf|%nYd<;yiC>tyEyk z4I+a`(%%Ie=-*n z-{mg=j&t12)LH3R?@-B1tEb7FLMePI1HK0`Ae@#)KcS%!Qt9p4_fmBl5zhO10n401 zBSfnfJ;?_r{%R)hh}BBNSl=$BiAKbuWrNGQUZ)+0=Mt&5!X*D@yGCSaMNY&@`;^a4 z;v=%D_!K!WXV1!3%4P-M*s%V2b#2jF2bk!)#2GLVuGKd#vNpRMyg`kstw0GQ8@^k^ zuqK5uR<>FeRZ#3{%!|4X!hh7hgirQ@Mwg%%ez8pF!N$xhMNQN((yS(F2-OfduxxKE zxY#7O(VGfNuLv-ImAw5+h@gwn%!ER;*Q+001;W7W^waWT%@(T+5k!c3A-j)a8y11t zx4~rSN0s$M8HEOzkcWW4YbKK9GQez2XJ|Nq?TFy;jmGbg;`m&%U4hIiarKmdTHt#l zL=H;ZHE?fYxKQQXKnC+K!TAU}r086{4m}r()-QaFmU(qWhJlc$eas&y?=H9EYQy8N$8^bni9TpDp zkA^WRs?KgYgjxX4T6?`SMs$`s3vlut(YU~f2F+id(Rf_)$BIMibk9lACI~LA+i7xn z%-+=DHV*0TCTJp~-|$VZ@g2vmd*|2QXV;HeTzt530KyK>v&253N1l}bP_J#UjLy4) zBJili9#-ey8Kj(dxmW^ctorxd;te|xo)%46l%5qE-YhAjP`Cc03vT)vV&GAV%#Cgb zX~2}uWNvh`2<*AuxuJpq>SyNtZwzuU)r@@dqC@v=Ocd(HnnzytN+M&|Qi#f4Q8D=h ziE<3ziFW%+!yy(q{il8H44g^5{_+pH60Mx5Z*FgC_3hKxmeJ+wVuX?T#ZfOOD3E4C zRJsj#wA@3uvwZwHKKGN{{Ag+8^cs?S4N@6(Wkd$CkoCst(Z&hp+l=ffZ?2m%%ffI3 zdV7coR`R+*dPbNx=*ivWeNJK=Iy_vKd`-_Hng{l?hmp=|T3U&epbmgXXWs9ySE|=G zeQ|^ioL}tveN{s72_&h+F+W;G}?;?_s@h5>DX(rp#eaZ!E=NivgLI zWykLKev+}sHH41NCRm7W>K+_qdoJ8x9o5Cf!)|qLtF7Izxk*p|fX8UqEY)_sI_45O zL2u>x=r5xLE%s|d%MO>zU%KV6QKFiEeo12g#bhei4!Hm+`~Fo~4h|BJ)%ENxy9)Up zOxupSf1QZWun=)gF{L0YWJ<(r0?$bPFANrmphJ>kG`&7E+RgrWQi}ZS#-CQJ*i#8j zM_A0?w@4Mq@xvk^>QSvEU|VYQoVI=TaOrsLTa`RZfe8{9F~mM{L+C`9YP9?OknLw| zmkvz>cS6`pF0FYeLdY%>u&XpPj5$*iYkj=m7wMzHqzZ5SG~$i_^f@QEPEC+<2nf-{ zE7W+n%)q$!5@2pBuXMxhUSi*%F>e_g!$T-_`ovjBh(3jK9Q^~OR{)}!0}vdTE^M+m z9QWsA?xG>EW;U~5gEuKR)Ubfi&YWnXV;3H6Zt^NE725*`;lpSK4HS1sN?{~9a4JkD z%}23oAovytUKfRN87XTH2c=kq1)O5(fH_M3M-o{{@&~KD`~TRot-gqg7Q2U2o-iiF}K>m?CokhmODaLB z1p6(6JYGntNOg(s!(>ZU&lzDf+Ur)^Lirm%*}Z>T)9)fAZ9>k(kvnM;ab$ptA=hoh zVgsVaveXbMpm{|4*d<0>?l_JUFOO8A3xNLQOh%nVXjYI6X8h?a@6kDe5-m&;M0xqx z+1U$s>(P9P)f0!{z%M@E7|9nn#IWgEx6A6JNJ(7dk`%6$3@!C!l;JK-p2?gg+W|d- ziEzgk$w7k48NMqg$CM*4O~Abj3+_yUKTyK1p6GDsGEs;}=E_q>^LI-~pym$qhXPJf z2`!PJDp4l(TTm#|n@bN!j;-FFOM__eLl!6{*}z=)UAcGYloj?bv!-XY1TA6Xz;82J zLRaF{8ayzGa|}c--}|^xh)xgX>6R(sZD|Z|qX50gu=d`gEwHqC@WYU7{%<5VOnf9+ zB@FX?|UL%`8EIAe!*UdYl|6wRz6Y>(#8x92$#y}wMeE|ZM2X*c}dKJ^4NIf;Fm zNwzq%QcO?$NR-7`su!*$dlIKo2y(N;qgH@1|8QNo$0wbyyJ2^}$iZ>M{BhBjTdMjK z>gPEzgX4;g3$rU?jvDeOq`X=>)zdt|jk1Lv3u~bjHI=EGLfIR&+K3ldcc4D&Um&04 z3^F*}WaxR(ZyaB>DlmF_UP@+Q*h$&nsOB#gwLt{1#F4i-{A5J@`>B9@{^i?g_Ce&O z<<}_We-RUFU&&MHa1#t56u_oM(Ljn7djja!T|gcxSoR=)@?owC*NkDarpBj=W4}=i1@)@L|C) zQKA+o<(pMVp*Su(`zBC0l1yTa$MRfQ#uby|$mlOMs=G`4J|?apMzKei%jZql#gP@IkOaOjB7MJM=@1j(&!jNnyVkn5;4lvro1!vq ztXiV8HYj5%)r1PPpIOj)f!>pc^3#LvfZ(hz}C@-3R(Cx7R427*Fwd!XO z4~j&IkPHcBm0h_|iG;ZNrYdJ4HI!$rSyo&sibmwIgm1|J#g6%>=ML1r!kcEhm(XY& zD@mIJt;!O%WP7CE&wwE3?1-dt;RTHdm~LvP7K`ccWXkZ0kfFa2S;wGtx_a}S2lslw z$<4^Jg-n#Ypc(3t2N67Juasu=h)j&UNTPNDil4MQMTlnI81kY46uMH5B^U{~nmc6+ z9>(lGhhvRK9ITfpAD!XQ&BPphL3p8B4PVBN0NF6U49;ZA0Tr75AgGw7(S=Yio+xg_ zepZ*?V#KD;sHH+15ix&yCs0eSB-Z%D%uujlXvT#V$Rz@$+w!u#3GIo*AwMI#Bm^oO zLr1e}k5W~G0xaO!C%Mb{sarxWZ4%Dn9vG`KHmPC9GWZwOOm11XJp#o0-P-${3m4g( z6~)X9FXw%Xm~&99tj>a-ri})ZcnsfJtc10F@t9xF5vq6E)X!iUXHq-ohlO`gQdS&k zZl})3k||u)!_=nNlvMbz%AuIr89l#I$;rG}qvDGiK?xTd5HzMQkw*p$YvFLGyQM!J zNC^gD!kP{A84nGosi~@MLKqWQNacfs7O$dkZtm4-BZ~iA8xWZPkTK!HpA5zr!9Z&+icfAJ1)NWkTd!-9`NWU>9uXXUr;`Js#NbKFgrNhTcY4GNv*71}}T zFJh?>=EcbUd2<|fiL+H=wMw8hbX6?+_cl4XnCB#ddwdG>bki* zt*&6Dy&EIPluL@A3_;R%)shA-tDQA1!Tw4ffBRyy;2n)vm_JV06(4Or&QAOKNZB5f(MVC}&_!B>098R{Simr!UG}?CW1Ah+X+0#~0`X)od zLYablwmFxN21L))!_zc`IfzWi`5>MxPe(DmjjO1}HHt7TJtAW+VXHt!aKZk>y6PoMsbDXRJnov;D~Ur~2R_7(Xr)aa%wJwZhS3gr7IGgt%@;`jpL@gyc6bGCVx!9CE7NgIbUNZ!Ur1RHror0~ zr(j$^yM4j`#c2KxSP61;(Tk^pe7b~}LWj~SZC=MEpdKf;B@on9=?_n|R|0q;Y*1_@ z>nGq>)&q!;u-8H)WCwtL&7F4vbnnfSAlK1mwnRq2&gZrEr!b1MA z(3%vAbh3aU-IX`d7b@q`-WiT6eitu}ZH9x#d&qx}?CtDuAXak%5<-P!{a`V=$|XmJ zUn@4lX6#ulB@a=&-9HG)a>KkH=jE7>&S&N~0X0zD=Q=t|7w;kuh#cU=NN7gBGbQTT z;?bdSt8V&IIi}sDTzA0dkU}Z-Qvg;RDe8v>468p3*&hbGT1I3hi9hh~Z(!H}{+>eUyF)H&gdrX=k$aB%J6I;6+^^kn1mL+E+?A!A}@xV(Qa@M%HD5C@+-4Mb4lI=Xp=@9+^x+jhtOc zYgF2aVa(uSR*n(O)e6tf3JEg2xs#dJfhEmi1iOmDYWk|wXNHU?g23^IGKB&yHnsm7 zm_+;p?YpA#N*7vXCkeN2LTNG`{QDa#U3fcFz7SB)83=<8rF)|udrEbrZL$o6W?oDR zQx!178Ih9B#D9Ko$H(jD{4MME&<|6%MPu|TfOc#E0B}!j^MMpV69D#h2`vsEQ{(?c zJ3Lh!3&=yS5fWL~;1wCZ?)%nmK`Eqgcu)O6rD^3%ijcxL50^z?OI(LaVDvfL0#zjZ z2?cPvC$QCzpxpt5jMFp05OxhK0F!Q`rPhDi5)y=-0C} zIM~ku&S@pl1&0=jl+rlS<4`riV~LC-#pqNde@44MB(j%)On$0Ko(@q?4`1?4149Z_ zZi!5aU@2vM$dHR6WSZpj+VboK+>u-CbNi7*lw4K^ZxxM#24_Yc`jvb9NPVi75L+MlM^U~`;a7`4H0L|TYK>%hfEfXLsu1JGM zbh|8{wuc7ucV+`Ys1kqxsj`dajwyM;^X^`)#<+a~$WFy8b2t_RS{8yNYKKlnv+>vB zX(QTf$kqrJ;%I@EwEs{cIcH@Z3|#^S@M+5jsP<^`@8^I4_8MlBb`~cE^n+{{;qW2q z=p1=&+fUo%T{GhVX@;56kH8K_%?X=;$OTYqW1L*)hzelm^$*?_K;9JyIWhsn4SK(| zSmXLTUE8VQX{se#8#Rj*lz`xHtT<61V~fb;WZUpu(M)f#;I+2_zR+)y5Jv?l`CxAinx|EY!`IJ*x9_gf_k&Gx2alL!hK zUWj1T_pk|?iv}4EP#PZvYD_-LpzU!NfcLL%fK&r$W8O1KH9c2&GV~N#T$kaXGvAOl)|T zuF9%6(i=Y3q?X%VK-D2YIYFPH3f|g$TrXW->&^Ab`WT z7>Oo!u1u40?jAJ8Hy`bv}qbgs8)cF0&qeVjD?e+3Ggn1Im>K77ZSpbU*08 zfZkIFcv?y)!*B{|>nx@cE{KoutP+seQU?bCGE`tS0GKUO3PN~t=2u7q_6$l;uw^4c zVu^f{uaqsZ{*a-N?2B8ngrLS8E&s6}Xtv9rR9C^b`@q8*iH)pFzf1|kCfiLw6u{Z%aC z!X^5CzF6qofFJgklJV3oc|Qc2XdFl+y5M9*P8}A>Kh{ zWRgRwMSZ(?Jw;m%0etU5BsWT-Dj-5F;Q$OQJrQd+lv`i6>MhVo^p*^w6{~=fhe|bN z*37oV0kji)4an^%3ABbg5RC;CS50@PV5_hKfXjYx+(DqQdKC^JIEMo6X66$qDdLRc z!YJPSKnbY`#Ht6`g@xGzJmKzzn|abYbP+_Q(v?~~ z96%cd{E0BCsH^0HaWt{y(Cuto4VE7jhB1Z??#UaU(*R&Eo+J`UN+8mcb51F|I|n*J zJCZ3R*OdyeS9hWkc_mA7-br>3Tw=CX2bl(=TpVt#WP8Bg^vE_9bP&6ccAf3lFMgr` z{3=h@?Ftb$RTe&@IQtiJfV;O&4fzh)e1>7seG; z=%mA4@c7{aXeJnhEg2J@Bm;=)j=O=cl#^NNkQ<{r;Bm|8Hg}bJ-S^g4`|itx)~!LN zXtL}?f1Hs6UQ+f0-X6&TBCW=A4>bU0{rv8C4T!(wD-h>VCK4YJk`6C9$by!fxOYw- zV#n+0{E(0ttq_#16B} ze8$E#X9o{B!0vbq#WUwmv5Xz6{(!^~+}sBW{xctdNHL4^vDk!0E}(g|W_q;jR|ZK< z8w>H-8G{%R#%f!E7cO_^B?yFRKLOH)RT9GJsb+kAKq~}WIF)NRLwKZ^Q;>!2MNa|} z-mh?=B;*&D{Nd-mQRcfVnHkChI=DRHU4ga%xJ%+QkBd|-d9uRI76@BT(bjsjwS+r) zvx=lGNLv1?SzZ;P)Gnn>04fO7Culg*?LmbEF0fATG8S@)oJ>NT3pYAXa*vX!eUTDF ziBrp(QyDqr0ZMTr?4uG_Nqs6f%S0g?h`1vO5fo=5S&u#wI2d4+3hWiolEU!=3_oFo zfie?+4W#`;1dd#X@g9Yj<53S<6OB!TM8w8})7k-$&q5(smc%;r z(BlXkTp`C47+%4JA{2X}MIaPbVF!35P#p;u7+fR*46{T+LR8+j25oduCfDzDv6R-hU{TVVo9fz?^N3ShMt!t0NsH)pB zRK8-S{Dn*y3b|k^*?_B70<2gHt==l7c&cT>r`C#{S}J2;s#d{M)ncW(#Y$C*lByLQ z&?+{dR7*gpdT~(1;M(FfF==3z`^eW)=5a9RqvF-)2?S-(G zhS;p(u~_qBum*q}On@$#08}ynd0+spzyVco0%G6;<-i5&016cV5UKzhQ~)fX03|>L z8ej+HzzgVr6_5ZUpa4HW0Ca!=r1%*}Oo;2no&Zz8DfR)L!@r<5 z2viSZpmvo5XqXyAz{Ms7`7kX>fnr1gi4X~7KpznRT0{Xc5Cfz@43PjBMBoH@z_{~( z(Wd}IPJ9hH+%)Fc)0!hrV+(A;76rhtI|YHbEDeERV~Ya>SQg^IvlazFkSK(KG9&{q zkPIR~EeQaaBmwA<20}mBO?)N$(z1@p)5?%}rM| zGF()~Z&Kx@OIDRI$d0T8;JX@vj3^2%pd_+@l9~a4lntZ;AvUIjqIZbuNTR6@hNJoV zk4F;ut)LN4ARuyn2M6F~eg-e#UH%2P;8uPGFW^vq1vj8mdIayFOZo(tphk8C7hpT~ z1Fv8?b_LNR3QD9J+!v=p%}# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.ttf b/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.ttf deleted file mode 100644 index 1413fc609ab6f21774de0cb7e01360095584f65b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 45404 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 G`u_lkUM?>H diff --git a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 b/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 deleted file mode 100644 index 64539b54c3751a6d9adb44c8e3a45ba5a73b77f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18028 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 diff --git a/src/pretix/static/bootstrap/scss/_bootstrap.scss b/src/pretix/static/bootstrap/scss/_bootstrap.scss index a56d5b8261..d1b0cd2b29 100644 --- a/src/pretix/static/bootstrap/scss/_bootstrap.scss +++ b/src/pretix/static/bootstrap/scss/_bootstrap.scss @@ -11,7 +11,6 @@ // Reset and dependencies @import "bootstrap/normalize"; @import "bootstrap/print"; -// @import "bootstrap/glyphicons"; // Core CSS @import "bootstrap/scaffolding"; diff --git a/src/pretix/static/bootstrap/scss/_bootstrap_reduced.scss b/src/pretix/static/bootstrap/scss/_bootstrap_reduced.scss index 7dd6f4eccd..bfe102cc21 100644 --- a/src/pretix/static/bootstrap/scss/_bootstrap_reduced.scss +++ b/src/pretix/static/bootstrap/scss/_bootstrap_reduced.scss @@ -11,7 +11,6 @@ // Reset and dependencies @import "bootstrap/normalize"; @import "bootstrap/print"; -// @import "bootstrap/glyphicons"; // Core CSS @import "bootstrap/scaffolding"; diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_alerts.scss b/src/pretix/static/bootstrap/scss/bootstrap/_alerts.scss index f9e69bd084..83e296cd03 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_alerts.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_alerts.scss @@ -57,17 +57,17 @@ // Generate contextual modifier classes for colorizing the alert. .alert-success { - @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); + @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text, $alert-success-hr, $alert-success-link); } .alert-info { - @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); + @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text, $alert-info-hr, $alert-info-link); } .alert-warning { - @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text); + @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text, $alert-warning-hr, $alert-warning-link); } .alert-danger { - @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text); + @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text, $alert-danger-hr, $alert-danger-link); } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_buttons.scss b/src/pretix/static/bootstrap/scss/bootstrap/_buttons.scss index 62962d786a..5d826dad26 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_buttons.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_buttons.scss @@ -66,26 +66,26 @@ a.btn { // -------------------------------------------------- .btn-default { - @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); + @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border, darken($btn-default-bg, 10%), darken($btn-default-border, 25%), darken($btn-default-border, 12%)); } .btn-primary { - @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); + @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border, $btn-primary-background-active, $btn-primary-border-active, $btn-primary-border-hover); } // Success appears as green .btn-success { - @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border); + @include button-variant($btn-success-color, $btn-success-bg, $btn-success-border, $btn-success-background-active, $btn-success-border-active, $btn-success-border-hover); } // Info appears as blue-green .btn-info { - @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border); + @include button-variant($btn-info-color, $btn-info-bg, $btn-info-border, $btn-info-background-active, $btn-info-border-active, $btn-info-border-hover); } // Warning appears as orange .btn-warning { - @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border); + @include button-variant($btn-warning-color, $btn-warning-bg, $btn-warning-border, $btn-warning-background-active, $btn-warning-border-active, $btn-warning-border-hover); } // Danger and error appear as red .btn-danger { - @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border); + @include button-variant($btn-danger-color, $btn-danger-bg, $btn-danger-border, $btn-danger-background-active, $btn-danger-border-active, $btn-danger-border-hover); } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_forms.scss b/src/pretix/static/bootstrap/scss/bootstrap/_forms.scss index d2e2bac5cd..407a49a4e4 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_forms.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_forms.scss @@ -414,13 +414,13 @@ textarea.form-control { // Feedback states .has-success { - @include form-control-validation($state-success-text, $state-success-text, $state-success-bg); + @include form-control-validation($state-success-text, $state-success-text, $state-success-bg, $state-success-border-focus, $state-success-shadow-focus); } .has-warning { - @include form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg); + @include form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg, $state-warning-border-focus, $state-warning-shadow-focus); } .has-error { - @include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg); + @include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg, $state-danger-border-focus, $state-danger-shadow-focus); } // Reposition feedback icon if input has visible label above diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_glyphicons.scss b/src/pretix/static/bootstrap/scss/bootstrap/_glyphicons.scss deleted file mode 100644 index bd5966dd26..0000000000 --- a/src/pretix/static/bootstrap/scss/bootstrap/_glyphicons.scss +++ /dev/null @@ -1,307 +0,0 @@ -// -// Glyphicons for Bootstrap -// -// Since icons are fonts, they can be placed anywhere text is placed and are -// thus automatically sized to match the surrounding child. To use, create an -// inline element with the appropriate classes, like so: -// -// Star - -@at-root { - // Import the fonts - @font-face { - font-family: "Glyphicons Halflings"; - src: url(if($bootstrap-sass-asset-helper, twbs-font-path("#{$icon-font-path}#{$icon-font-name}.eot"), "#{$icon-font-path}#{$icon-font-name}.eot")); - src: url(if($bootstrap-sass-asset-helper, twbs-font-path("#{$icon-font-path}#{$icon-font-name}.eot?#iefix"), "#{$icon-font-path}#{$icon-font-name}.eot?#iefix")) format("embedded-opentype"), - url(if($bootstrap-sass-asset-helper, twbs-font-path("#{$icon-font-path}#{$icon-font-name}.woff2"), "#{$icon-font-path}#{$icon-font-name}.woff2")) format("woff2"), - url(if($bootstrap-sass-asset-helper, twbs-font-path("#{$icon-font-path}#{$icon-font-name}.woff"), "#{$icon-font-path}#{$icon-font-name}.woff")) format("woff"), - url(if($bootstrap-sass-asset-helper, twbs-font-path("#{$icon-font-path}#{$icon-font-name}.ttf"), "#{$icon-font-path}#{$icon-font-name}.ttf")) format("truetype"), - url(if($bootstrap-sass-asset-helper, twbs-font-path("#{$icon-font-path}#{$icon-font-name}.svg##{$icon-font-svg-id}"), "#{$icon-font-path}#{$icon-font-name}.svg##{$icon-font-svg-id}")) format("svg"); - } -} - -// Catchall baseclass -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: "Glyphicons Halflings"; - font-style: normal; - font-weight: 400; - line-height: 1; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -// Individual icons -.glyphicon-asterisk { &:before { content: "\002a"; } } -.glyphicon-plus { &:before { content: "\002b"; } } -.glyphicon-euro, -.glyphicon-eur { &:before { content: "\20ac"; } } -.glyphicon-minus { &:before { content: "\2212"; } } -.glyphicon-cloud { &:before { content: "\2601"; } } -.glyphicon-envelope { &:before { content: "\2709"; } } -.glyphicon-pencil { &:before { content: "\270f"; } } -.glyphicon-glass { &:before { content: "\e001"; } } -.glyphicon-music { &:before { content: "\e002"; } } -.glyphicon-search { &:before { content: "\e003"; } } -.glyphicon-heart { &:before { content: "\e005"; } } -.glyphicon-star { &:before { content: "\e006"; } } -.glyphicon-star-empty { &:before { content: "\e007"; } } -.glyphicon-user { &:before { content: "\e008"; } } -.glyphicon-film { &:before { content: "\e009"; } } -.glyphicon-th-large { &:before { content: "\e010"; } } -.glyphicon-th { &:before { content: "\e011"; } } -.glyphicon-th-list { &:before { content: "\e012"; } } -.glyphicon-ok { &:before { content: "\e013"; } } -.glyphicon-remove { &:before { content: "\e014"; } } -.glyphicon-zoom-in { &:before { content: "\e015"; } } -.glyphicon-zoom-out { &:before { content: "\e016"; } } -.glyphicon-off { &:before { content: "\e017"; } } -.glyphicon-signal { &:before { content: "\e018"; } } -.glyphicon-cog { &:before { content: "\e019"; } } -.glyphicon-trash { &:before { content: "\e020"; } } -.glyphicon-home { &:before { content: "\e021"; } } -.glyphicon-file { &:before { content: "\e022"; } } -.glyphicon-time { &:before { content: "\e023"; } } -.glyphicon-road { &:before { content: "\e024"; } } -.glyphicon-download-alt { &:before { content: "\e025"; } } -.glyphicon-download { &:before { content: "\e026"; } } -.glyphicon-upload { &:before { content: "\e027"; } } -.glyphicon-inbox { &:before { content: "\e028"; } } -.glyphicon-play-circle { &:before { content: "\e029"; } } -.glyphicon-repeat { &:before { content: "\e030"; } } -.glyphicon-refresh { &:before { content: "\e031"; } } -.glyphicon-list-alt { &:before { content: "\e032"; } } -.glyphicon-lock { &:before { content: "\e033"; } } -.glyphicon-flag { &:before { content: "\e034"; } } -.glyphicon-headphones { &:before { content: "\e035"; } } -.glyphicon-volume-off { &:before { content: "\e036"; } } -.glyphicon-volume-down { &:before { content: "\e037"; } } -.glyphicon-volume-up { &:before { content: "\e038"; } } -.glyphicon-qrcode { &:before { content: "\e039"; } } -.glyphicon-barcode { &:before { content: "\e040"; } } -.glyphicon-tag { &:before { content: "\e041"; } } -.glyphicon-tags { &:before { content: "\e042"; } } -.glyphicon-book { &:before { content: "\e043"; } } -.glyphicon-bookmark { &:before { content: "\e044"; } } -.glyphicon-print { &:before { content: "\e045"; } } -.glyphicon-camera { &:before { content: "\e046"; } } -.glyphicon-font { &:before { content: "\e047"; } } -.glyphicon-bold { &:before { content: "\e048"; } } -.glyphicon-italic { &:before { content: "\e049"; } } -.glyphicon-text-height { &:before { content: "\e050"; } } -.glyphicon-text-width { &:before { content: "\e051"; } } -.glyphicon-align-left { &:before { content: "\e052"; } } -.glyphicon-align-center { &:before { content: "\e053"; } } -.glyphicon-align-right { &:before { content: "\e054"; } } -.glyphicon-align-justify { &:before { content: "\e055"; } } -.glyphicon-list { &:before { content: "\e056"; } } -.glyphicon-indent-left { &:before { content: "\e057"; } } -.glyphicon-indent-right { &:before { content: "\e058"; } } -.glyphicon-facetime-video { &:before { content: "\e059"; } } -.glyphicon-picture { &:before { content: "\e060"; } } -.glyphicon-map-marker { &:before { content: "\e062"; } } -.glyphicon-adjust { &:before { content: "\e063"; } } -.glyphicon-tint { &:before { content: "\e064"; } } -.glyphicon-edit { &:before { content: "\e065"; } } -.glyphicon-share { &:before { content: "\e066"; } } -.glyphicon-check { &:before { content: "\e067"; } } -.glyphicon-move { &:before { content: "\e068"; } } -.glyphicon-step-backward { &:before { content: "\e069"; } } -.glyphicon-fast-backward { &:before { content: "\e070"; } } -.glyphicon-backward { &:before { content: "\e071"; } } -.glyphicon-play { &:before { content: "\e072"; } } -.glyphicon-pause { &:before { content: "\e073"; } } -.glyphicon-stop { &:before { content: "\e074"; } } -.glyphicon-forward { &:before { content: "\e075"; } } -.glyphicon-fast-forward { &:before { content: "\e076"; } } -.glyphicon-step-forward { &:before { content: "\e077"; } } -.glyphicon-eject { &:before { content: "\e078"; } } -.glyphicon-chevron-left { &:before { content: "\e079"; } } -.glyphicon-chevron-right { &:before { content: "\e080"; } } -.glyphicon-plus-sign { &:before { content: "\e081"; } } -.glyphicon-minus-sign { &:before { content: "\e082"; } } -.glyphicon-remove-sign { &:before { content: "\e083"; } } -.glyphicon-ok-sign { &:before { content: "\e084"; } } -.glyphicon-question-sign { &:before { content: "\e085"; } } -.glyphicon-info-sign { &:before { content: "\e086"; } } -.glyphicon-screenshot { &:before { content: "\e087"; } } -.glyphicon-remove-circle { &:before { content: "\e088"; } } -.glyphicon-ok-circle { &:before { content: "\e089"; } } -.glyphicon-ban-circle { &:before { content: "\e090"; } } -.glyphicon-arrow-left { &:before { content: "\e091"; } } -.glyphicon-arrow-right { &:before { content: "\e092"; } } -.glyphicon-arrow-up { &:before { content: "\e093"; } } -.glyphicon-arrow-down { &:before { content: "\e094"; } } -.glyphicon-share-alt { &:before { content: "\e095"; } } -.glyphicon-resize-full { &:before { content: "\e096"; } } -.glyphicon-resize-small { &:before { content: "\e097"; } } -.glyphicon-exclamation-sign { &:before { content: "\e101"; } } -.glyphicon-gift { &:before { content: "\e102"; } } -.glyphicon-leaf { &:before { content: "\e103"; } } -.glyphicon-fire { &:before { content: "\e104"; } } -.glyphicon-eye-open { &:before { content: "\e105"; } } -.glyphicon-eye-close { &:before { content: "\e106"; } } -.glyphicon-warning-sign { &:before { content: "\e107"; } } -.glyphicon-plane { &:before { content: "\e108"; } } -.glyphicon-calendar { &:before { content: "\e109"; } } -.glyphicon-random { &:before { content: "\e110"; } } -.glyphicon-comment { &:before { content: "\e111"; } } -.glyphicon-magnet { &:before { content: "\e112"; } } -.glyphicon-chevron-up { &:before { content: "\e113"; } } -.glyphicon-chevron-down { &:before { content: "\e114"; } } -.glyphicon-retweet { &:before { content: "\e115"; } } -.glyphicon-shopping-cart { &:before { content: "\e116"; } } -.glyphicon-folder-close { &:before { content: "\e117"; } } -.glyphicon-folder-open { &:before { content: "\e118"; } } -.glyphicon-resize-vertical { &:before { content: "\e119"; } } -.glyphicon-resize-horizontal { &:before { content: "\e120"; } } -.glyphicon-hdd { &:before { content: "\e121"; } } -.glyphicon-bullhorn { &:before { content: "\e122"; } } -.glyphicon-bell { &:before { content: "\e123"; } } -.glyphicon-certificate { &:before { content: "\e124"; } } -.glyphicon-thumbs-up { &:before { content: "\e125"; } } -.glyphicon-thumbs-down { &:before { content: "\e126"; } } -.glyphicon-hand-right { &:before { content: "\e127"; } } -.glyphicon-hand-left { &:before { content: "\e128"; } } -.glyphicon-hand-up { &:before { content: "\e129"; } } -.glyphicon-hand-down { &:before { content: "\e130"; } } -.glyphicon-circle-arrow-right { &:before { content: "\e131"; } } -.glyphicon-circle-arrow-left { &:before { content: "\e132"; } } -.glyphicon-circle-arrow-up { &:before { content: "\e133"; } } -.glyphicon-circle-arrow-down { &:before { content: "\e134"; } } -.glyphicon-globe { &:before { content: "\e135"; } } -.glyphicon-wrench { &:before { content: "\e136"; } } -.glyphicon-tasks { &:before { content: "\e137"; } } -.glyphicon-filter { &:before { content: "\e138"; } } -.glyphicon-briefcase { &:before { content: "\e139"; } } -.glyphicon-fullscreen { &:before { content: "\e140"; } } -.glyphicon-dashboard { &:before { content: "\e141"; } } -.glyphicon-paperclip { &:before { content: "\e142"; } } -.glyphicon-heart-empty { &:before { content: "\e143"; } } -.glyphicon-link { &:before { content: "\e144"; } } -.glyphicon-phone { &:before { content: "\e145"; } } -.glyphicon-pushpin { &:before { content: "\e146"; } } -.glyphicon-usd { &:before { content: "\e148"; } } -.glyphicon-gbp { &:before { content: "\e149"; } } -.glyphicon-sort { &:before { content: "\e150"; } } -.glyphicon-sort-by-alphabet { &:before { content: "\e151"; } } -.glyphicon-sort-by-alphabet-alt { &:before { content: "\e152"; } } -.glyphicon-sort-by-order { &:before { content: "\e153"; } } -.glyphicon-sort-by-order-alt { &:before { content: "\e154"; } } -.glyphicon-sort-by-attributes { &:before { content: "\e155"; } } -.glyphicon-sort-by-attributes-alt { &:before { content: "\e156"; } } -.glyphicon-unchecked { &:before { content: "\e157"; } } -.glyphicon-expand { &:before { content: "\e158"; } } -.glyphicon-collapse-down { &:before { content: "\e159"; } } -.glyphicon-collapse-up { &:before { content: "\e160"; } } -.glyphicon-log-in { &:before { content: "\e161"; } } -.glyphicon-flash { &:before { content: "\e162"; } } -.glyphicon-log-out { &:before { content: "\e163"; } } -.glyphicon-new-window { &:before { content: "\e164"; } } -.glyphicon-record { &:before { content: "\e165"; } } -.glyphicon-save { &:before { content: "\e166"; } } -.glyphicon-open { &:before { content: "\e167"; } } -.glyphicon-saved { &:before { content: "\e168"; } } -.glyphicon-import { &:before { content: "\e169"; } } -.glyphicon-export { &:before { content: "\e170"; } } -.glyphicon-send { &:before { content: "\e171"; } } -.glyphicon-floppy-disk { &:before { content: "\e172"; } } -.glyphicon-floppy-saved { &:before { content: "\e173"; } } -.glyphicon-floppy-remove { &:before { content: "\e174"; } } -.glyphicon-floppy-save { &:before { content: "\e175"; } } -.glyphicon-floppy-open { &:before { content: "\e176"; } } -.glyphicon-credit-card { &:before { content: "\e177"; } } -.glyphicon-transfer { &:before { content: "\e178"; } } -.glyphicon-cutlery { &:before { content: "\e179"; } } -.glyphicon-header { &:before { content: "\e180"; } } -.glyphicon-compressed { &:before { content: "\e181"; } } -.glyphicon-earphone { &:before { content: "\e182"; } } -.glyphicon-phone-alt { &:before { content: "\e183"; } } -.glyphicon-tower { &:before { content: "\e184"; } } -.glyphicon-stats { &:before { content: "\e185"; } } -.glyphicon-sd-video { &:before { content: "\e186"; } } -.glyphicon-hd-video { &:before { content: "\e187"; } } -.glyphicon-subtitles { &:before { content: "\e188"; } } -.glyphicon-sound-stereo { &:before { content: "\e189"; } } -.glyphicon-sound-dolby { &:before { content: "\e190"; } } -.glyphicon-sound-5-1 { &:before { content: "\e191"; } } -.glyphicon-sound-6-1 { &:before { content: "\e192"; } } -.glyphicon-sound-7-1 { &:before { content: "\e193"; } } -.glyphicon-copyright-mark { &:before { content: "\e194"; } } -.glyphicon-registration-mark { &:before { content: "\e195"; } } -.glyphicon-cloud-download { &:before { content: "\e197"; } } -.glyphicon-cloud-upload { &:before { content: "\e198"; } } -.glyphicon-tree-conifer { &:before { content: "\e199"; } } -.glyphicon-tree-deciduous { &:before { content: "\e200"; } } -.glyphicon-cd { &:before { content: "\e201"; } } -.glyphicon-save-file { &:before { content: "\e202"; } } -.glyphicon-open-file { &:before { content: "\e203"; } } -.glyphicon-level-up { &:before { content: "\e204"; } } -.glyphicon-copy { &:before { content: "\e205"; } } -.glyphicon-paste { &:before { content: "\e206"; } } -// The following 2 Glyphicons are omitted for the time being because -// they currently use Unicode codepoints that are outside the -// Basic Multilingual Plane (BMP). Older buggy versions of WebKit can't handle -// non-BMP codepoints in CSS string escapes, and thus can't display these two icons. -// Notably, the bug affects some older versions of the Android Browser. -// More info: https://github.com/twbs/bootstrap/issues/10106 -// .glyphicon-door { &:before { content: "\1f6aa"; } } -// .glyphicon-key { &:before { content: "\1f511"; } } -.glyphicon-alert { &:before { content: "\e209"; } } -.glyphicon-equalizer { &:before { content: "\e210"; } } -.glyphicon-king { &:before { content: "\e211"; } } -.glyphicon-queen { &:before { content: "\e212"; } } -.glyphicon-pawn { &:before { content: "\e213"; } } -.glyphicon-bishop { &:before { content: "\e214"; } } -.glyphicon-knight { &:before { content: "\e215"; } } -.glyphicon-baby-formula { &:before { content: "\e216"; } } -.glyphicon-tent { &:before { content: "\26fa"; } } -.glyphicon-blackboard { &:before { content: "\e218"; } } -.glyphicon-bed { &:before { content: "\e219"; } } -.glyphicon-apple { &:before { content: "\f8ff"; } } -.glyphicon-erase { &:before { content: "\e221"; } } -.glyphicon-hourglass { &:before { content: "\231b"; } } -.glyphicon-lamp { &:before { content: "\e223"; } } -.glyphicon-duplicate { &:before { content: "\e224"; } } -.glyphicon-piggy-bank { &:before { content: "\e225"; } } -.glyphicon-scissors { &:before { content: "\e226"; } } -.glyphicon-bitcoin { &:before { content: "\e227"; } } -.glyphicon-btc { &:before { content: "\e227"; } } -.glyphicon-xbt { &:before { content: "\e227"; } } -.glyphicon-yen { &:before { content: "\00a5"; } } -.glyphicon-jpy { &:before { content: "\00a5"; } } -.glyphicon-ruble { &:before { content: "\20bd"; } } -.glyphicon-rub { &:before { content: "\20bd"; } } -.glyphicon-scale { &:before { content: "\e230"; } } -.glyphicon-ice-lolly { &:before { content: "\e231"; } } -.glyphicon-ice-lolly-tasted { &:before { content: "\e232"; } } -.glyphicon-education { &:before { content: "\e233"; } } -.glyphicon-option-horizontal { &:before { content: "\e234"; } } -.glyphicon-option-vertical { &:before { content: "\e235"; } } -.glyphicon-menu-hamburger { &:before { content: "\e236"; } } -.glyphicon-modal-window { &:before { content: "\e237"; } } -.glyphicon-oil { &:before { content: "\e238"; } } -.glyphicon-grain { &:before { content: "\e239"; } } -.glyphicon-sunglasses { &:before { content: "\e240"; } } -.glyphicon-text-size { &:before { content: "\e241"; } } -.glyphicon-text-color { &:before { content: "\e242"; } } -.glyphicon-text-background { &:before { content: "\e243"; } } -.glyphicon-object-align-top { &:before { content: "\e244"; } } -.glyphicon-object-align-bottom { &:before { content: "\e245"; } } -.glyphicon-object-align-horizontal{ &:before { content: "\e246"; } } -.glyphicon-object-align-left { &:before { content: "\e247"; } } -.glyphicon-object-align-vertical { &:before { content: "\e248"; } } -.glyphicon-object-align-right { &:before { content: "\e249"; } } -.glyphicon-triangle-right { &:before { content: "\e250"; } } -.glyphicon-triangle-left { &:before { content: "\e251"; } } -.glyphicon-triangle-bottom { &:before { content: "\e252"; } } -.glyphicon-triangle-top { &:before { content: "\e253"; } } -.glyphicon-console { &:before { content: "\e254"; } } -.glyphicon-superscript { &:before { content: "\e255"; } } -.glyphicon-subscript { &:before { content: "\e256"; } } -.glyphicon-menu-left { &:before { content: "\e257"; } } -.glyphicon-menu-right { &:before { content: "\e258"; } } -.glyphicon-menu-down { &:before { content: "\e259"; } } -.glyphicon-menu-up { &:before { content: "\e260"; } } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_labels.scss b/src/pretix/static/bootstrap/scss/bootstrap/_labels.scss index f7f3013923..dfb1010f92 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_labels.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_labels.scss @@ -42,25 +42,25 @@ a.label { // Contextual variations (linked labels get darker on :hover) .label-default { - @include label-variant($label-default-bg); + @include label-variant($label-default-bg, $label-default-bg-hover); } .label-primary { - @include label-variant($label-primary-bg); + @include label-variant($label-primary-bg, $label-primary-bg-hover); } .label-success { - @include label-variant($label-success-bg); + @include label-variant($label-success-bg, $label-success-bg-hover); } .label-info { - @include label-variant($label-info-bg); + @include label-variant($label-info-bg, $label-info-bg-hover); } .label-warning { - @include label-variant($label-warning-bg); + @include label-variant($label-warning-bg, $label-warning-bg-hover); } .label-danger { - @include label-variant($label-danger-bg); + @include label-variant($label-danger-bg, $label-danger-bg-hover); } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_list-group.scss b/src/pretix/static/bootstrap/scss/bootstrap/_list-group.scss index 529f179e8c..6bb01b5994 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_list-group.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_list-group.scss @@ -108,10 +108,10 @@ button.list-group-item { // Add modifier classes to change text and background color on individual items. // Organizationally, this must come after the `:hover` states. -@include list-group-item-variant(success, $state-success-bg, $state-success-text); -@include list-group-item-variant(info, $state-info-bg, $state-info-text); -@include list-group-item-variant(warning, $state-warning-bg, $state-warning-text); -@include list-group-item-variant(danger, $state-danger-bg, $state-danger-text); +@include list-group-item-variant(success, $state-success-bg, $state-success-text, $state-success-bg-hover); +@include list-group-item-variant(info, $state-info-bg, $state-info-text, $state-info-bg-hover); +@include list-group-item-variant(warning, $state-warning-bg, $state-warning-text, $state-warning-bg-hover); +@include list-group-item-variant(danger, $state-danger-bg, $state-danger-text, $state-danger-bg-hover); // Custom content options diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_tables.scss b/src/pretix/static/bootstrap/scss/bootstrap/_tables.scss index 7bff4b0450..23793f335a 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_tables.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_tables.scss @@ -155,11 +155,11 @@ th { // inheritance to nested tables. // Generate the contextual variants -@include table-row-variant('active', $table-bg-active); -@include table-row-variant('success', $state-success-bg); -@include table-row-variant('info', $state-info-bg); -@include table-row-variant('warning', $state-warning-bg); -@include table-row-variant('danger', $state-danger-bg); +@include table-row-variant('active', $table-bg-active, $table-bg-active-hover); +@include table-row-variant('success', $state-success-bg, $state-success-bg-hover); +@include table-row-variant('info', $state-info-bg, $state-info-bg-hover); +@include table-row-variant('warning', $state-warning-bg, $state-warning-bg-hover); +@include table-row-variant('danger', $state-danger-bg, $state-danger-bg-hover); // Responsive tables diff --git a/src/pretix/static/bootstrap/scss/bootstrap/_type.scss b/src/pretix/static/bootstrap/scss/bootstrap/_type.scss index c63cc4c071..bff464ec03 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/_type.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/_type.scss @@ -103,15 +103,15 @@ mark, color: $text-muted; } -@include text-emphasis-variant('.text-primary', $brand-primary); +@include text-emphasis-variant('.text-primary', $brand-primary, $brand-primary-darken-10); -@include text-emphasis-variant('.text-success', $state-success-text); +@include text-emphasis-variant('.text-success', $state-success-text, $state-success-text-hover); -@include text-emphasis-variant('.text-info', $state-info-text); +@include text-emphasis-variant('.text-info', $state-info-text, $state-info-text-hover); -@include text-emphasis-variant('.text-warning', $state-warning-text); +@include text-emphasis-variant('.text-warning', $state-warning-text, $state-warning-text-hover); -@include text-emphasis-variant('.text-danger', $state-danger-text); +@include text-emphasis-variant('.text-danger', $state-danger-text, $state-danger-text-hover); // Contextual backgrounds // For now we'll leave these alongside the text classes until v4 when we can @@ -121,15 +121,15 @@ mark, // automatically. color: #fff; } -@include bg-variant('.bg-primary', $brand-primary); +@include bg-variant('.bg-primary', $brand-primary, $brand-primary-darken-10); -@include bg-variant('.bg-success', $state-success-bg); +@include bg-variant('.bg-success', $state-success-bg, $state-success-bg-hover); -@include bg-variant('.bg-info', $state-info-bg); +@include bg-variant('.bg-info', $state-info-bg, $state-info-bg-hover); -@include bg-variant('.bg-warning', $state-warning-bg); +@include bg-variant('.bg-warning', $state-warning-bg, $state-warning-bg-hover); -@include bg-variant('.bg-danger', $state-danger-bg); +@include bg-variant('.bg-danger', $state-danger-bg, $state-danger-bg-hover); // Page header diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_alerts.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_alerts.scss index b1e6df9d24..d6da09178c 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_alerts.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_alerts.scss @@ -1,15 +1,15 @@ // Alerts -@mixin alert-variant($background, $border, $text-color) { +@mixin alert-variant($background, $border, $text-color, $hr-color, $link-color) { color: $text-color; background-color: $background; border-color: $border; hr { - border-top-color: darken($border, 5%); + border-top-color: $hr-color; } .alert-link { - color: darken($text-color, 10%); + color: $link-color; } } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_background-variant.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_background-variant.scss index 4c7769e13a..cb4392e268 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_background-variant.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_background-variant.scss @@ -1,12 +1,12 @@ // Contextual backgrounds // [converter] $parent hack -@mixin bg-variant($parent, $color) { +@mixin bg-variant($parent, $color, $color-hover) { #{$parent} { background-color: $color; } a#{$parent}:hover, a#{$parent}:focus { - background-color: darken($color, 10%); + background-color: $color-hover; } } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_buttons.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_buttons.scss index 5afa735e8e..56c5fd7a80 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_buttons.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_buttons.scss @@ -3,7 +3,7 @@ // Easily pump out default styles, as well as :hover, :focus, :active, // and disabled options for all buttons -@mixin button-variant($color, $background, $border) { +@mixin button-variant($color, $background, $border, $background-active, $border-active, $border-hover) { color: $color; background-color: $background; border-color: $border; @@ -11,28 +11,29 @@ &:focus, &.focus { color: $color; - background-color: darken($background, 10%); - border-color: darken($border, 25%); + background-color: $background-active; + border-color: $border-active; } &:hover { color: $color; - background-color: darken($background, 10%); - border-color: darken($border, 12%); + background-color: $background-active; + border-color: $border-hover; } &:active, &.active, .open > &.dropdown-toggle { color: $color; - background-color: darken($background, 10%); + background-color: $background-active; background-image: none; - border-color: darken($border, 12%); + border-color: $border-hover; &:hover, &:focus, &.focus { color: $color; - background-color: darken($background, 17%); - border-color: darken($border, 25%); + /* Not an exact translation of the bootstrap originals but close enough */ + background-color: $border-hover; + border-color: $border-hover; } } &.disabled, diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_forms.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_forms.scss index 46578a14b0..f14b502f84 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_forms.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_forms.scss @@ -3,7 +3,7 @@ // Used in forms.less to generate the form validation CSS for warnings, errors, // and successes. -@mixin form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) { +@mixin form-control-validation($text-color, $border-color, $background-color, $border-color-focus, $border-color-focus-shadow) { // Color the label and help text .help-block, .control-label, @@ -22,8 +22,8 @@ border-color: $border-color; @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075)); // Redeclare so transitions work &:focus { - border-color: darken($border-color, 10%); - $shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px lighten($border-color, 20%); + border-color: $border-color-focus; + $shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px $border-color-focus-shadow; @include box-shadow($shadow); } } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_labels.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_labels.scss index eda6dfd29e..8089432a45 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_labels.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_labels.scss @@ -1,12 +1,12 @@ // Labels -@mixin label-variant($color) { +@mixin label-variant($color, $color-hover) { background-color: $color; &[href] { &:hover, &:focus { - background-color: darken($color, 10%); + background-color: $color-hover; } } } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_list-group.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_list-group.scss index c478eeb31e..093dfc602e 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_list-group.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_list-group.scss @@ -1,6 +1,6 @@ // List Groups -@mixin list-group-item-variant($state, $background, $color) { +@mixin list-group-item-variant($state, $background, $color, $background-hover) { .list-group-item-#{$state} { color: $color; background-color: $background; @@ -19,7 +19,7 @@ &:hover, &:focus { color: $color; - background-color: darken($background, 5%); + background-color: $background-hover; } &.active, &.active:hover, diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_table-row.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_table-row.scss index 136795081e..edb4b1cd7f 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_table-row.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_table-row.scss @@ -1,6 +1,6 @@ // Tables -@mixin table-row-variant($state, $background) { +@mixin table-row-variant($state, $background, $background-hover) { // Exact selectors below required to override `.table-striped` and prevent // inheritance to nested tables. .table > thead > tr, @@ -22,7 +22,7 @@ &.#{$state}:hover > td, &:hover > .#{$state}, &.#{$state}:hover > th { - background-color: darken($background, 5%); + background-color: $background-hover; } } } diff --git a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_text-emphasis.scss b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_text-emphasis.scss index 3b446c4152..b0fdbe8fa6 100644 --- a/src/pretix/static/bootstrap/scss/bootstrap/mixins/_text-emphasis.scss +++ b/src/pretix/static/bootstrap/scss/bootstrap/mixins/_text-emphasis.scss @@ -1,12 +1,12 @@ // Typography // [converter] $parent hack -@mixin text-emphasis-variant($parent, $color) { +@mixin text-emphasis-variant($parent, $color, $color-hover) { #{$parent} { color: $color; } a#{$parent}:hover, a#{$parent}:focus { - color: darken($color, 10%); + color: $color-hover; } } diff --git a/src/pretix/static/pretixbase/scss/_bootstrap_vars.scss b/src/pretix/static/pretixbase/scss/_bootstrap_vars.scss new file mode 100644 index 0000000000..d7457775bf --- /dev/null +++ b/src/pretix/static/pretixbase/scss/_bootstrap_vars.scss @@ -0,0 +1,159 @@ +/* Variables setting for bootstrap */ +$gray-darker: lighten(#000, 13.5%); +$gray-dark: lighten(#000, 20%); +$gray: lighten(#000, 33.5%); +$gray-light: lighten(#000, 55%); +$gray-lighter: lighten(#000, 93.5%); +$gray-lightest: lighten(#000, 97.25%); + +$font-family-sans-serif: var(--pretix-font-family-sans-serif); +$text-color: #222222 !default; +$text-muted: #767676 !default; +$input-color-placeholder: lighten(#000, 70%) !default; + +$border-radius-base: var(--pretix-border-radius-base); +$border-radius-large: var(--pretix-border-radius-large); +$border-radius-small: var(--pretix-border-radius-small); + +$navbar-inverse-bg: #3b1c4a !default; +$navbar-inverse-link-color: white; +$navbar-inverse-link-hover-color: $gray-lighter; +$navbar-inverse-brand-hover-color: $gray-lighter; +$navbar-inverse-color: white; + +$brand-primary: var(--pretix-brand-primary); +$brand-success: var(--pretix-brand-success); +$brand-info: var(--pretix-brand-info); +$brand-warning: var(--pretix-brand-warning); +$brand-danger: var(--pretix-brand-danger); +$body-bg: var(--pretix-body-bg); + +$state-success-bg: var(--pretix-brand-success-lighten-48); +$state-success-bg-hover: var(--pretix-brand-success-lighten-38); +$state-success-border: var(--pretix-brand-success); +$state-success-text: var(--pretix-brand-success-darken-10); +$state-success-text-hover: var(--pretix-brand-success-darken-20); +$state-success-border-focus: var(--pretix-brand-success-darken-20); +$state-success-shadow-focus: var(--pretix-brand-success); +$state-success-shadow: var(--pretix-brand-success-lighten-10); + +$state-info-bg: var(--pretix-brand-info-lighten-33); +$state-info-bg-hover: var(--pretix-brand-info-lighten-23); +$state-info-border: var(--pretix-brand-info); +$state-info-text: var(--pretix-brand-info-darken-20); +$state-info-text-hover: var(--pretix-brand-info-darken-30); + +$state-warning-bg: var(--pretix-brand-warning-lighten-41); +$state-warning-bg-hover: var(--pretix-brand-warning-lighten-31); +$state-warning-border: var(--pretix-brand-warning); +$state-warning-text: var(--pretix-brand-warning-darken-25); +$state-warning-text-hover: var(--pretix-brand-warning-darken-35); +$state-warning-border-focus: var(--pretix-brand-warning-darken-35); +$state-warning-shadow-focus: var(--pretix-brand-warning-darken-15); +$state-warning-shadow: var(--pretix-brand-warning-darken-5); + +$state-danger-bg: var(--pretix-brand-danger-lighten-43); +$state-danger-bg-hover: var(--pretix-brand-danger-lighten-33); +$state-danger-border: var(--pretix-brand-danger); +$state-danger-text: var(--pretix-brand-danger-darken-5); +$state-danger-text-hover: var(--pretix-brand-danger-darken-15); +$state-danger-border-focus: var(--pretix-brand-danger-darken-15); +$state-danger-shadow-focus: var(--pretix-brand-danger-lighten-5); +$state-danger-shadow: var(--pretix-brand-danger-lighten-15); + +$panel-success-border: var(--pretix-brand-success-tint-50); +$panel-success-heading-bg: var(--pretix-brand-success-tint-50); +$panel-danger-border: var(--pretix-brand-danger-tint-50); +$panel-danger-heading-bg: var(--pretix-brand-danger-tint-50); +$panel-warning-border: var(--pretix-brand-warning-tint-50); +$panel-warning-heading-bg: var(--pretix-brand-warning-tine-50); +$panel-default-border: #e5e5e5 !default; +$panel-default-heading-bg: #e5e5e5 !default; + +$link-hover-color: var(--pretix-brand-primary-darken-15); + +$btn-default-border: #CCCCCC; + +$btn-primary-border: var(--pretix-brand-primary-darken-5); +$btn-primary-border-active: var(--pretix-brand-primary-darken-30); +$btn-primary-border-hover: var(--pretix-brand-primary-darken-17); +$btn-primary-background-active: var(--pretix-brand-primary-darken-10); + +$btn-success-border: var(--pretix-brand-success-darken-5); +$btn-success-border-active: var(--pretix-brand-success-darken-30); +$btn-success-border-hover: var(--pretix-brand-success-darken-17); +$btn-success-background-active: var(--pretix-brand-success-darken-10); + +$btn-info-border: var(--pretix-brand-info-darken-5); +$btn-info-border-active: var(--pretix-brand-info-darken-30); +$btn-info-border-hover: var(--pretix-brand-info-darken-17); +$btn-info-background-active: var(--pretix-brand-info-darken-10); + +$btn-warning-border: var(--pretix-brand-warning-darken-5); +$btn-warning-border-active: var(--pretix-brand-warning-darken-30); +$btn-warning-border-hover: var(--pretix-brand-warning-darken-17); +$btn-warning-background-active: var(--pretix-brand-warning-darken-10); + +$btn-danger-border: var(--pretix-brand-danger-darken-5); +$btn-danger-border-hover: var(--pretix-brand-danger-darken-17); +$btn-danger-border-active: var(--pretix-brand-danger-darken-30); +$btn-danger-background-active: var(--pretix-brand-danger-darken-10); + +$list-group-active-text-color: var(--pretix-brand-primary-lighten-40); + +$brand-primary-darken-10: var(--pretix-brand-primary-darken-10); + +$table-bg: transparent !default; +$table-bg-accent: #f9f9f9 !default; +$table-bg-hover: #f5f5f5 !default; +$table-bg-active: $table-bg-hover !default; +$table-bg-active-hover: darken($table-bg-active, 5%); + +$label-default-bg: $gray-light !default; +$label-default-bg-hover: darken($gray-light, 10%); +$label-primary-bg: var(--pretix-brand-primary); +$label-primary-bg-hover: var(--pretix-brand-primary-darken-10); +$label-success-bg: var(--pretix-brand-success); +$label-success-bg-hover: var(--pretix-brand-success-darken-10); +$label-info-bg: var(--pretix-brand-info); +$label-info-bg-hover: var(--pretix-brand-info-darken-10); +$label-warning-bg: var(--pretix-brand-warning); +$label-warning-bg-hover: var(--pretix-brand-warning-darken-10); +$label-danger-bg: var(--pretix-brand-danger); +$label-danger-bg-hover: var(--pretix-brand-danger-darken-10); + +$alert-success-bg: var(--pretix-brand-success-lighten-48); +$alert-success-text: var(--pretix-brand-success-darken-10); +$alert-success-border: var(--pretix-brand-success); +$alert-success-hr: var(--pretix-brand-success-darken-5); +$alert-success-link: var(--pretix-brand-success-darken-20); + +$alert-info-bg: var(--pretix-brand-info-lighten-33); +$alert-info-text: var(--pretix-brand-info-darken-20); +$alert-info-border: var(--pretix-brand-info); +$alert-info-hr: var(--pretix-brand-info-darken-5); +$alert-info-link: var(--pretix-brand-info-darken-30); + +$alert-warning-bg: var(--pretix-brand-warning-lighten-41); +$alert-warning-text: var(--pretix-brand-warning-darken-25); +$alert-warning-border: var(--pretix-brand-warning); +$alert-warning-hr: var(--pretix-brand-warning-darken-5); +$alert-warning-link: var(--pretix-brand-warning-darken-35); + +$alert-danger-bg: var(--pretix-brand-danger-lighten-43); +$alert-danger-text: var(--pretix-brand-danger-darken-5); +$alert-danger-border: var(--pretix-brand-danger); +$alert-danger-hr: var(--pretix-brand-danger-darken-5); +$alert-danger-link: var(--pretix-brand-danger-darken-15); + +$main-box-bg: #FFFFFF; + +$slider-primary-top: var(--pretix-brand-primary); +$slider-primary-bottom: var(--pretix-brand-primary-darken-5); +$slider-secondary-top: var(--pretix-brand-primary-lighten-28-saturate-20); +$slider-secondary-bottom: var(--pretix-brand-primary-lighten-23-saturate-2); + +/* The following vars default to $body-bg in bootstrap, which we don't want */ +$nav-tabs-active-link-hover-bg: #FFFFFF; +$nav-tabs-justified-active-link-border-color: #FFFFFF; +$thumbnail-bg: #FFFFFF; \ No newline at end of file diff --git a/src/pretix/static/pretixbase/scss/_contrast.scss b/src/pretix/static/pretixbase/scss/_contrast.scss index ae5e870198..55f7976c2b 100644 --- a/src/pretix/static/pretixbase/scss/_contrast.scss +++ b/src/pretix/static/pretixbase/scss/_contrast.scss @@ -289,10 +289,8 @@ $linear-channel-values: .9911020971138298 1; -/** - * Calculate the luminance for a color. - * See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests - */ +// Calculate the luminance for a color. +// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests @function luminance($color) { $red: nth($linear-channel-values, red($color) + 1); $green: nth($linear-channel-values, green($color) + 1); @@ -301,10 +299,8 @@ $linear-channel-values: @return .2126 * $red + .7152 * $green + .0722 * $blue; } -/** - * Calculate the contrast ratio between two colors. - * See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests - */ +// Calculate the contrast ratio between two colors. +// See https://www.w3.org/TR/WCAG20-TECHS/G17.html#G17-tests @function contrast($back, $front) { $backLum: luminance($back) + .05; $foreLum: luminance($front) + .05; @@ -312,10 +308,8 @@ $linear-channel-values: @return max($backLum, $foreLum) / min($backLum, $foreLum); } -/** - * Determine whether to use dark or light text on top of given color. - * Returns black for dark text and white for light text. - */ +// Determine whether to use dark or light text on top of given color. +// Returns black for dark text and white for light text. @function choose-contrast-color($color, $l, $d) { $lightContrast: contrast($color, $l); $darkContrast: contrast($color, $d); diff --git a/src/pretix/static/pretixbase/scss/_theme.scss b/src/pretix/static/pretixbase/scss/_theme.scss index 193e9ccf8f..1846eea54f 100644 --- a/src/pretix/static/pretixbase/scss/_theme.scss +++ b/src/pretix/static/pretixbase/scss/_theme.scss @@ -57,56 +57,56 @@ input[type=number]::-webkit-outer-spin-button { } .btn-primary:hover, .btn-primary:focus { - box-shadow: inset 0 1px 3px 0 shade($brand-primary, 25%); + box-shadow: inset 0 1px 3px 0 var(--pretix-brand-primary-shade-25); background: $btn-primary-bg; } .btn-primary:active, .btn-primary:active:hover, .btn-primary:active:focus { - box-shadow: inset 0 1px 8px 0 shade($brand-primary, 25%); + box-shadow: inset 0 1px 8px 0 var(--pretix-brand-primary-shade-25); background: $btn-primary-bg; outline: 0; } .btn-success:hover, .btn-success:focus { - box-shadow: inset 0 1px 3px 0 shade($brand-success, 25%); + box-shadow: inset 0 1px 3px 0 var(--pretix-brand-success-shade-25); background: $btn-success-bg; } .btn-success:active, .btn-success:active:hover, .btn-success:active:focus { - box-shadow: inset 0 1px 8px 0 shade($brand-success, 25%); + box-shadow: inset 0 1px 8px 0 var(--pretix-brand-success-shade-25); background: $btn-success-bg; outline: 0; } .btn-warning:hover, .btn-warning:focus { - box-shadow: inset 0 1px 3px 0 shade($brand-warning, 25%); + box-shadow: inset 0 1px 3px 0 var(--pretix-brand-warning-shade-25); background: $btn-warning-bg; } .btn-warning:active, .btn-warning:active:hover, .btn-warning:active:focus { - box-shadow: inset 0 1px 8px 0 shade($brand-warning, 25%); + box-shadow: inset 0 1px 8px 0 var(--pretix-brand-warning-shade-25); background: $btn-warning-bg; outline: 0; } .btn-danger:hover, .btn-danger:focus { - box-shadow: inset 0 1px 8px 0 shade($brand-danger, 25%); + box-shadow: inset 0 1px 8px 0 var(--pretix-brand-danger-shade-25); background: $btn-danger-bg; } .btn-danger:active, .btn-danger:active:hover, .btn-danger:active:focus { - box-shadow: inset 0 1px 3px 0 shade($brand-danger, 25%); + box-shadow: inset 0 1px 3px 0 var(--pretix-brand-danger-shade-25); background: $btn-danger-bg; outline: 0; } .btn-primary-if-active { - @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); + @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border, darken($btn-default-bg, 10%), darken($btn-default-border, 25%), darken($btn-default-border, 12%)); box-shadow: 0px 0px 0px 1px #cccccc inset; box-sizing: border-box; &.active { - @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); + @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border, $btn-primary-background-active, $btn-primary-border-active, $btn-primary-border-hover); } } @@ -151,7 +151,7 @@ input[type=number]::-webkit-outer-spin-button { text-align: left; } .alert-legal { - @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); + @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text, $alert-info-hr, $alert-info-link); } .alert-success, .alert-danger, .alert-info, .alert-warning, .alert-legal { position: relative; @@ -179,19 +179,19 @@ input[type=number]::-webkit-outer-spin-button { } .alert-success::before { background-color: $state-success-border; - background-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Crect%20x='5'%20y='5'%20width='26'%20height='26'%20fill='%23fff'/%3E%3Cpath%20d='M25.57%2014.65c0-.23-.1-.46-.26-.62l-1.24-1.24a.89.89%200%200%200-1.24%200l-5.98%206-2.68-2.7a.89.89%200%200%200-1.24%200l-1.24%201.24a.88.88%200%200%200%200%201.24l4.54%204.54a.88.88%200%200%200%201.24%200l7.84-7.84c.17-.16.26-.4.26-.62Z'%20fill='#{url-friendly-colour($state-success-text)}'/%3E%3C/svg%3E%0A"); + background-image: var(--pretix-success-icon); } .alert-info::before { background-color: $state-info-border; - background-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Ccircle%20cx='18'%20cy='18'%20r='16'%20fill='%23fff'/%3E%3Cpath%20d='M21.74%2022.66a.7.7%200%200%200-.7-.7h-.7v-6.28a.7.7%200%200%200-.7-.7h-4.19a.7.7%200%200%200-.7.7v1.4c0%20.38.32.7.7.7h.7v4.19h-.7a.7.7%200%200%200-.7.7v1.4c0%20.37.32.7.7.7h5.6a.7.7%200%200%200%20.69-.7v-1.4Zm-1.4-12.57a.7.7%200%200%200-.7-.7h-2.8a.7.7%200%200%200-.69.7v2.1c0%20.38.32.7.7.7h2.8a.7.7%200%200%200%20.7-.7v-2.1Z'%20fill='#{url-friendly-colour($state-info-text)}'/%3E%3C/svg%3E%0A"); + background-image: var(--pretix-info-icon); } .alert-warning::before { background-color: $state-warning-border; - background-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Cpath%20d='M20.04%202.89a2.2%202.2%200%200%200-3.87%200L2.88%2027.24a2.22%202.22%200%200%200%201.94%203.27h26.57a2.22%202.22%200%200%200%201.94-3.27L20.04%202.89Z'%20fill='%23fff'/%3E%3Cpath%20d='M19.92%2025.22c0%20.26-.2.47-.45.47h-2.73a.46.46%200%200%201-.45-.47v-2.7c0-.25.2-.46.45-.46h2.73c.25%200%20.45.21.45.47v2.7Zm-.02-5.31c-.02.19-.24.33-.5.33h-2.62c-.27%200-.48-.14-.48-.33l-.24-6.48c0-.09.04-.23.14-.3.09-.07.21-.16.34-.16h3.12c.13%200%20.26.09.35.16.1.07.14.18.14.27l-.26%206.51Z'%20fill='#{url-friendly-colour($state-warning-text)}'/%3E%3C/svg%3E"); + background-image: var(--pretix-warning-icon); } .alert-legal::before { background-color: $state-info-border; - background-image: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Ccircle%20cx='18'%20cy='18'%20r='16'%20fill='%23fff'/%3E%3Cpath%20d='M19.41%2011.55c0-.43.2-.79.58-1.08-.42-.27-.99-.4-1.7-.4-.62%200-1.1.13-1.46.4-.36.26-.54.62-.54%201.08%200%20.42.13.76.38%201.01.18.15.5.33.95.57l1.77.85c1.04.48%201.79.9%202.25%201.25.51.36.88.8%201.1%201.3h-.02c.18.38.27.76.27%201.14%200%201.1-.62%202.18-1.85%203.22.48.46.81.9%201%201.3a3.84%203.84%200%200%201-.05%203.07c-.22.5-.54.91-.95%201.26-.42.34-.92.61-1.5.8a6.45%206.45%200%200%201-3.91%200%205.44%205.44%200%200%201-1.6-.79%203.67%203.67%200%200%201-1.1-1.22%203.11%203.11%200%200%201-.36-1.5c0-.66.17-1.2.52-1.59.36-.39.83-.59%201.42-.59.46%200%20.87.16%201.22.48.36.32.53.7.53%201.16%200%20.56-.33%201.03-1%201.41.03.05.07.1.13.13l.2.16a3.24%203.24%200%200%200%201.9.53c.67%200%201.2-.15%201.6-.46.38-.3.58-.7.58-1.22%200-.37-.13-.69-.38-.96a4.46%204.46%200%200%200-1.16-.82l-.9-.44A24.66%2024.66%200%200%201%2015%2020.44a7.7%207.7%200%200%201-.9-.6c-.86-.73-1.29-1.54-1.29-2.45%200-1.16.62-2.23%201.87-3.22a4.7%204.7%200%200%201-.79-1.16%203.38%203.38%200%200%201%20.95-3.98A5.02%205.02%200%200%201%2018.14%208c1.3%200%202.37.3%203.21.91V8.9c.92.6%201.37%201.38%201.37%202.33%200%20.51-.16.93-.49%201.27-.33.34-.75.5-1.26.5a1.6%201.6%200%200%201-1.12-.4c-.3-.28-.44-.62-.44-1.04Zm-1.27%204.66a26.2%2026.2%200%200%201-1.73-.84c-.6.6-.89%201.13-.89%201.6%200%20.34.13.63.39.87.17.15.42.31.74.5a30.57%2030.57%200%200%200%201.93%201l.85.45c.58-.58.87-1.14.87-1.68%200-.36-.15-.67-.46-.94-.3-.27-.87-.6-1.7-.96Z'%20fill='#{url-friendly-colour($state-info-text)}'/%3E%3C/svg%3E"); + background-image: var(--pretix-legal-icon); } .alert-danger::before { background-color: $state-danger-border; @@ -248,7 +248,7 @@ svg.svg-icon { .badge-warning { background: $brand-warning; } .badge-info { background: $brand-info; } -@include table-row-variant('success', lighten($brand-success, 40%)); -@include table-row-variant('info', lighten($brand-info, 30%)); -@include table-row-variant('warning', lighten($brand-warning, 40%)); -@include table-row-variant('danger', lighten($brand-danger, 30%)); +@include table-row-variant('success', var(--pretix-brand-success-lighten-40), var(--pretix-brand-success-lighten-35)); +@include table-row-variant('info', var(--pretix-brand-info-success-lighten-30), var(--pretix-brand-info-success-lighten-25)); +@include table-row-variant('warning', var(--pretix-brand-warning-lighten-40), var(--pretix-brand-warning-lighten-35)); +@include table-row-variant('danger', var(--pretix-brand-danger-lighten-30), var(--pretix-brand-danger-lighten-25)); diff --git a/src/pretix/static/pretixbase/scss/_theme_variables.scss b/src/pretix/static/pretixbase/scss/_theme_variables.scss new file mode 100644 index 0000000000..9e86f3148e --- /dev/null +++ b/src/pretix/static/pretixbase/scss/_theme_variables.scss @@ -0,0 +1,134 @@ +@import "_contrast.scss"; +@function tint($color, $percentage) { + @return mix(white, $color, $percentage); +} + +@function shade($color, $percentage) { + @return mix(black, $color, $percentage); +} + +@function url-friendly-colour($colour) { + @return '%23' + str-slice('#{$colour}', 2, -1) +} + +$widget: false !default; + +// Input variables that may be overridden by event themes. +$in-font-family-sans-serif: "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif !default; +$in-brand-primary: #8e44b3 !default; +$in-brand-success: #50a167 !default; +$in-brand-info: #5f9cd4 !default; +$in-brand-warning: #ffb419 !default; +$in-brand-danger: #c44f4f !default; +$in-body-bg: #FFFFFF !default; +$in-border-radius-base: 3px !default; +$in-border-radius-large: 4px !default; +$in-border-radius-small: 2px !default; + +:root { + @if (not $widget) { + --pretix-font-family-sans-serif: #{$in-font-family-sans-serif}; + } + + --pretix-brand-primary: #{$in-brand-primary}; + --pretix-brand-success: #{$in-brand-success}; + --pretix-brand-info: #{$in-brand-info}; + --pretix-brand-warning: #{$in-brand-warning}; + --pretix-brand-danger: #{$in-brand-danger}; + + --pretix-link-contrast-color: #{choose-contrast-color($in-body-bg, white, $in-brand-primary)}; + --pretix-link-hover-contrast-color: #{choose-contrast-color($in-body-bg, white, darken($in-brand-primary, 15%))}; + + --pretix-brand-primary-lighten-5: #{lighten($in-brand-primary, 5%)}; + --pretix-brand-primary-lighten-20: #{lighten($in-brand-primary, 20%)}; + --pretix-brand-primary-lighten-30: #{lighten($in-brand-primary, 30%)}; + --pretix-brand-primary-lighten-40: #{lighten($in-brand-primary, 40%)}; + --pretix-brand-primary-lighten-48: #{lighten($in-brand-primary, 48%)}; + --pretix-brand-primary-lighten-50: #{lighten($in-brand-primary, 50%)}; + --pretix-brand-primary-lighten-53: #{lighten($in-brand-primary, 53%)}; + --pretix-brand-primary-lighten-55: #{lighten($in-brand-primary, 55%)}; + --pretix-brand-primary-darken-5: #{darken($in-brand-primary, 5%)}; + --pretix-brand-primary-darken-10: #{darken($in-brand-primary, 10%)}; + --pretix-brand-primary-darken-15: #{darken($in-brand-primary, 15%)}; + --pretix-brand-primary-darken-17: #{darken($in-brand-primary, 17%)}; + --pretix-brand-primary-darken-20: #{darken($in-brand-primary, 20%)}; + --pretix-brand-primary-darken-30: #{darken($in-brand-primary, 30%)}; + --pretix-brand-primary-shade-25: #{shade($in-brand-primary, 25%)}; + --pretix-brand-primary-lighten-28-saturate-20: #{saturate(lighten($in-brand-primary, 28%), 20%)}; + --pretix-brand-primary-lighten-23-saturate-2: #{saturate(lighten($in-brand-primary, 23%), 2%)}; + + --pretix-brand-success-lighten-10: #{lighten($in-brand-success, 10%)}; + --pretix-brand-success-lighten-20: #{lighten($in-brand-success, 20%)}; + --pretix-brand-success-lighten-30: #{lighten($in-brand-success, 30%)}; + --pretix-brand-success-lighten-35: #{lighten($in-brand-success, 35%)}; + --pretix-brand-success-lighten-38: #{lighten($in-brand-success, 38%)}; + --pretix-brand-success-lighten-40: #{lighten($in-brand-success, 40%)}; + --pretix-brand-success-lighten-48: #{lighten($in-brand-success, 48%)}; + --pretix-brand-success-lighten-50: #{lighten($in-brand-success, 50%)}; + --pretix-brand-success-darken-5: #{darken($in-brand-success, 5%)}; + --pretix-brand-success-darken-10: #{darken($in-brand-success, 10%)}; + --pretix-brand-success-darken-12: #{darken($in-brand-success, 12%)}; + --pretix-brand-success-darken-15: #{darken($in-brand-success, 15%)}; + --pretix-brand-success-darken-17: #{darken($in-brand-success, 17%)}; + --pretix-brand-success-darken-20: #{darken($in-brand-success, 20%)}; + --pretix-brand-success-darken-30: #{darken($in-brand-success, 30%)}; + --pretix-brand-success-tint-50: #{tint($in-brand-success, 50%)}; + --pretix-brand-success-shade-25: #{shade($in-brand-success, 25%)}; + + --pretix-brand-info-lighten-23: #{lighten($in-brand-info, 23%)}; + --pretix-brand-info-lighten-25: #{lighten($in-brand-info, 25%)}; + --pretix-brand-info-lighten-30: #{lighten($in-brand-info, 30%)}; + --pretix-brand-info-lighten-33: #{lighten($in-brand-info, 33%)}; + --pretix-brand-info-darken-5: #{darken($in-brand-info, 5%)}; + --pretix-brand-info-darken-10: #{darken($in-brand-info, 10%)}; + --pretix-brand-info-darken-17: #{darken($in-brand-info, 17%)}; + --pretix-brand-info-darken-20: #{darken($in-brand-info, 20%)}; + --pretix-brand-info-darken-30: #{darken($in-brand-info, 30%)}; + --pretix-brand-info-shade-25: #{shade($in-brand-info, 25%)}; + + --pretix-brand-warning-lighten-12: #{lighten($in-brand-warning, 12%)}; + --pretix-brand-warning-lighten-31: #{lighten($in-brand-warning, 31%)}; + --pretix-brand-warning-lighten-35: #{lighten($in-brand-warning, 35%)}; + --pretix-brand-warning-lighten-40: #{lighten($in-brand-warning, 40%)}; + --pretix-brand-warning-lighten-41: #{lighten($in-brand-warning, 41%)}; + --pretix-brand-warning-lighten-43: #{lighten($in-brand-warning, 43%)}; + --pretix-brand-warning-darken-5: #{darken($in-brand-warning, 5%)}; + --pretix-brand-warning-darken-10: #{darken($in-brand-warning, 10%)}; + --pretix-brand-warning-darken-15: #{darken($in-brand-warning, 15%)}; + --pretix-brand-warning-darken-17: #{darken($in-brand-warning, 17%)}; + --pretix-brand-warning-darken-20: #{darken($in-brand-warning, 20%)}; + --pretix-brand-warning-darken-25: #{darken($in-brand-warning, 25%)}; + --pretix-brand-warning-darken-30: #{darken($in-brand-warning, 30%)}; + --pretix-brand-warning-darken-35: #{darken($in-brand-warning, 35%)}; + --pretix-brand-warning-tint-50: #{tint($in-brand-warning, 50%)}; + --pretix-brand-warning-shade-25: #{shade($in-brand-warning, 25%)}; + --pretix-brand-warning-transparent-60: #{transparentize($in-brand-warning, 0.6)}; + + --pretix-brand-danger-lighten-5: #{lighten($in-brand-danger, 5%)}; + --pretix-brand-danger-lighten-15: #{lighten($in-brand-danger, 15%)}; + --pretix-brand-danger-lighten-25: #{lighten($in-brand-danger, 25%)}; + --pretix-brand-danger-lighten-30: #{lighten($in-brand-danger, 30%)}; + --pretix-brand-danger-lighten-33: #{lighten($in-brand-danger, 33%)}; + --pretix-brand-danger-lighten-43: #{lighten($in-brand-danger, 43%)}; + --pretix-brand-danger-lighten-45: #{lighten($in-brand-danger, 45%)}; + --pretix-brand-danger-darken-5: #{darken($in-brand-danger, 5%)}; + --pretix-brand-danger-darken-10: #{darken($in-brand-danger, 10%)}; + --pretix-brand-danger-darken-15: #{darken($in-brand-danger, 15%)}; + --pretix-brand-danger-darken-17: #{darken($in-brand-danger, 17%)}; + --pretix-brand-danger-darken-20: #{darken($in-brand-danger, 20%)}; + --pretix-brand-danger-darken-30: #{darken($in-brand-danger, 30%)}; + --pretix-brand-danger-tint-50: #{tint($in-brand-danger, 50%)}; + --pretix-brand-danger-shade-25: #{shade($in-brand-danger, 25%)}; + + --pretix-border-radius-base: #{$in-border-radius-base}; + --pretix-border-radius-large: #{$in-border-radius-large}; + --pretix-border-radius-small: #{$in-border-radius-small}; + + @if (not $widget) { + --pretix-body-bg: #{$in-body-bg}; + --pretix-warning-icon: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Cpath%20d='M20.04%202.89a2.2%202.2%200%200%200-3.87%200L2.88%2027.24a2.22%202.22%200%200%200%201.94%203.27h26.57a2.22%202.22%200%200%200%201.94-3.27L20.04%202.89Z'%20fill='%23fff'/%3E%3Cpath%20d='M19.92%2025.22c0%20.26-.2.47-.45.47h-2.73a.46.46%200%200%201-.45-.47v-2.7c0-.25.2-.46.45-.46h2.73c.25%200%20.45.21.45.47v2.7Zm-.02-5.31c-.02.19-.24.33-.5.33h-2.62c-.27%200-.48-.14-.48-.33l-.24-6.48c0-.09.04-.23.14-.3.09-.07.21-.16.34-.16h3.12c.13%200%20.26.09.35.16.1.07.14.18.14.27l-.26%206.51Z'%20fill='#{url-friendly-colour(darken($in-brand-warning, 25%))}'/%3E%3C/svg%3E"); + --pretix-info-icon: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Ccircle%20cx='18'%20cy='18'%20r='16'%20fill='%23fff'/%3E%3Cpath%20d='M21.74%2022.66a.7.7%200%200%200-.7-.7h-.7v-6.28a.7.7%200%200%200-.7-.7h-4.19a.7.7%200%200%200-.7.7v1.4c0%20.38.32.7.7.7h.7v4.19h-.7a.7.7%200%200%200-.7.7v1.4c0%20.37.32.7.7.7h5.6a.7.7%200%200%200%20.69-.7v-1.4Zm-1.4-12.57a.7.7%200%200%200-.7-.7h-2.8a.7.7%200%200%200-.69.7v2.1c0%20.38.32.7.7.7h2.8a.7.7%200%200%200%20.7-.7v-2.1Z'%20fill='#{url-friendly-colour(darken($in-brand-info, 20%))}'/%3E%3C/svg%3E%0A"); + --pretix-success-icon: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Crect%20x='5'%20y='5'%20width='26'%20height='26'%20fill='%23fff'/%3E%3Cpath%20d='M25.57%2014.65c0-.23-.1-.46-.26-.62l-1.24-1.24a.89.89%200%200%200-1.24%200l-5.98%206-2.68-2.7a.89.89%200%200%200-1.24%200l-1.24%201.24a.88.88%200%200%200%200%201.24l4.54%204.54a.88.88%200%200%200%201.24%200l7.84-7.84c.17-.16.26-.4.26-.62Z'%20fill='#{url-friendly-colour(darken($in-brand-success, 10%))}'/%3E%3C/svg%3E%0A"); + --pretix-legal-icon: url("data:image/svg+xml,%3Csvg%20viewBox='0%200%2036%2036'%20xmlns='http://www.w3.org/2000/svg'%20xml:space='preserve'%3E%3Ccircle%20cx='18'%20cy='18'%20r='16'%20fill='%23fff'/%3E%3Cpath%20d='M19.41%2011.55c0-.43.2-.79.58-1.08-.42-.27-.99-.4-1.7-.4-.62%200-1.1.13-1.46.4-.36.26-.54.62-.54%201.08%200%20.42.13.76.38%201.01.18.15.5.33.95.57l1.77.85c1.04.48%201.79.9%202.25%201.25.51.36.88.8%201.1%201.3h-.02c.18.38.27.76.27%201.14%200%201.1-.62%202.18-1.85%203.22.48.46.81.9%201%201.3a3.84%203.84%200%200%201-.05%203.07c-.22.5-.54.91-.95%201.26-.42.34-.92.61-1.5.8a6.45%206.45%200%200%201-3.91%200%205.44%205.44%200%200%201-1.6-.79%203.67%203.67%200%200%201-1.1-1.22%203.11%203.11%200%200%201-.36-1.5c0-.66.17-1.2.52-1.59.36-.39.83-.59%201.42-.59.46%200%20.87.16%201.22.48.36.32.53.7.53%201.16%200%20.56-.33%201.03-1%201.41.03.05.07.1.13.13l.2.16a3.24%203.24%200%200%200%201.9.53c.67%200%201.2-.15%201.6-.46.38-.3.58-.7.58-1.22%200-.37-.13-.69-.38-.96a4.46%204.46%200%200%200-1.16-.82l-.9-.44A24.66%2024.66%200%200%201%2015%2020.44a7.7%207.7%200%200%201-.9-.6c-.86-.73-1.29-1.54-1.29-2.45%200-1.16.62-2.23%201.87-3.22a4.7%204.7%200%200%201-.79-1.16%203.38%203.38%200%200%201%20.95-3.98A5.02%205.02%200%200%201%2018.14%208c1.3%200%202.37.3%203.21.91V8.9c.92.6%201.37%201.38%201.37%202.33%200%20.51-.16.93-.49%201.27-.33.34-.75.5-1.26.5a1.6%201.6%200%200%201-1.12-.4c-.3-.28-.44-.62-.44-1.04Zm-1.27%204.66a26.2%2026.2%200%200%201-1.73-.84c-.6.6-.89%201.13-.89%201.6%200%20.34.13.63.39.87.17.15.42.31.74.5a30.57%2030.57%200%200%200%201.93%201l.85.45c.58-.58.87-1.14.87-1.68%200-.36-.15-.67-.46-.94-.3-.27-.87-.6-1.7-.96Z'%20fill='#{url-friendly-colour(darken($in-brand-info, 20%))}'/%3E%3C/svg%3E"); + } +} \ No newline at end of file diff --git a/src/pretix/static/pretixbase/scss/_variables.scss b/src/pretix/static/pretixbase/scss/_variables.scss deleted file mode 100644 index 2b96fdf5ad..0000000000 --- a/src/pretix/static/pretixbase/scss/_variables.scss +++ /dev/null @@ -1,75 +0,0 @@ -@function tint($color, $percentage) { - @return mix(white, $color, $percentage); -} - -@function shade($color, $percentage) { - @return mix(black, $color, $percentage); -} - -@function url-friendly-colour($colour) { - @return '%23' + str-slice('#{$colour}', 2, -1) -} - -$gray-darker: lighten(#000, 13.5%); -$gray-dark: lighten(#000, 20%); -$gray: lighten(#000, 33.5%); -$gray-light: lighten(#000, 55%); -$gray-lighter: lighten(#000, 93.5%); -$gray-lightest: lighten(#000, 97.25%); - -$font-family-sans-serif: "Open Sans", "OpenSans", "Helvetica Neue", Helvetica, Arial, sans-serif !default; -$text-color: #222222 !default; -$text-muted: #767676 !default; -$input-color-placeholder: lighten(#000, 70%) !default; - -$brand-primary: #7f5a91 !default; -$brand-success: #50a167 !default; -$brand-info: #5f9cd4 !default; -$brand-warning: #ffb419 !default; -$brand-danger: #c44f4f !default; - -$btn-default-border: #CCCCCC; - -$border-radius-base: 3px !default; -$border-radius-large: 4px !default; -$border-radius-small: 2px !default; - -$state-success-text: $brand-success; -$state-info-text: $brand-info; -$state-warning-text: $brand-warning; -$state-danger-text: $brand-danger; - -$navbar-inverse-bg: #3b1c4a !default; -$navbar-inverse-link-color: white; -$navbar-inverse-link-hover-color: $gray-lighter; -$navbar-inverse-brand-hover-color: $gray-lighter; -$navbar-inverse-color: white; - -$state-success-bg: lighten($brand-success, 48%) !default; -$state-success-border: $brand-success; -$state-success-text: darken($brand-success, 10%); -$state-info-bg: lighten($brand-info, 33%) !default; -$state-info-border: $brand-info; -$state-info-text: darken($brand-info, 20%); -$state-warning-bg: lighten($brand-warning, 41%) !default; -$state-warning-border: $brand-warning; -$state-warning-text: darken($brand-warning, 25%); -$state-danger-bg: lighten($brand-danger, 43%) !default; -$state-danger-border: $brand-danger; -$state-danger-text: darken($brand-danger, 5%); -$panel-success-border: tint($brand-success, 50%); -$panel-success-heading-bg: tint($brand-success, 50%); -$panel-danger-border: tint($brand-danger, 50%); -$panel-danger-heading-bg: tint($brand-danger, 50%); -$panel-warning-border: tint($brand-warning, 50%); -$panel-warning-heading-bg: tint($brand-warning, 50%); -$panel-default-border: #e5e5e5 !default; -$panel-default-heading-bg: #e5e5e5 !default; - -$body-bg: #FFFFFF !default; -$main-box-bg: #FFFFFF; - -/* The following vars default to $body-bg in bootstrap, which we don't want */ -$nav-tabs-active-link-hover-bg: #FFFFFF; -$nav-tabs-justified-active-link-border-color: #FFFFFF; -$thumbnail-bg: #FFFFFF; diff --git a/src/pretix/static/pretixbase/scss/cachedfiles.scss b/src/pretix/static/pretixbase/scss/cachedfiles.scss index 7029126dac..5b76e02762 100644 --- a/src/pretix/static/pretixbase/scss/cachedfiles.scss +++ b/src/pretix/static/pretixbase/scss/cachedfiles.scss @@ -1,4 +1,5 @@ -@import "_variables.scss"; +@import "_theme_variables.scss"; +@import "_bootstrap_vars.scss"; @import "../../bootstrap/scss/_bootstrap.scss"; @import "_theme.scss"; @import "../../fontawesome/scss/font-awesome.scss"; diff --git a/src/pretix/static/pretixbase/scss/error.scss b/src/pretix/static/pretixbase/scss/error.scss index 6ad8d516e0..a4af8b15d2 100644 --- a/src/pretix/static/pretixbase/scss/error.scss +++ b/src/pretix/static/pretixbase/scss/error.scss @@ -1,4 +1,5 @@ -@import "_variables.scss"; +@import "_theme_variables.scss"; +@import "_bootstrap_vars.scss"; @import "../../bootstrap/scss/_bootstrap.scss"; @import "_theme.scss"; @import "../../fontawesome/scss/font-awesome.scss"; diff --git a/src/pretix/static/pretixcontrol/scss/_forms.scss b/src/pretix/static/pretixcontrol/scss/_forms.scss index 7d77d3147b..c557d3d522 100644 --- a/src/pretix/static/pretixcontrol/scss/_forms.scss +++ b/src/pretix/static/pretixcontrol/scss/_forms.scss @@ -200,7 +200,7 @@ div.mail-preview { border-radius: 3px; .placeholder { - background: transparentize($brand-warning, 0.6); + background: var(--pretix-brand-warning-transparent-60); } } diff --git a/src/pretix/static/pretixcontrol/scss/_sb-admin-2.scss b/src/pretix/static/pretixcontrol/scss/_sb-admin-2.scss index 9e49403348..103ee8d34f 100644 --- a/src/pretix/static/pretixcontrol/scss/_sb-admin-2.scss +++ b/src/pretix/static/pretixcontrol/scss/_sb-admin-2.scss @@ -393,7 +393,7 @@ table.dataTable thead .sorting:after { a { color: $brand-success; &:hover { - color: darken($brand-success, 15%); + color: var(--pretix-brand-success-darken-15); } } } @@ -408,7 +408,7 @@ table.dataTable thead .sorting:after { a { color: $brand-danger; &:hover { - color: darken($brand-danger, 15%); + color: var(--pretix-brand-danger-darken-15); } } } @@ -423,7 +423,7 @@ table.dataTable thead .sorting:after { a { color: $brand-warning; &:hover { - color: darken($brand-warning, 15%); + color: var(--pretix-brand-warning-darken-15); } } } diff --git a/src/pretix/static/pretixcontrol/scss/auth.scss b/src/pretix/static/pretixcontrol/scss/auth.scss index b44ec408eb..f5a6513720 100644 --- a/src/pretix/static/pretixcontrol/scss/auth.scss +++ b/src/pretix/static/pretixcontrol/scss/auth.scss @@ -1,4 +1,5 @@ -@import "../../pretixbase/scss/_variables.scss"; +@import "../../pretixbase/scss/_theme_variables.scss"; +@import "../../pretixbase/scss/_bootstrap_vars.scss"; @import "../../bootstrap/scss/_bootstrap.scss"; @import "../../fontawesome/scss/font-awesome.scss"; @import "../../pretixbase/scss/_theme.scss"; diff --git a/src/pretix/static/pretixcontrol/scss/main.scss b/src/pretix/static/pretixcontrol/scss/main.scss index cba73446a6..a8404a628a 100644 --- a/src/pretix/static/pretixcontrol/scss/main.scss +++ b/src/pretix/static/pretixcontrol/scss/main.scss @@ -1,4 +1,5 @@ -@import "../../pretixbase/scss/_variables.scss"; +@import "../../pretixbase/scss/_theme_variables.scss"; +@import "../../pretixbase/scss/_bootstrap_vars.scss"; @import "../../bootstrap/scss/_bootstrap.scss"; @import "../../fontawesome/scss/font-awesome.scss"; @import "../../pretixbase/scss/_theme.scss"; @@ -587,7 +588,7 @@ td > p:last-child, text-align: center; } &.availability .progress-bar-success { - background: lighten($brand-success, 20%); + background: var(--pretix-brand-success-lighten-20); } } .quotabox-more { diff --git a/src/pretix/static/pretixpresale/scss/_calendar.scss b/src/pretix/static/pretixpresale/scss/_calendar.scss index b331c8dc35..678cffc2dc 100644 --- a/src/pretix/static/pretixpresale/scss/_calendar.scss +++ b/src/pretix/static/pretixpresale/scss/_calendar.scss @@ -14,11 +14,11 @@ } a.event { display: block; - background: lighten($brand-primary, 48%); + background: var(--pretix-brand-primary-lighten-48); color: $brand-primary; border-radius: $border-radius-base; border-style: solid; - border-color: lighten($brand-primary, 30%); + border-color: var(--pretix-brand-primary-lighten-30); border-width: 1px 1px 1px 12px; border-left-color: inherit; @@ -29,7 +29,7 @@ text-decoration: none; &:hover { - background: lighten($brand-primary, 50%); + background: var(--pretix-brand-primary-lighten-50); border-color: $brand-primary; } &:focus { @@ -48,28 +48,28 @@ } &.soon { - background: lighten($brand-primary, 53%); - border-color: lighten($brand-primary, 40%); - border-left-color: lighten($brand-primary, 20%); - color: lighten($brand-primary, 5%); + background: var(--brand-primary-lighten-53); + border-color: var(--brand-primary-lighten-40); + border-left-color: var(--brand-primary-lighten-20); + color: var(--brand-primary-lighten-5); &:hover { - background: lighten($brand-primary, 55%); - border-color: lighten($brand-primary, 20%); + background: var(--brand-primary-lighten-55); + border-color: var(--brand-primary-lighten-20); } } &.available { - background: lighten($brand-success, 48%); - border-color: lighten($brand-success, 30%); + background: var(--brand-success-lighten-48); + border-color: var(--brand-success-lighten-30); border-left-color: $brand-success; - color: darken($brand-success, 12%); + color: var(--brand-success-darken-12); &.low { - border-left-color: lighten($brand-warning, 12%); + border-left-color: var(--brand-warning-lighten-12); } &:hover { - background: lighten($brand-success, 50%); + background: var(--brand-success-lighten-50); border-color: $brand-success; &.low { @@ -79,26 +79,26 @@ } &.waitinglist { - background: lighten($brand-warning, 41%); - border-color: lighten($brand-warning, 30%); - border-left-color: lighten($brand-warning, 12%); + background: var(--brand-warning-lighten-41); + border-color: var(--brand-warning-lighten-31); + border-left-color: var(--brand-warning-lighten-12); color: #963; &:hover { - background: lighten($brand-warning, 43%); + background: var(--brand-warning-lighten-43); border-color: $brand-warning; } } &.reserved, &.soldout, { - background: lighten($brand-danger, 43%); - border-color: lighten($brand-danger, 30%); - border-left-color: lighten($brand-danger, 30%); - color: darken($brand-danger, 5%); + background: var(--brand-danger-lighten-43); + border-color: var(--brand-danger-lighten-30); + border-left-color: var(--brand-danger-lighten-30); + color: var(--brand-danger-darken-5); &:hover { - background: lighten($brand-danger, 45%); - border-color: lighten($brand-danger, 25%); + background: var(--brand-danger-lighten-45); + border-color: var(--brand-danger-lighten-25); } } @@ -481,7 +481,7 @@ if concurrency is higher than 9, JavaScript (currently in pretixpresale/js/ui/ma color: white; } .table-calendar td.day.has-events:hover { - background: darken($brand-primary, 15%); + background: var(--brand-primary-darken-15); } } #monthselform .row { diff --git a/src/pretix/static/pretixpresale/scss/_event.scss b/src/pretix/static/pretixpresale/scss/_event.scss index e04fc19660..2710a941d5 100644 --- a/src/pretix/static/pretixpresale/scss/_event.scss +++ b/src/pretix/static/pretixpresale/scss/_event.scss @@ -282,7 +282,7 @@ h2.subevent-head { background: $brand-success; } .slider-handle { - @include slider_background-image($brand-success, darken($brand-success, 5%), mix($brand-success, darken($brand-success, 5%))); + @include slider_background-image($brand-success, var(--pretix-brand-success-darken-5), $brand-success); } .slider-tick-container { diff --git a/src/pretix/static/pretixpresale/scss/_forms.scss b/src/pretix/static/pretixpresale/scss/_forms.scss index faf1fad0cb..8e8a32282d 100644 --- a/src/pretix/static/pretixpresale/scss/_forms.scss +++ b/src/pretix/static/pretixpresale/scss/_forms.scss @@ -64,8 +64,8 @@ a.btn, button.btn { border-color: $state-danger-text; @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, .075)); // Redeclare so transitions work &:focus, &.is-focused { - border-color: darken($state-danger-text, 10%); - $shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px lighten($state-danger-text, 20%); + border-color: $state-danger-text-hover; + $shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px var(--pretix-brand-danger-lighten-5); @include box-shadow($shadow); } } diff --git a/src/pretix/static/pretixpresale/scss/_theme.scss b/src/pretix/static/pretixpresale/scss/_theme.scss index 98113723ad..d55a46a008 100644 --- a/src/pretix/static/pretixpresale/scss/_theme.scss +++ b/src/pretix/static/pretixpresale/scss/_theme.scss @@ -1,5 +1,3 @@ -@import "../../pretixbase/scss/_contrast.scss"; - h1, .h1, h2, .h2, h3, .h3 { @@ -38,10 +36,10 @@ h3, .h3 { padding-top: 15px; a:link, a:visited { - color: choose-contrast-color($body-bg, white, $link-color); + color: var(--pretix-link-contrast-color); } a:active, a:hover, a:focus { - color: choose-contrast-color($body-bg, white, $link-hover-color); + color: var(--pretix-link-hover-contrast-color); } } @@ -130,7 +128,7 @@ footer { } .label-success-warning { - @include label-variant($label-success-bg); + @include label-variant($label-success-bg, $label-success-bg-hover); padding-left: 2.5em; position: relative; diff --git a/src/pretix/static/pretixpresale/scss/main.scss b/src/pretix/static/pretixpresale/scss/main.scss index 8800743972..a51096d172 100644 --- a/src/pretix/static/pretixpresale/scss/main.scss +++ b/src/pretix/static/pretixpresale/scss/main.scss @@ -1,8 +1,9 @@ -/* before variables.scss because it only affects presale, not control */ +// before variables.scss because it only affects presale, not control $body-bg: #f5f5f5 !default; /* imports */ -@import "../../pretixbase/scss/_variables.scss"; +@import "../../pretixbase/scss/_theme_variables.scss"; +@import "../../pretixbase/scss/bootstrap_vars.scss"; @import "../../bootstrap/scss/_bootstrap_reduced.scss"; @import "../../pretixbase/scss/_theme.scss"; @import "../../lightbox/css/lightbox.scss"; @@ -111,8 +112,8 @@ footer nav .btn-link { font-size: 40px; } a:hover .panel-primary > .panel-heading { - background-color: darken($btn-primary-bg, 10%); - border-color: darken($btn-primary-border, 12%); + background-color: var(--pretix-brand-primary-darken-10); + border-color: var(--pretix-brand-primary-darken-17); } .thank-you { @@ -394,7 +395,7 @@ details.details-open .panel-title::before { } .panel-primary .panel-title a[data-toggle="collapse"]:hover { - background-color: darken($btn-primary-bg, 10%); + background-color: var(--pretix-brand-primary-darken-10); } .panel-title a.h6 { @@ -496,7 +497,7 @@ h2 .label { text-align: center; } &.availability .progress-bar-success { - background: lighten($brand-success, 20%); + background: var(--brand-success-lighten-20); } } diff --git a/src/pretix/static/pretixpresale/scss/waiting.scss b/src/pretix/static/pretixpresale/scss/waiting.scss index 94ab41b995..b7b655f1b3 100644 --- a/src/pretix/static/pretixpresale/scss/waiting.scss +++ b/src/pretix/static/pretixpresale/scss/waiting.scss @@ -1,4 +1,5 @@ -@import "../../pretixbase/scss/_variables.scss"; +@import "../../pretixbase/scss/_theme_variables.scss"; +@import "../../pretixbase/scss/_bootstrap_vars.scss"; @import "../../bootstrap/scss/_bootstrap.scss"; @import "../../pretixbase/scss/_theme.scss"; @import "../../fontawesome/scss/font-awesome.scss"; diff --git a/src/pretix/static/pretixpresale/scss/widget.scss b/src/pretix/static/pretixpresale/scss/widget.scss index 7ba3520ce4..edfc74bdbc 100644 --- a/src/pretix/static/pretixpresale/scss/widget.scss +++ b/src/pretix/static/pretixpresale/scss/widget.scss @@ -1,4 +1,5 @@ -@import "../../pretixbase/scss/_variables.scss"; +// not included, will be dynamically prepended @import "../../pretixbase/scss/_theme_variables.scss"; +@import "../../pretixbase/scss/_bootstrap_vars.scss"; @import "../../bootstrap/scss/bootstrap/variables"; @import "../../bootstrap/scss/bootstrap/mixins"; @@ -47,7 +48,7 @@ text-decoration: none; @include button-size($padding-base-vertical, $padding-base-horizontal, $font-size-base, $line-height-base, $btn-border-radius-base); @include user-select(none); - @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border); + @include button-variant($btn-primary-color, $btn-primary-bg, $btn-primary-border, $btn-primary-background-active, $btn-primary-border-active, $btn-primary-border-hover); &, &:active, @@ -66,11 +67,11 @@ @include box-shadow(none); } &.pretix-widget-btn-default { - @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); + @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border, darken($btn-default-bg, 10%), darken($btn-default-border, 25%), darken($btn-default-border, 12%)); } } label.pretix-widget-btn-checkbox { - @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border); + @include button-variant($btn-default-color, $btn-default-bg, $btn-default-border, darken($btn-default-bg, 10%), darken($btn-default-border, 25%), darken($btn-default-border, 12%)); border-width: 1px; border-style: solid; position: relative; diff --git a/src/pretix/static/select2/_select2_bootstrap.scss b/src/pretix/static/select2/_select2_bootstrap.scss index 1afafdcb99..5841a0b096 100644 --- a/src/pretix/static/select2/_select2_bootstrap.scss +++ b/src/pretix/static/select2/_select2_bootstrap.scss @@ -90,8 +90,8 @@ $s2bs-form-control-transition: border-color ease-in-out .15s, box-shadow ease-in // @see http://getbootstrap.com/css/#forms-control-validation // @see https://github.com/twbs/bootstrap-sass/blob/master/assets/stylesheets/bootstrap/_forms.scss#L388 -@mixin validation-state-focus($color) { - $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($color, 20%); +@mixin validation-state-focus($color, $color-shadow, $color-hover) { + $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px $color-shadow; .select2-dropdown, .select2-selection { @@ -101,14 +101,14 @@ $s2bs-form-control-transition: border-color ease-in-out .15s, box-shadow ease-in .select2-container--focus .select2-selection, .select2-container--open .select2-selection { @include box-shadow($shadow); - border-color: darken($color, 10%); + border-color: $color-hover; } &.select2-drop-active { - border-color: darken($color, 10%); + border-color: $color-hover; &.select2-drop.select2-drop-above { - border-top-color: darken($color, 10%); + border-top-color: $color-hover; } } } @@ -724,15 +724,15 @@ $s2bs-form-control-transition: border-color ease-in-out .15s, box-shadow ease-in */ .has-warning { - @include validation-state-focus($state-warning-text); + @include validation-state-focus($state-warning-text, $state-warning-shadow, $state-warning-text-hover); } .has-error { - @include validation-state-focus($state-danger-text); + @include validation-state-focus($state-danger-text, $state-danger-shadow, $state-danger-text-hover); } .has-success { - @include validation-state-focus($state-success-text); + @include validation-state-focus($state-success-text, $state-success-shadow, $state-success-text-hover); } /** diff --git a/src/pretix/static/slider/_bootstrap-slider.scss b/src/pretix/static/slider/_bootstrap-slider.scss index cb524808da..e2544a0db3 100644 --- a/src/pretix/static/slider/_bootstrap-slider.scss +++ b/src/pretix/static/slider/_bootstrap-slider.scss @@ -301,7 +301,7 @@ $slider-unicode-color: #726204 !default; } .slider-selection.tick-slider-selection { - @include slider_background-image($slider-secondary-top, $slider-secondary-bottom, mix($slider-secondary-top, $slider-secondary-bottom)); + @include slider_background-image($slider-secondary-top, $slider-secondary-bottom, $slider-secondary-top); } .slider-track-low, .slider-track-high { @@ -313,7 +313,7 @@ $slider-unicode-color: #726204 !default; } .slider-handle { - @include slider_background-image($slider-primary-top, $slider-primary-bottom, mix($slider-primary-top, $slider-primary-bottom)); + @include slider_background-image($slider-primary-top, $slider-primary-bottom, $slider-primary-top); @include slider_box-shadow(inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05)); position: absolute; @@ -380,7 +380,7 @@ $slider-unicode-color: #726204 !default; } &.in-selection { - @include slider_background-image($slider-secondary-top, $slider-secondary-bottom, mix($slider-secondary-top, $slider-secondary-bottom)); + @include slider_background-image($slider-secondary-top, $slider-secondary-bottom, $slider-secondary-top); opacity: 1; } } diff --git a/src/tests/api/test_events.py b/src/tests/api/test_events.py index 805397cded..35255c0291 100644 --- a/src/tests/api/test_events.py +++ b/src/tests/api/test_events.py @@ -49,7 +49,6 @@ from pretix.base.models import ( Event, InvoiceAddress, Order, OrderPosition, Organizer, SeatingPlan, ) from pretix.base.models.orders import OrderFee -from pretix.testutils.mock import mocker_context @pytest.fixture @@ -1244,121 +1243,117 @@ def test_get_event_settings(token_client, organizer, event): @pytest.mark.django_db def test_patch_event_settings(token_client, organizer, event): - with mocker_context() as mocker: - mocked = mocker.patch('pretix.presale.style.regenerate_css.apply_async') - organizer.settings.imprint_url = 'https://example.org' - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'imprint_url': 'https://example.com', - 'confirm_texts': [ - { - 'de': 'Ich bin mit den AGB einverstanden.' - } - ], - 'reusable_media_active': True, # readonly, ignored - }, - format='json' - ) - assert resp.status_code == 200 - assert resp.data['imprint_url'] == "https://example.com" - assert not resp.data['reusable_media_active'] - event.settings.flush() - assert event.settings.imprint_url == 'https://example.com' - assert not event.settings.reusable_media_active - assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 1 - mocked.assert_not_called() + organizer.settings.imprint_url = 'https://example.org' + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'imprint_url': 'https://example.com', + 'confirm_texts': [ + { + 'de': 'Ich bin mit den AGB einverstanden.' + } + ], + 'reusable_media_active': True, # readonly, ignored + }, + format='json' + ) + assert resp.status_code == 200 + assert resp.data['imprint_url'] == "https://example.com" + assert not resp.data['reusable_media_active'] + event.settings.flush() + assert event.settings.imprint_url == 'https://example.com' + assert not event.settings.reusable_media_active + assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 1 - # The same settings again do not create a new log entry - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'imprint_url': 'https://example.com', - 'confirm_texts': [ - { - 'de': 'Ich bin mit den AGB einverstanden.' - } - ], - 'reusable_media_active': True, # readonly, ignored - }, - format='json' - ) - assert resp.status_code == 200 - assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 1 + # The same settings again do not create a new log entry + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'imprint_url': 'https://example.com', + 'confirm_texts': [ + { + 'de': 'Ich bin mit den AGB einverstanden.' + } + ], + 'reusable_media_active': True, # readonly, ignored + }, + format='json' + ) + assert resp.status_code == 200 + assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 1 - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'primary_color': '#ff0000', - 'theme_color_background': '#ff0000', - }, - format='json' - ) - assert resp.status_code == 200 - event.settings.flush() - mocked.assert_any_call(args=(event.pk,)) - assert event.settings.primary_color == '#ff0000' - assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 2 + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'primary_color': '#ff0000', + 'theme_color_background': '#ff0000', + }, + format='json' + ) + assert resp.status_code == 200 + event.settings.flush() + assert event.settings.primary_color == '#ff0000' + assert event.all_logentries().filter(action_type="pretix.event.settings").count() == 2 - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'primary_color': None, - 'theme_color_background': None, - }, - format='json' - ) - assert resp.status_code == 200 - event.settings.flush() - assert event.settings.primary_color != '#ff0000' - assert 'primary_color' not in event.settings._cache() + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'primary_color': None, + 'theme_color_background': None, + }, + format='json' + ) + assert resp.status_code == 200 + event.settings.flush() + assert event.settings.primary_color != '#ff0000' + assert 'primary_color' not in event.settings._cache() - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'imprint_url': None, - }, - format='json' - ) - assert resp.status_code == 200 - assert resp.data['imprint_url'] == "https://example.org" - event.settings.flush() - assert event.settings.imprint_url == 'https://example.org' + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'imprint_url': None, + }, + format='json' + ) + assert resp.status_code == 200 + assert resp.data['imprint_url'] == "https://example.org" + event.settings.flush() + assert event.settings.imprint_url == 'https://example.org' - resp = token_client.put( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'imprint_url': 'invalid' - }, - format='json' - ) - assert resp.status_code == 405 + resp = token_client.put( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'imprint_url': 'invalid' + }, + format='json' + ) + assert resp.status_code == 405 - locales = event.settings.locales + locales = event.settings.locales - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'locales': event.settings.locales + ['de', 'de-informal'], - }, - format='json' - ) - assert resp.status_code == 200 - assert set(resp.data['locales']) == set(locales + ['de', 'de-informal']) - event.settings.flush() - assert set(event.settings.locales) == set(locales + ['de', 'de-informal']) + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'locales': event.settings.locales + ['de', 'de-informal'], + }, + format='json' + ) + assert resp.status_code == 200 + assert set(resp.data['locales']) == set(locales + ['de', 'de-informal']) + event.settings.flush() + assert set(event.settings.locales) == set(locales + ['de', 'de-informal']) - resp = token_client.patch( - '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), - { - 'locales': locales, - }, - format='json' - ) - assert resp.status_code == 200 - assert set(resp.data['locales']) == set(locales) - event.settings.flush() - assert set(event.settings.locales) == set(locales) + resp = token_client.patch( + '/api/v1/organizers/{}/events/{}/settings/'.format(organizer.slug, event.slug), + { + 'locales': locales, + }, + format='json' + ) + assert resp.status_code == 200 + assert set(resp.data['locales']) == set(locales) + event.settings.flush() + assert set(event.settings.locales) == set(locales) @pytest.mark.django_db diff --git a/src/tests/api/test_organizers.py b/src/tests/api/test_organizers.py index 40ee35e255..b5bb0c208b 100644 --- a/src/tests/api/test_organizers.py +++ b/src/tests/api/test_organizers.py @@ -23,8 +23,6 @@ import pytest from django.core.files.base import ContentFile from tests.const import SAMPLE_PNG -from pretix.testutils.mock import mocker_context - TEST_ORGANIZER_RES = { "name": "Dummy", "slug": "dummy", @@ -69,62 +67,57 @@ def test_get_settings(token_client, organizer): @pytest.mark.django_db def test_patch_settings(token_client, organizer): - with mocker_context() as mocker: - mocked = mocker.patch('pretix.presale.style.regenerate_organizer_css.apply_async') + organizer.settings.event_list_type = 'week' + resp = token_client.patch( + '/api/v1/organizers/{}/settings/'.format(organizer.slug), + { + 'event_list_type': 'list' + }, + format='json' + ) + assert resp.status_code == 200 + assert resp.data['event_list_type'] == "list" + organizer.settings.flush() + assert organizer.settings.event_list_type == 'list' - organizer.settings.event_list_type = 'week' - resp = token_client.patch( - '/api/v1/organizers/{}/settings/'.format(organizer.slug), - { - 'event_list_type': 'list' - }, - format='json' - ) - assert resp.status_code == 200 - assert resp.data['event_list_type'] == "list" - organizer.settings.flush() - assert organizer.settings.event_list_type == 'list' + resp = token_client.patch( + '/api/v1/organizers/{}/settings/'.format(organizer.slug), + { + 'event_list_type': None, + }, + format='json' + ) + assert resp.status_code == 200 + assert resp.data['event_list_type'] == "list" + organizer.settings.flush() + assert organizer.settings.event_list_type == 'list' - resp = token_client.patch( - '/api/v1/organizers/{}/settings/'.format(organizer.slug), - { - 'event_list_type': None, - }, - format='json' - ) - assert resp.status_code == 200 - assert resp.data['event_list_type'] == "list" - organizer.settings.flush() - assert organizer.settings.event_list_type == 'list' - mocked.assert_not_called() + resp = token_client.put( + '/api/v1/organizers/{}/settings/'.format(organizer.slug), + { + 'event_list_type': 'put-not-allowed' + }, + format='json' + ) + assert resp.status_code == 405 - resp = token_client.put( - '/api/v1/organizers/{}/settings/'.format(organizer.slug), - { - 'event_list_type': 'put-not-allowed' - }, - format='json' - ) - assert resp.status_code == 405 + resp = token_client.patch( + '/api/v1/organizers/{}/settings/'.format(organizer.slug), + { + 'primary_color': 'invalid-color' + }, + format='json' + ) + assert resp.status_code == 400 - resp = token_client.patch( - '/api/v1/organizers/{}/settings/'.format(organizer.slug), - { - 'primary_color': 'invalid-color' - }, - format='json' - ) - assert resp.status_code == 400 - - resp = token_client.patch( - '/api/v1/organizers/{}/settings/'.format(organizer.slug), - { - 'primary_color': '#ff0000' - }, - format='json' - ) - assert resp.status_code == 200 - mocked.assert_any_call(args=(organizer.pk,)) + resp = token_client.patch( + '/api/v1/organizers/{}/settings/'.format(organizer.slug), + { + 'primary_color': '#ff0000' + }, + format='json' + ) + assert resp.status_code == 200 @pytest.mark.django_db diff --git a/src/tests/control/test_events.py b/src/tests/control/test_events.py index c76bcff29f..dac406cde4 100644 --- a/src/tests/control/test_events.py +++ b/src/tests/control/test_events.py @@ -47,7 +47,6 @@ from i18nfield.strings import LazyI18nString from tests.base import SoupTest, extract_form_fields from pretix.base.models import Event, LogEntry, Order, Organizer, Team, User -from pretix.testutils.mock import mocker_context @pytest.fixture @@ -476,18 +475,14 @@ class EventsTest(SoupTest): assert self.event1.settings.get('invoice_address_required', as_type=bool) def test_display_settings(self): - with mocker_context() as mocker: - mocked = mocker.patch('pretix.presale.style.regenerate_css.apply_async') - - doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug)) - data = extract_form_fields(doc.select("form")[0]) - data['settings-primary_color'] = '#000000' - doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug), - data, follow=True) - assert doc.select('.alert-success') - self.event1.settings.flush() - assert self.event1.settings.get('primary_color') == '#000000' - mocked.assert_any_call(args=(self.event1.pk,)) + doc = self.get_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug)) + data = extract_form_fields(doc.select("form")[0]) + data['settings-primary_color'] = '#000000' + doc = self.post_doc('/control/event/%s/%s/settings/' % (self.orga1.slug, self.event1.slug), + data, follow=True) + assert doc.select('.alert-success') + self.event1.settings.flush() + assert self.event1.settings.get('primary_color') == '#000000' def test_display_settings_do_not_override_parent(self): self.orga1.settings.primary_color = '#000000' diff --git a/src/tests/control/test_organizer.py b/src/tests/control/test_organizer.py index fe4b336563..f9f36800b4 100644 --- a/src/tests/control/test_organizer.py +++ b/src/tests/control/test_organizer.py @@ -81,13 +81,6 @@ class OrganizerTest(SoupTest): assert self.orga1.name == "CCC e.V." def test_organizer_display_settings(self): - called = False - - def set_called(*args, **kwargs): - nonlocal called - called = True - - self.monkeypatch.setattr("pretix.presale.style.regenerate_organizer_css.apply_async", set_called) assert not self.orga1.settings.presale_css_checksum doc = self.get_doc('/control/organizer/%s/edit' % (self.orga1.slug,)) doc.select("[name=settings-primary_color]")[0]['value'] = "#33c33c" @@ -99,7 +92,6 @@ class OrganizerTest(SoupTest): assert doc.select("[name=settings-primary_color]")[0]['value'] == "#33c33c" self.orga1.settings.flush() assert self.orga1.settings.primary_color == "#33c33c" - assert called def test_email_settings(self): doc = self.get_doc('/control/organizer/%s/settings/email' % self.orga1.slug) diff --git a/src/tests/presale/test_style.py b/src/tests/presale/test_style.py index ff6e428cc2..e71124f88a 100644 --- a/src/tests/presale/test_style.py +++ b/src/tests/presale/test_style.py @@ -20,15 +20,11 @@ # . # import datetime -import os.path -from django.conf import settings -from django.test import TestCase, override_settings +from django.test import TestCase from django_scopes import scopes_disabled from pretix.base.models import Event, Organizer -from pretix.multidomain.models import KnownDomain -from pretix.presale.style import regenerate_css, regenerate_organizer_css class StyleTest(TestCase): @@ -44,110 +40,19 @@ class StyleTest(TestCase): def test_organizer_generate_css_for_inherited_events(self): self.orga.settings.primary_color = "#33c33c" - regenerate_organizer_css.apply(args=(self.orga.pk,)) - self.orga.settings.flush() - assert self.orga.settings.presale_css_file - with open(os.path.join(settings.MEDIA_ROOT, self.orga.settings.presale_css_file), 'r') as c: - assert '#33c33c' in c.read() + c = self.client.get("/ccc/theme.css").content.decode() + assert '#33c33c' in c - self.event.settings.flush() - assert self.event.settings.presale_css_file - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert '#33c33c' in c.read() + c = self.client.get("/ccc/30c3/theme.css").content.decode() + assert '#33c33c' in c def test_organizer_generate_css_only_for_inherited_events(self): self.orga.settings.primary_color = "#33c33c" self.event.settings.primary_color = "#34c34c" - regenerate_organizer_css.apply(args=(self.orga.pk,)) - self.orga.settings.flush() - assert self.orga.settings.presale_css_file - with open(os.path.join(settings.MEDIA_ROOT, self.orga.settings.presale_css_file), 'r') as c: - assert '#33c33c' in c.read() - self.event.settings.flush() - assert self.event.settings.presale_css_file - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert '#34c34c' not in c.read() - assert '#33c33c' not in c.read() + c = self.client.get("/ccc/theme.css").content.decode() + assert '#33c33c' in c - def test_event_generate_css_individually(self): - self.orga.settings.primary_color = "#33c33c" - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - - self.event.settings.flush() - assert self.event.settings.presale_css_file - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert '#34c34c' in c.read() - assert '#33c33c' not in c.read() - - regenerate_organizer_css.apply(args=(self.orga.pk,)) - - self.event.settings.flush() - assert self.event.settings.presale_css_file - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert '#34c34c' in c.read() - assert '#33c33c' not in c.read() - - def test_event_generate_css_new_file(self): - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - - self.event.settings.flush() - fname = self.event.settings.presale_css_file - - self.event.settings.primary_color = "#ff00ff" - regenerate_css.apply(args=(self.event.pk,)) - self.event.settings.flush() - assert self.event.settings.presale_css_file != fname - - def test_event_generate_css_cache_file(self): - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - - self.event.settings.flush() - fname = self.event.settings.presale_css_file - - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - self.event.settings.flush() - assert self.event.settings.presale_css_file == fname - - @override_settings( - MEDIA_URL="https://usercontent.pretix.space/media/", - SITE_URL="https://pretix.eu" - ) - def test_seperate_media_domain(self): - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - self.event.settings.flush() - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert 'https://pretix.eu/static/' in c.read() - - @override_settings( - MEDIA_URL="https://usercontent.pretix.space/media/", - SITE_URL="https://pretix.eu" - ) - def test_seperate_media_domain_and_organizer_domain(self): - KnownDomain.objects.create(domainname="test.pretix.eu", organizer=self.orga) - - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - self.event.settings.flush() - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert 'https://test.pretix.eu/static/' in c.read() - - @override_settings( - STATIC_URL="https://static.pretix.files/static/", - MEDIA_URL="https://usercontent.pretix.space/media/", - SITE_URL="https://pretix.eu" - ) - def test_seperate_media_domain_and_static_domain(self): - KnownDomain.objects.create(domainname="test.pretix.eu", organizer=self.orga) - - self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) - self.event.settings.flush() - with open(os.path.join(settings.MEDIA_ROOT, self.event.settings.presale_css_file), 'r') as c: - assert 'https://static.pretix.files/static/' in c.read() - assert 'https://test.pretix.eu/static/' not in c.read() + c = self.client.get("/ccc/30c3/theme.css").content.decode() + assert '#34c34c' in c + assert '#33c33c' not in c diff --git a/src/tests/presale/test_widget.py b/src/tests/presale/test_widget.py index 5e97882fac..372336457f 100644 --- a/src/tests/presale/test_widget.py +++ b/src/tests/presale/test_widget.py @@ -31,7 +31,6 @@ from django_scopes import scopes_disabled from freezegun import freeze_time from pretix.base.models import Order, OrderPosition -from pretix.presale.style import regenerate_css, regenerate_organizer_css from .test_cart import CartTestMixin @@ -544,23 +543,23 @@ class WidgetCartTest(CartTestMixin, TestCase): def test_css_customized(self): response = self.client.get('/%s/%s/widget/v1.css' % (self.orga.slug, self.event.slug)) c = b"".join(response.streaming_content).decode() - assert '#7f5a91' in c + assert '#8E44B3' in c assert '#33c33c' not in c assert '#34c34c' not in c self.orga.settings.primary_color = "#33c33c" - regenerate_organizer_css.apply(args=(self.orga.pk,)) + self.orga.cache.clear() response = self.client.get('/%s/%s/widget/v1.css' % (self.orga.slug, self.event.slug)) c = b"".join(response.streaming_content).decode() - assert '#7f5a91' not in c + assert '#8E44B3' not in c assert '#33c33c' in c assert '#34c34c' not in c self.event.settings.primary_color = "#34c34c" - regenerate_css.apply(args=(self.event.pk,)) + self.event.cache.clear() response = self.client.get('/%s/%s/widget/v1.css' % (self.orga.slug, self.event.slug)) c = b"".join(response.streaming_content).decode() - assert '#7f5a91' not in c + assert '#8E44B3' not in c assert '#33c33c' not in c assert '#34c34c' in c