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

[#2030] Add option to upload and use custom fonts #963

Merged
merged 2 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
14 changes: 12 additions & 2 deletions src/open_inwoner/configurations/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from ..utils.css import ALLOWED_PROPERTIES
from ..utils.fields import CSSEditorWidget
from ..utils.iteration import split
from .models import SiteConfiguration, SiteConfigurationPage
from .models import CustomFontSet, SiteConfiguration, SiteConfigurationPage


@admin.action(description=_("Delete selected websites"))
Expand Down Expand Up @@ -60,6 +60,9 @@ def delete_model(self, request, obj):
else:
super().delete_model(request, obj)

class Media:
css = {"all": ("css/admin/admin_overrides.css",)}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure why (as it was working before) but when I upload any kind of TTF file, it does correctly end up in the Media directory, but I do not see anything happening on the front-end (perhaps we need a fallback font somewhere?).
Here you can download clearly different TTF fonts for testing: https://www.fontsquirrel.com/fonts/acme

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The font faces definitions were missing the custom fonts after rebasing. Also, I had to specify the font-family for our utrecht-heading. Should work now.



# re-register `Site` with our CustomSiteAdmin
admin.site.unregister(Site)
Expand All @@ -82,6 +85,13 @@ class SiteConfigurationPageInline(OrderedTabularInline):
autocomplete_fields = ("flatpage",)


class FontConfigurationInline(admin.StackedInline):
model = CustomFontSet
verbose_name = "Fonts"
min_num = 1
can_delete = False


class SiteConfigurationAdminForm(forms.ModelForm):
class Meta:
model = SiteConfiguration
Expand Down Expand Up @@ -284,7 +294,7 @@ class SiteConfigurationAdmin(OrderedInlineModelAdminMixin, SingletonModelAdmin):
),
(_("Social media"), {"fields": ("display_social",)}),
)
inlines = [SiteConfigurationPageInline]
inlines = [SiteConfigurationPageInline, FontConfigurationInline]
form = SiteConfigurationAdminForm

readonly_fields = [
Expand Down
8 changes: 8 additions & 0 deletions src/open_inwoner/configurations/choices.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ class ColorTypeChoices(models.TextChoices):
class OpenIDDisplayChoices(models.TextChoices):
admin = "admin", _("Admin")
regular = "regular", _("Regular user")


class CustomFontName(models.TextChoices):
body = _("body_font_regular"), _("Text body font")
body_italic = _("body_font_italic"), _("Text body font italic")
body_bold = _("body_font_bold"), _("Text body font bold")
body_bold_italic = _("body_font_bold_italic"), _("Text body font bold italic")
heading = _("heading_font"), _("Heading font")
69 changes: 69 additions & 0 deletions src/open_inwoner/configurations/migrations/0058_customfontset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Generated by Django 3.2.23 on 2024-01-29 10:45

import django.core.validators
import django.db.models.deletion
from django.db import migrations, models

import open_inwoner.configurations.models
import open_inwoner.utils.files


class Migration(migrations.Migration):

dependencies = [
("configurations", "0057_siteconfiguration_theme_stylesheet"),
]

operations = [
migrations.CreateModel(
name="CustomFontSet",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"text_body_font",
open_inwoner.configurations.models.CustomFontField(
blank=True,
help_text="Upload text body font. TTF font types only.",
null=True,
storage=open_inwoner.utils.files.OverwriteStorage(),
upload_to=open_inwoner.configurations.models.CustomFontSet.update_filename_body,
validators=[
django.core.validators.FileExtensionValidator(["ttf"])
],
verbose_name="Text body font",
),
),
(
"heading_font",
open_inwoner.configurations.models.CustomFontField(
blank=True,
help_text="Upload heading font. TTF font types only.",
null=True,
storage=open_inwoner.utils.files.OverwriteStorage(),
upload_to=open_inwoner.configurations.models.CustomFontSet.update_filename_heading,
validators=[
django.core.validators.FileExtensionValidator(["ttf"])
],
verbose_name="Heading font",
),
),
(
"site_configuration",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="custom_fonts",
to="configurations.siteconfiguration",
verbose_name="Configuration",
),
),
],
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Generated by Django 3.2.23 on 2024-01-29 15:45

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("configurations", "0058_customfontset"),
("configurations", "0058_siteconfiguration_recipients_email_digest"),
]

