Files
mayan-edms/mayan/apps/smart_settings/utils.py
Roberto Rosario fbcd424474 Add MediaBootstrapSetting class
Used for settings that depend on the value
of the MEDIA_ROOT setting.

Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
2019-11-16 02:47:36 -04:00

389 lines
12 KiB
Python

from __future__ import unicode_literals
import errno
import os
import yaml
from mayan.apps.common.serialization import yaml_load
from .literals import (
CONFIGURATION_FILENAME, CONFIGURATION_LAST_GOOD_FILENAME
)
class SettingNamespaceSingleton(object):
"""
Self hosting bootstrap setting class.
Allow managing setting in a compatible way before Mayan EDMS starts.
"""
_setting_kwargs = {}
_settings = {}
class SettingNotFound(Exception):
"""Mostly a stand-in or typecast for KeyError for readability."""
@classmethod
def load_config_file(cls, filepath):
try:
with open(filepath) as file_object:
file_object.seek(0, os.SEEK_END)
if file_object.tell():
file_object.seek(0)
try:
return yaml_load(stream=file_object)
except yaml.YAMLError as exception:
exit(
'Error loading configuration file: {}; {}'.format(
filepath, exception
)
)
except IOError as exception:
if exception.errno == errno.ENOENT:
# No config file, return empty dictionary
return {}
else:
raise
@classmethod
def register_setting(cls, name, klass, kwargs=None):
cls._settings[name] = klass
cls._setting_kwargs[name] = kwargs or {}
def __init__(self, global_symbol_table):
self.global_symbol_table = global_symbol_table
self.settings = {}
for name, klass in self.__class__._settings.items():
kwargs = self.__class__._setting_kwargs[name].copy()
kwargs['name'] = name
setting = klass(**kwargs)
setting.namespace = self
self.settings[name] = setting
def get_config_file_setting(self, name):
"""
Wrapper for load_config_file to cache the result.
"""
if hasattr(self, '_cache_file_data'):
file_data = self._cache_file_data
else:
filepath = self.get_setting_value(
name='CONFIGURATION_FILEPATH'
)
file_data = self.load_config_file(filepath=filepath)
self._cache_file_data = file_data
try:
return file_data[name]
except KeyError:
raise SettingNamespaceSingleton.SettingNotFound
def get_setting_value(self, name):
"""
Wrapper that calls the individual setting .get_value method.
Convenience method to allow returning setting values from the
namespace.
"""
try:
return self.settings[name].get_value()
except KeyError:
raise SettingNamespaceSingleton.SettingNotFound
def get_values(self, only_critical=False):
"""
Return a dictionary will all the settings and their respective
resolved values.
"""
result = {}
for name, setting in self.settings.items():
# If only_critical is set to True load only the settings with
# the critical flag. Otherwise load all.
if only_critical and setting.critical or not only_critical:
try:
result[name] = setting.get_value()
except SettingNamespaceSingleton.SettingNotFound:
"""
Not critical, we just avoid adding it to the result
dictionary.
"""
return result
def update_globals(self, only_critical=False):
"""
Insert all resolved values into the symbol table of the caller.
"""
result = self.get_values(only_critical=only_critical)
self.global_symbol_table.update(result)
class BaseSetting(object):
def __init__(
self, name, critical=False, has_default=False, default_value=None
):
self.critical = critical
self.name = name
self.has_default = has_default
self.default_value = default_value
def _get_environment_value(self):
return os.environ.get(self.get_environment_name())
def get_default_value(self):
return self.default_value
def get_environment_name(self):
return 'MAYAN_{}'.format(self.name)
def get_value(self):
"""
By default will try to get the value from the namespace symbol table,
then the configuration file, and finally from the environment.
"""
try:
return self.namespace.global_symbol_table.get(
self.name, self.namespace.get_config_file_setting(name=self.name)
)
except SettingNamespaceSingleton.SettingNotFound:
return self.load_environment_value()
def load_environment_value(self):
value = self._get_environment_value()
if value:
try:
return yaml_load(stream=value)
except yaml.YAMLError as exception:
exit(
'Error loading setting environment value: {}; {}'.format(
self.name, exception
)
)
else:
if self.has_default:
return self.get_default_value()
else:
raise SettingNamespaceSingleton.SettingNotFound
class FilesystemBootstrapSetting(BaseSetting):
def __init__(self, name, critical=False, path_parts=None):
self.critical = critical
self.has_default = True
self.name = name
self.path_parts = path_parts
def get_default_value(self):
"""
The default value of this setting class is not static but calculated.
"""
return os.path.join(
# Can't use BASE_DIR from django.conf.settings
# Use it from the global_symbol_table which should be the same
self.namespace.global_symbol_table.get('BASE_DIR'), *self.path_parts
)
def get_value(self):
"""
It is not possible to look for this setting in the config file
because not even the config file setup has completed.
This setting only supports being set from the environment.
"""
return self.load_environment_value()
class MediaBootstrapSetting(FilesystemBootstrapSetting):
def get_default_value(self):
"""
The default value of this setting class is not static but calculated.
"""
return os.path.join(
self.namespace.get_setting_value(name='MEDIA_ROOT'),
*self.path_parts
)
# FilesystemBootstrapSetting settings
SettingNamespaceSingleton.register_setting(
name='CONFIGURATION_FILEPATH', klass=MediaBootstrapSetting,
kwargs={
'critical': True, 'path_parts': (CONFIGURATION_FILENAME,)
}
)
# MediaBootstrapSetting settings
SettingNamespaceSingleton.register_setting(
name='CONFIGURATION_LAST_GOOD_FILEPATH', klass=MediaBootstrapSetting,
kwargs={
'critical': True, 'path_parts': (CONFIGURATION_LAST_GOOD_FILENAME,)
}
)
SettingNamespaceSingleton.register_setting(
name='MEDIA_ROOT', klass=FilesystemBootstrapSetting, kwargs={
'critical': True, 'path_parts': ('media',)
}
)
# Normal settings
SettingNamespaceSingleton.register_setting(
name='ALLOWED_HOSTS', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': ['127.0.0.1', 'localhost', '[::1]']
}
)
SettingNamespaceSingleton.register_setting(
name='APPEND_SLASH', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='AUTH_PASSWORD_VALIDATORS', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='DATA_UPLOAD_MAX_MEMORY_SIZE', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='DATABASES', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DEBUG', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': False
}
)
SettingNamespaceSingleton.register_setting(
name='DEFAULT_FROM_EMAIL', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='DISALLOWED_USER_AGENTS', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_BACKEND', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_HOST', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_HOST_PASSWORD', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_HOST_USER', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_PORT', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_TIMEOUT', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_USE_SSL', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='EMAIL_USE_TLS', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='FILE_UPLOAD_MAX_MEMORY_SIZE', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='INTERNAL_IPS', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': ['127.0.0.1']
}
)
SettingNamespaceSingleton.register_setting(
name='LOGIN_REDIRECT_URL', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': 'common:home'
}
)
SettingNamespaceSingleton.register_setting(
name='LOGIN_URL', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': 'authentication:login_view'
}
)
SettingNamespaceSingleton.register_setting(
name='LOGOUT_REDIRECT_URL', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': 'authentication:login_view'
}
)
SettingNamespaceSingleton.register_setting(
name='LANGUAGES', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='LANGUAGE_CODE', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='STATIC_URL', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='STATICFILES_STORAGE', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='TIME_ZONE', klass=BaseSetting,
)
SettingNamespaceSingleton.register_setting(
name='WSGI_APPLICATION', klass=BaseSetting,
)
# Celery
SettingNamespaceSingleton.register_setting(
name='CELERY_TASK_ALWAYS_EAGER', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': True
}
)
SettingNamespaceSingleton.register_setting(
name='CELERY_BROKER_URL', klass=BaseSetting
)
SettingNamespaceSingleton.register_setting(
name='CELERY_RESULT_BACKEND', klass=BaseSetting
)
# Mayan
SettingNamespaceSingleton.register_setting(
name='COMMON_DISABLED_APPS', klass=BaseSetting, kwargs={
'critical': True, 'has_default': True, 'default_value': ()
}
)
SettingNamespaceSingleton.register_setting(
name='COMMON_EXTRA_APPS', klass=BaseSetting, kwargs={
'critical': True, 'has_default': True, 'default_value': ()
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_ENGINE', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_NAME', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_USER', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_PASSWORD', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_HOST', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_PORT', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)
SettingNamespaceSingleton.register_setting(
name='DATABASE_CONN_MAX_AGE', klass=BaseSetting, kwargs={
'has_default': True, 'default_value': None
}
)