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 b93a4953ff..0000000000 Binary files a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.eot and /dev/null differ diff --git a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.svg b/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.svg deleted file mode 100644 index 94fb5490a2..0000000000 --- a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.svg +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 1413fc609a..0000000000 Binary files a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.ttf and /dev/null differ diff --git a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff b/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff deleted file mode 100644 index 9e612858f8..0000000000 Binary files a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff and /dev/null differ 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 64539b54c3..0000000000 Binary files a/src/pretix/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 and /dev/null differ 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