operations = []
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Generated by Django 3.2.23 on 2024-02-08 13:26

import django.core.validators
from django.db import migrations
import open_inwoner.configurations.models
import open_inwoner.utils.files


class Migration(migrations.Migration):

dependencies = [
("configurations", "0059_merge_20240129_1645"),
]

operations = [
migrations.RemoveField(
model_name="customfontset",
name="text_body_font",
),
migrations.AddField(
model_name="customfontset",
name="body_font_bold",
field=open_inwoner.configurations.models.CustomFontField(
blank=True,
help_text="Upload bold text body font. TTF font types only.",
null=True,
storage=open_inwoner.utils.files.OverwriteStorage(),
upload_to=open_inwoner.configurations.models.CustomFontSet.update_filename_body_bold,
validators=[django.core.validators.FileExtensionValidator(["ttf"])],
verbose_name="Body font bold",
),
),
migrations.AddField(
model_name="customfontset",
name="body_font_bold_italic",
field=open_inwoner.configurations.models.CustomFontField(
blank=True,
help_text="Upload bold italic text body font. TTF font types only.",
null=True,
storage=open_inwoner.utils.files.OverwriteStorage(),
upload_to=open_inwoner.configurations.models.CustomFontSet.update_filename_body_bold_italic,
validators=[django.core.validators.FileExtensionValidator(["ttf"])],
verbose_name="Body font bold italic",
),
),
migrations.AddField(
model_name="customfontset",
name="body_font_italic",
field=open_inwoner.configurations.models.CustomFontField(
blank=True,
help_text="Upload italic text body font. TTF font types only.",
null=True,
storage=open_inwoner.utils.files.OverwriteStorage(),
upload_to=open_inwoner.configurations.models.CustomFontSet.update_filename_body_italic,
validators=[django.core.validators.FileExtensionValidator(["ttf"])],
verbose_name="Body font italic",
),
),
migrations.AddField(
model_name="customfontset",
name="body_font_regular",
field=open_inwoner.configurations.models.CustomFontField(
blank=True,
help_text="Upload regular text body font. TTF font types only.",
null=True,
storage=open_inwoner.utils.files.OverwriteStorage(),
upload_to=open_inwoner.configurations.models.CustomFontSet.update_filename_body,
validators=[django.core.validators.FileExtensionValidator(["ttf"])],
verbose_name="Body font regular",
),
),
]
133 changes: 131 additions & 2 deletions src/open_inwoner/configurations/models.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import os
from typing import Optional

from django.conf import settings
from django.contrib.flatpages.models import FlatPage
from django.core.validators import FileExtensionValidator
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.utils.translation import ugettext_lazy as _

from colorfield.fields import ColorField
from django_better_admin_arrayfield.models.fields import ArrayField
from filer.fields.file import FilerFileField
from filer.fields.image import FilerImageField
from ordered_model.models import OrderedModel, OrderedModelManager
from solo.models import SingletonModel

from ..utils.colors import hex_to_hsl
from ..utils.css import clean_stylesheet
from ..utils.fields import CSSField
from ..utils.files import OverwriteStorage
from ..utils.validators import FilerExactImageSizeValidator
from .choices import ColorTypeChoices, OpenIDDisplayChoices
from .choices import ColorTypeChoices, CustomFontName, OpenIDDisplayChoices
from .validators import validate_oidc_config


Expand Down Expand Up @@ -594,6 +598,131 @@ def get_help_text(self, request) -> Optional[str]:
return ""


class CustomFontField(models.FileField):
def __init__(self, file_name: str = "", **kwargs):
self.file_name = file_name
super().__init__(**kwargs)


