Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#1675] Added extra_css field to SiteConfiguration and master template #740

Merged
merged 4 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions requirements/base.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ glom # processing nested data
furl # processing urls
weasyprint # export to pdf
pywatchman
bleach[css]
tinycss2


# Framework libraries
Django>=3.2.11<4.0
Expand Down
6 changes: 6 additions & 0 deletions requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ beautifulsoup4==4.10.0
# via -r requirements/base.in
billiard==3.6.4.0
# via celery
bleach[css]==6.0.0
# via -r requirements/base.in
boltons==21.0.0
# via
# face
Expand Down Expand Up @@ -472,6 +474,7 @@ simplejson==3.18.0
# via mail-parser
six==1.16.0
# via
# bleach
# click-repl
# django-appdata
# django-choices
Expand All @@ -497,6 +500,8 @@ text-unidecode==1.3
# via faker
tinycss2==1.1.1
# via
# -r requirements/base.in
# bleach
# cssselect2
# svglib
# weasyprint
Expand Down Expand Up @@ -525,6 +530,7 @@ weasyprint==54.2
# via -r requirements/base.in
webencodings==0.5.1
# via
# bleach
# cssselect2
# html5lib
# tinycss2
Expand Down
7 changes: 7 additions & 0 deletions requirements/ci.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ billiard==3.6.4.0
# -c requirements/base.txt
# -r requirements/base.txt
# celery
bleach[css]==6.0.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
boltons==21.0.0
# via
# -c requirements/base.txt
Expand Down Expand Up @@ -841,6 +845,7 @@ six==1.16.0
# via
# -c requirements/base.txt
# -r requirements/base.txt
# bleach
# click-repl
# django-appdata
# django-choices
Expand Down Expand Up @@ -885,6 +890,7 @@ tinycss2==1.1.1
# via
# -c requirements/base.txt
# -r requirements/base.txt
# bleach
# cssselect2
# svglib
# weasyprint
Expand Down Expand Up @@ -941,6 +947,7 @@ webencodings==0.5.1
# via
# -c requirements/base.txt
# -r requirements/base.txt
# bleach
# cssselect2
# html5lib
# tinycss2
Expand Down
7 changes: 7 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ billiard==3.6.4.0
# celery
black==22.12.0
# via -r requirements/dev.in
bleach[css]==6.0.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
boltons==21.0.0
# via
# -c requirements/ci.txt
Expand Down Expand Up @@ -934,6 +938,7 @@ six==1.16.0
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# bleach
# click-repl
# django-appdata
# django-choices
Expand Down Expand Up @@ -1005,6 +1010,7 @@ tinycss2==1.1.1
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# bleach
# cssselect2
# svglib
# weasyprint
Expand Down Expand Up @@ -1073,6 +1079,7 @@ webencodings==0.5.1
# via
# -c requirements/ci.txt
# -r requirements/ci.txt
# bleach
# cssselect2
# html5lib
# tinycss2
Expand Down
39 changes: 39 additions & 0 deletions src/open_inwoner/configurations/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.forms import ValidationError
from django.urls import resolve
from django.urls.exceptions import Resolver404
from django.utils.html import format_html, format_html_join
from django.utils.translation import ugettext_lazy as _

from ordered_model.admin import OrderedInlineModelAdminMixin, OrderedTabularInline
Expand All @@ -16,6 +17,9 @@
from open_inwoner.ckeditor5.widgets import CKEditorWidget

from ..utils.colors import ACCESSIBLE_CONTRAST_RATIO, get_contrast_ratio
from ..utils.css import allowed_properties
from ..utils.fields import CSSEditorWidget
from ..utils.itertools import split
from .models import SiteConfiguration, SiteConfigurationPage


Expand All @@ -39,6 +43,9 @@ class SiteConfigurarionAdminForm(forms.ModelForm):
class Meta:
model = SiteConfiguration
fields = "__all__"
widgets = {
"extra_css": CSSEditorWidget,
}

def clean_redirect_to(self):
redirect_to = self.cleaned_data["redirect_to"]
Expand Down Expand Up @@ -198,10 +205,42 @@ class SiteConfigurarionAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin):
)
},
),
(
_("Advanced display options"),
{
"classes": ["collapse"],
"fields": (
"extra_css",
"extra_css_allowed",
),
},
),
)
inlines = [SiteConfigurationPageInline]
form = SiteConfigurarionAdminForm

readonly_fields = [
"extra_css_allowed",
Bartvaderkin marked this conversation as resolved.
Show resolved Hide resolved
]

@admin.display(
description="",
)
Bartvaderkin marked this conversation as resolved.
Show resolved Hide resolved
def extra_css_allowed(self, obj):
columns = split(allowed_properties(), 4)

def _get_column(props):
return format_html_join("", "{}<br>", ((p,) for p in props))

return format_html(
'<div class="css-properties-table">\n{}\n</div>',
format_html_join(
"\n",
'<div class="css-properties-table__column">{}</div>',
((_get_column(c),) for c in columns),
),
)

