From c7e1e59bc338e290aebfdc859ff8f0858b567dc1 Mon Sep 17 00:00:00 2001 From: Dominik George <dominik.george@teckids.org> Date: Fri, 10 Jan 2020 15:43:34 +0100 Subject: [PATCH] Make app settings merging generic Implements a merge_app_settings() function that merges a named setting from all installed apps into the original setting in core. --- aleksis/core/settings.py | 12 ++++++++- aleksis/core/util/core_helpers.py | 42 +++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py index bd0f30cff..28b637465 100644 --- a/aleksis/core/settings.py +++ b/aleksis/core/settings.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _ from dynaconf import LazySettings from easy_thumbnails.conf import Settings as thumbnail_settings -from .util.core_helpers import get_app_packages +from .util.core_helpers import get_app_packages, merge_app_settings ENVVAR_PREFIX_FOR_DYNACONF = "ALEKSIS" DIRS_FOR_DYNACONF = ["/etc/aleksis"] @@ -77,6 +77,7 @@ INSTALLED_APPS = [ "pwa", ] +merge_app_settings("INSTALLED_APPS", INSTALLED_APPS, True) INSTALLED_APPS += get_app_packages() STATICFILES_FINDERS = [ @@ -150,6 +151,8 @@ DATABASES = { } } +merge_app_settings("DATABASES", DATABASES, False) + if _settings.get("caching.memcached.enabled", True): CACHES = { "default": { @@ -239,6 +242,8 @@ YARN_INSTALLED_APPS = [ "select2", ] +merge_app_settings("YARN_INSTALLED_APPS", YARN_INSTALLED_APPS, True) + JS_URL = _settings.get("js_assets.url", STATIC_URL) JS_ROOT = _settings.get("js_assets.root", NODE_MODULES_ROOT + "/node_modules") @@ -255,6 +260,8 @@ ANY_JS = { }, } +merge_app_settings("ANY_JS", ANY_JS, True) + SASS_PROCESSOR_AUTO_INCLUDE = False SASS_PROCESSOR_CUSTOM_FUNCTIONS = { "get-colour": "aleksis.core.util.sass_helpers.get_colour", @@ -302,6 +309,9 @@ CONSTANCE_CONFIG_FIELDSETS = { "Footer settings": ("PRIVACY_URL", "IMPRINT_URL"), } +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( "maintenance.ignore_ips", _settings.get("maintenance.internal_ips", []) diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py index 4fb88fa49..e2cdd439d 100644 --- a/aleksis/core/util/core_helpers.py +++ b/aleksis/core/util/core_helpers.py @@ -30,19 +30,41 @@ def get_app_packages() -> Sequence[str]: except ImportError: return [] - pkgs = [] - for pkg in pkgutil.iter_modules(aleksis.apps.__path__): - mod = import_module("aleksis.apps.%s" % pkg[1]) + return ["aleksis.apps.%s" % pkg[1] for pkg in pkgutil.iter_modules(aleksis.apps.__path__)] - # Add additional apps defined in module's INSTALLED_APPS constant - additional_apps = getattr(mod, "INSTALLED_APPS", []) - for app in additional_apps: - if app not in pkgs: - pkgs.append(app) - pkgs.append("aleksis.apps.%s" % pkg[1]) +def merge_app_settings(setting: str, original: Union[dict, list], deduplicate: bool = False) -> Union[dict, list]: + """ Get a named settings constant from all apps and merge it into the original. + To use this, add a settings.py file to the app, in the same format as Django's + main settings.py. - return pkgs + Note: Only selected names will be imported frm it to minimise impact of + potentially malicious apps! + """ + + for pkg in get_app_packages(): + try: + mod_settings = import_module(pkg) + except ImportError: + # Import errors are non-fatal. They mean that the app has no settings.py. + continue + + app_setting = getattr(mod_settings, setting, None) + if not app_setting: + # The app might not have this setting or it might be empty. Ignore it in that case. + continue + + for entry in app_setting: + if entry in original: + if not deduplicate: + raise AttributeError("%s already set in original.") + else: + if isinstance(original, list): + original.append(entry) + elif isinstance(original, dict): + original[entry] = app_setting[entry] + else: + raise TypeError("Only dict and list settings can be merged.") def is_impersonate(request: HttpRequest) -> bool: -- GitLab