class CustomFontSet(models.Model):
def update_filename(self, filename: str, new_name: str, path: str) -> str:
ext = filename.split(".")[1]
filename = f"{new_name}.{ext}"
return "{path}/{filename}".format(path=path, filename=filename)

def update_filename_body(self, filename: str) -> str:
return CustomFontSet.update_filename(
self,
filename,
new_name=CustomFontName.body,
path="custom_fonts/",
)

def update_filename_body_italic(self, filename: str) -> str:
return CustomFontSet.update_filename(
self,
filename,
new_name=CustomFontName.body_italic,
path="custom_fonts/",
)

def update_filename_body_bold(self, filename: str) -> str:
return CustomFontSet.update_filename(
self,
filename,
new_name=CustomFontName.body_bold,
path="custom_fonts/",
)

def update_filename_body_bold_italic(self, filename: str) -> str:
return CustomFontSet.update_filename(
self,
filename,
new_name=CustomFontName.body_bold_italic,
path="custom_fonts/",
)

def update_filename_heading(self, filename: str) -> str:
return CustomFontSet.update_filename(
self,
filename,
new_name=CustomFontName.heading,
path="custom_fonts/",
)

heading_font = CustomFontField(
verbose_name=_("Heading font"),
upload_to=update_filename_heading,
file_name=CustomFontName.heading,
storage=OverwriteStorage(),
validators=[FileExtensionValidator(["ttf"])],
blank=True,
null=True,
help_text=_("Upload heading font. TTF font types only."),
)
site_configuration = models.OneToOneField(
SiteConfiguration,
verbose_name=_("Configuration"),
related_name="custom_fonts",
on_delete=models.CASCADE,
)
pi-sigma marked this conversation as resolved.
Show resolved Hide resolved
body_font_regular = CustomFontField(
verbose_name=_("Body font regular"),
upload_to=update_filename_body,
file_name=CustomFontName.body,
storage=OverwriteStorage(),
validators=[FileExtensionValidator(["ttf"])],
blank=True,
null=True,
help_text=_("Upload regular text body font. TTF font types only."),
)
body_font_italic = CustomFontField(
verbose_name=_("Body font italic"),
upload_to=update_filename_body_italic,
file_name=CustomFontName.body_italic,
storage=OverwriteStorage(),
validators=[FileExtensionValidator(["ttf"])],
blank=True,
null=True,
help_text=_("Upload italic text body font. TTF font types only."),
)
body_font_bold = CustomFontField(
verbose_name=_("Body font bold"),
upload_to=update_filename_body_bold,
file_name=CustomFontName.body_bold,
storage=OverwriteStorage(),
validators=[FileExtensionValidator(["ttf"])],
blank=True,
null=True,
help_text=_("Upload bold text body font. TTF font types only."),
)
body_font_bold_italic = CustomFontField(
verbose_name=_("Body font bold italic"),
upload_to=update_filename_body_bold_italic,
file_name=CustomFontName.body_bold_italic,
storage=OverwriteStorage(),
validators=[FileExtensionValidator(["ttf"])],
blank=True,
null=True,
help_text=_("Upload bold italic text body font. TTF font types only."),
)


@receiver(post_save, sender=CustomFontSet)
def remove_orphan_files(sender, instance, *args, **kwargs):
"""
Remove font files corresponding to `CustomFont` fields that have been cleared
"""
custom_fonts_dir = os.path.join(settings.MEDIA_ROOT, "custom_fonts")
font_names = os.listdir(custom_fonts_dir)

for field in sender._meta.concrete_fields:
if isinstance(field, models.FileField) and not getattr(instance, field.name):
for font_name in font_names:
if font_name.startswith(field.file_name):
os.remove(os.path.join(custom_fonts_dir, font_name))


class SiteConfigurationPage(OrderedModel):
configuration = models.ForeignKey(
SiteConfiguration,
Expand Down
Loading
Loading