def report_contrast_ratio(self, request, obj):
def check_contrast_ratio(label1, color1, label2, color2, expected_ratio):
ratio = get_contrast_ratio(color1, color2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 3.2.20 on 2023-08-22 12:08

from django.db import migrations

import open_inwoner.utils.fields


class Migration(migrations.Migration):

dependencies = [
("configurations", "0048_auto_20230816_1255"),
]

operations = [
migrations.AddField(
model_name="siteconfiguration",
name="extra_css",
field=open_inwoner.utils.fields.CSSField(
blank=True,
help_text="Additional CSS added to the page. Note only a (safe) subset of CSS properties is supported.",
verbose_name="Extra CSS",
),
),
]
15 changes: 15 additions & 0 deletions src/open_inwoner/configurations/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from open_inwoner.utils.validators import DutchPhoneNumberValidator

from ..utils.colors import hex_to_hsl
from ..utils.css import clean_stylesheet
from ..utils.fields import CSSField
from ..utils.validators import FilerExactImageSizeValidator
from .choices import ColorTypeChoices, OpenIDDisplayChoices
from .validators import validate_oidc_config
Expand Down Expand Up @@ -460,12 +462,25 @@ class SiteConfiguration(SingletonModel):
),
)

extra_css = CSSField(
blank=True,
verbose_name=_("Extra CSS"),
help_text=_(
"Additional CSS added to the page. Note only a (safe) subset of CSS properties is supported."
),
)

class Meta:
verbose_name = _("Site Configuration")

def __str__(self):
return str(_("Site Configuration"))

def save(self, *args, **kwargs):
if self.extra_css:
self.extra_css = clean_stylesheet(self.extra_css)
super().save(*args, **kwargs)

@property
def get_primary_color(self):
return hex_to_hsl(self.primary_color)
Expand Down
53 changes: 53 additions & 0 deletions src/open_inwoner/configurations/tests/test_extra_css.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from django.test import override_settings
from django.urls import reverse
from django.utils.html import escape

from django_webtest import WebTest

from ...cms.tests import cms_tools
from ...utils.test import ClearCachesMixin
from ..models import SiteConfiguration


@override_settings(ROOT_URLCONF="open_inwoner.cms.tests.urls")
class ExtraCSSTest(ClearCachesMixin, WebTest):
def test_extra_css_is_cleaned_on_save(self):
self.config = SiteConfiguration.get_solo()
self.config.extra_css = "body {color: red; unknown: bar; }"
self.config.save()

self.assertEquals("body {color: red; }", self.config.extra_css)

def test_extra_css_rendered_in_homepage(self):
cms_tools.create_homepage()

self.config = SiteConfiguration.get_solo()
self.config.extra_css = "body {color: red;}"
self.config.save()

response = self.app.get(reverse("pages-root"))
extra_css = response.context.get("extra_css")

self.assertEquals(extra_css, self.config.extra_css)
self.assertContains(response, "body {color: red;}")
Bartvaderkin marked this conversation as resolved.
Show resolved Hide resolved

def test_extra_css_rendered_in_homepage_is_escaped(self):
cms_tools.create_homepage()

self.config = SiteConfiguration.get_solo()
css = 'body {color: "/style><script>evil();</script><style ";}'
self.config.extra_css = css
self.config.save()

response = self.app.get(reverse("pages-root"))
extra_css = response.context.get("extra_css")
# not escaped in context
self.assertEquals(extra_css, self.config.extra_css)

style = response.pyquery("#extra-css")
self.assertEqual(1, len(style))
actual = style[0].text.strip()
expected = escape(css)

# escaped
self.assertEquals(expected, actual)
17 changes: 17 additions & 0 deletions src/open_inwoner/scss/admin/_admin_theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,20 @@ form div.help {
display: inline-block;
margin-left: inherit !important;
}

/* Custom code fields */
.textfield {
&--code {
font-family: monospace;
font-size: 95%;
}
}
.css-properties-table {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-column-gap: 2em;
&__column {
font-family: monospace;
font-size: 95%;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for this 5% decrease? I can barely tell the difference from looking at the screen.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To make it slightly smaller because monospace is so chonky.

}
}
6 changes: 5 additions & 1 deletion src/open_inwoner/templates/master.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@
{% render_block "css" %}
{% block extra_css %}{% endblock %}
{% block extra_head %}{% endblock %}

{% if extra_css %}
<style id="extra-css" nonce="{{ request.csp_nonce }}">
{{ extra_css }}
</style>
{% endif %}
{% if request|cookies_accepted %}
{% include "analytics/google.html" %}
{% include "analytics/matomo.html" %}
Expand Down
1 change: 1 addition & 0 deletions src/open_inwoner/utils/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def settings(request):
"cookie_info_text": config.cookie_info_text,
"cookie_link_text": config.cookie_link_text,
"cookie_link_url": config.cookie_link_url,
"extra_css": config.extra_css,
"menu_categories": Category.get_root_nodes().published(),
"search_form": SearchForm(auto_id=False),
"has_general_faq_questions": Question.objects.general().exists(),
Expand Down
Loading
Loading