diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py new file mode 100644 index 0000000000000000000000000000000000000000..2885a646cf0cc83ec47f46bc2ba5438886ef1952 --- /dev/null +++ b/aleksis/core/preferences.py @@ -0,0 +1,128 @@ +from django.conf import settings +from django.forms import EmailField, URLField +from django.utils.translation import gettext_lazy as _ + +from colorfield.widgets import ColorWidget +from dynamic_preferences.types import BooleanPreference, StringPreference +from dynamic_preferences.preferences import Section +from dynamic_preferences.registries import global_preferences_registry +from dynamic_preferences.users.registries import user_preferences_registry + + +general = Section("general") +theme = Section(_("theme") +mail = Section("mail") +notification = Section("notification") +footer = Section("footer") +account = Section("account") + + +@global_preferences_registry.register +class SiteTitle(StringPreference): + section = general + name = "title" + default = "AlekSIS" + required = False + verbose_name = _("Site title") + + +@global_preferences_registry.register +class SiteDescription(StringPreference): + section = general + name = "description" + default = "The Free School Information System" + required = False + verbose_name = _("Site description") + + +@global_preferences_registry.register +class ColourPrimary(StringPreference): + section = theme + name = "primary" + default = "#0d5eaf" + required = False + verbose_name = _("Primary colour") + widget = ColorWidget + + +@global_preferences_registry.register +class ColourSecondary(StringPreference): + section = theme + name = "secondary" + default = "#0d5eaf" + required = False + verbose_name = _("Secondary colour") + widget = ColorWidget + + +@global_preferences_registry.register +class MailOutName(StringPreference): + section = mail + name = "name" + default = "AlekSIS" + required = False + verbose_name = _("Mail out name") + + +@global_preferences_registry.register +class MailOut(StringPreference): + section = mail + name = "name" + default = settings.DEFAULT_FROM_EMAIL + required = False + verbose_name = _("Mail out address") + widget = EmailField + + +@global_preferences_registry.register +class PrivacyURL(StringPreference): + section = footer + name = "privacy_url" + default = "" + required = False + verbose_name = _("Link to privacy policy") + widget = URLField + + +@global_preferences_registry.register +class ImprintURL(StringPreference): + section = footer + name = "imprint_url" + default = "" + required = False + verbose_name = _("Link to imprint") + widget = URLField + + +@user_preferences_registry.register +class AdressingNameFormat(ChoicePreference): + section = notification + name = "addressing_name_format" + default = "german" + required = False + verbose_name = _("Name format for addressing") + choices = ( + (None, "-----"), + ("german", "John Doe"), + ("english", "Doe, John"), + ("dutch", "Doe John"), + ) + + +@user_preferences_registry.register +class NotificationChannels(MultipleChoicePreference): + section = notification + name = "channels" + default = ["email"] + required = False + verbose_name = _("Channels to use for notifications") + choices = get_notification_choices_lazy + + +@global_preferences_registry.register +class PrimaryGroupPattern(StringPreference): + section = account + name = "primary_group_pattern" + default = "" + required = False + verbose_name = _("Regular expression to match primary group, e.g. '^Class .*'") diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index 6fd8e14951bb0585f2138a37fe384d726f0b4ccc..42c9a0a4c174dca78fbb0f48a75d132ebc9b450c 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -5,13 +5,11 @@ from importlib import import_module from django.apps import apps from django.utils.translation import gettext_lazy as _ -from calendarweek.django import i18n_day_name_choices_lazy from dynaconf import LazySettings from easy_thumbnails.conf import Settings as thumbnail_settings from .util.core_helpers import get_app_packages, lazy_config, merge_app_settings -from .util.notifications import get_notification_choices_lazy ENVVAR_PREFIX_FOR_DYNACONF = "ALEKSIS" DIRS_FOR_DYNACONF = ["/etc/aleksis"] @@ -66,9 +64,9 @@ INSTALLED_APPS = [ "settings_context_processor", "sass_processor", "easyaudit", - "constance", - "constance.backends.database", "django_any_js", + "dynamic_preferences", + "dynamic_preferences.users.apps.UserPreferencesConfig", "django_yarnpkg", "django_tables2", "easy_thumbnails", @@ -142,7 +140,7 @@ TEMPLATES = [ "django.contrib.messages.context_processors.messages", "maintenance_mode.context_processors.maintenance_mode", "settings_context_processor.context_processors.settings", - "constance.context_processors.config", + "dynamic_preferences.processors.global_preferences", "aleksis.core.util.core_helpers.custom_information_processor", ], }, @@ -348,64 +346,9 @@ TEMPLATED_EMAIL_AUTO_PLAIN = True TEMPLATE_VISIBLE_SETTINGS = ["ADMINS", "DEBUG"] -CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend" -CONSTANCE_ADDITIONAL_FIELDS = { - "char_field": ["django.forms.CharField", {}], - "image_field": ["django.forms.ImageField", {}], - "email_field": ["django.forms.EmailField", {}], - "url_field": ["django.forms.URLField", {}], - "integer_field": ["django.forms.IntegerField", {}], - "password_field": ["django.forms.CharField", { - 'widget': 'django.forms.PasswordInput', - }], - "adressing-select": ['django.forms.fields.ChoiceField', { - 'widget': 'django.forms.Select', - 'choices': ((None, "-----"), - # ("german", _("<first name>") + " " + _("<last name>")), - # ("english", _("<last name>") + ", " + _("<first name>")), - # ("netherlands", _("<last name>") + " " + _("<first name>")), - ("german", "John Doe"), - ("english", "Doe, John"), - ("dutch", "Doe John"), - ) - }], - "notifications-select": ["django.forms.fields.MultipleChoiceField", { - "widget": "django.forms.CheckboxSelectMultiple", - "choices": get_notification_choices_lazy, - }], - "weekday_field": ["django.forms.fields.ChoiceField", { - 'widget': 'django.forms.Select', - "choices": i18n_day_name_choices_lazy - }], - "colour_field": ["django.forms.CharField", { - "widget": "colorfield.widgets.ColorWidget" - }], +DYNAMIC_PREFERENCES = { + "REGISTRY_MODULE": "preferences", } -CONSTANCE_CONFIG = { - "SITE_TITLE": ("AlekSIS", _("Site title"), "char_field"), - "SITE_DESCRIPTION": ("The Free School Information System", _("Site description")), - "COLOUR_PRIMARY": ("#0d5eaf", _("Primary colour"), "colour_field"), - "COLOUR_SECONDARY": ("#0d5eaf", _("Secondary colour"), "colour_field"), - "MAIL_OUT_NAME": ("AlekSIS", _("Mail out name")), - "MAIL_OUT": (DEFAULT_FROM_EMAIL, _("Mail out address"), "email_field"), - "PRIVACY_URL": ("", _("Link to privacy policy"), "url_field"), - "IMPRINT_URL": ("", _("Link to imprint"), "url_field"), - "ADRESSING_NAME_FORMAT": ("german", _("Name format of adresses"), "adressing-select"), - "NOTIFICATION_CHANNELS": (["email"], _("Channels to allow for notifications"), "notifications-select"), - "PRIMARY_GROUP_PATTERN": ("", _("Regular expression to match primary group, e.g. '^Class .*'"), str), -} -CONSTANCE_CONFIG_FIELDSETS = { - "General settings": ("SITE_TITLE", "SITE_DESCRIPTION"), - "Theme settings": ("COLOUR_PRIMARY", "COLOUR_SECONDARY"), - "Mail settings": ("MAIL_OUT_NAME", "MAIL_OUT"), - "Notification settings": ("NOTIFICATION_CHANNELS", "ADRESSING_NAME_FORMAT"), - "Footer settings": ("PRIVACY_URL", "IMPRINT_URL"), - "Account settings": ("PRIMARY_GROUP_PATTERN",), -} - -merge_app_settings("CONSTANCE_ADDITIONAL_FIELDS", CONSTANCE_ADDITIONAL_FIELDS, False) -merge_app_settings("CONSTANCE_CONFIG", CONSTANCE_CONFIG, False) -merge_app_settings("CONSTANCE_CONFIG_FIELDSETS", CONSTANCE_CONFIG_FIELDSETS, False) MAINTENANCE_MODE = _settings.get("maintenance.enabled", None) MAINTENANCE_MODE_IGNORE_IP_ADDRESSES = _settings.get( diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index 13558233068626c8f288b70624b2646840826819..797b8e25c5dac0a1532619a119234043f79796c4 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -13,6 +13,8 @@ from django.http import HttpRequest from django.utils import timezone from django.utils.functional import lazy +from dynamic_preferences.registries import global_preferences_registry + def copyright_years(years: Sequence[int], seperator: str = ", ", joiner: str = "–") -> str: """ Takes a sequence of integegers and produces a string with ranges @@ -87,17 +89,17 @@ def merge_app_settings(setting: str, original: Union[dict, list], deduplicate: b def lazy_config(key: str) -> Callable[[str], Any]: - """ Lazily get a config value from constance. Useful to bind constance - configs to other global settings to make them available to third-party - apps that are not aware of constance. + """ Lazily get a config value from dynamic preferences. Useful to bind preferences + to other global settings to make them available to third-party apps that are not + aware of dynamic preferences. """ def _get_config(key: str) -> Any: - from constance import config # noqa - return getattr(config, key) + return global_preferences[key] # The type is guessed from the default value to improve lazy()'s behaviour - return lazy(_get_config, type(settings.CONSTANCE_CONFIG[key][0]))(key) + # FIXME Reintroduce the behaviour described above + return lazy(_get_config, str)(key) def is_impersonate(request: HttpRequest) -> bool: diff --git a/pyproject.toml b/pyproject.toml index 118325ec2e47142790983685cc36bb77531ccf03..2dc251884b48139eb293f65e62c8986803cc0394 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ django-two-factor-auth = { version = "^1.10.0", extras = [ "YubiKey", "phonenumb django-yarnpkg = "^6.0" django-material = "^1.6.0" django-pwa = "^1.0.8" -django-constance = { version = "^2.6.0", extras = ["database"] } +django-dynamic-preferences = "^1.8.1" django_widget_tweaks = "^1.4.5" django-filter = "^2.2.0" django-templated-email = "^2.3.0"