Files
modoboa/modoboa/lib/parameters.py
T

352 lines
10 KiB
Python

# coding: utf-8
"""
This interface provides a simple way to declare and store parameters
in Modoboa's database.
Core components or extensions can register their own parameters, which
will be available and modifiable directly from the web interface.
Only super users will be able to access this part of the web interface.
"""
from django import forms
from modoboa.lib import events
from modoboa.lib.sysutils import guess_extension_name
from modoboa.lib.exceptions import ModoboaException
_params = {'A': {}, 'U': {}}
class NotDefined(ModoboaException):
http_code = 404
def __init__(self, app, name):
self.app = app
self.name = name
def __str__(self):
return "Application '%s' and/or parameter '%s' not defined" \
% (self.app, self.name)
class GenericParametersForm(forms.Form):
"""Base class for parameter forms.
Each extension has the possibility to define global parameters.
"""
app = None
visibility_rules = None
def __init__(self, *args, **kwargs):
if self.app is None:
raise NotImplementedError
kwargs["prefix"] = self.app
super(GenericParametersForm, self).__init__(*args, **kwargs)
self.visirules = {}
if self.visibility_rules is not None:
for key, rule in self.visibility_rules.items():
field, value = rule.split("=")
visibility = {
"field": "id_%s-%s" % (self.app, field), "value": value
}
self.visirules["%s-%s" % (self.app, key)] = visibility
if not args:
self._load_initial_values()
def _load_initial_values(self):
raise NotImplementedError
def _decode_value(self, value):
return value.decode('unicode_escape').replace('\\r\\n', '\n')
def _load_extra_parameters(self, level):
params = events.raiseDictEvent('GetExtraParameters', self.app, level)
for pname, pdef in params.items():
self.fields[pname] = pdef
def _save_parameter(self, p, name, value):
if p.value == value:
return
name = name.lower()
if hasattr(self, "update_%s" % name):
getattr(self, "update_%s" % name)(value)
if type(value) is unicode:
p.value = value.encode("unicode_escape").strip()
else:
p.value = str(value)
p.save()
def save(self):
raise NotImplementedError
class AdminParametersForm(GenericParametersForm):
def _load_initial_values(self):
from .models import Parameter
names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
for p in Parameter.objects.filter(name__in=names):
self.fields[p.shortname].initial = self._decode_value(p.value)
def save(self):
from .models import Parameter
from modoboa.lib.formutils import SeparatorField
for name, value in self.cleaned_data.items():
if type(self.fields[name]) is SeparatorField:
continue
fullname = "%s.%s" % (self.app, name.upper())
try:
p = Parameter.objects.get(name=fullname)
except Parameter.DoesNotExist:
p = Parameter()
p.name = fullname
self._save_parameter(p, name, value)
def to_django_settings(self):
pass
def get_current_values(self):
values = {}
for key in self.fields.keys():
try:
values[key] = get_admin(key.upper(), app=self.app)
except NotDefined:
pass
return values
class UserParametersForm(GenericParametersForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user") if "user" in kwargs else None
super(UserParametersForm, self).__init__(*args, **kwargs)
def _load_initial_values(self):
if self.user is None:
return
from .models import UserParameter
names = ["%s.%s" % (self.app, name.upper()) for name in self.fields.keys()]
for p in UserParameter.objects.filter(user=self.user, name__in=names):
self.fields[p.shortname].initial = self._decode_value(p.value)
@staticmethod
def has_access(user):
return True
def save(self):
from .models import UserParameter
from modoboa.lib.formutils import SeparatorField
for name, value in self.cleaned_data.items():
if type(self.fields[name]) is SeparatorField:
continue
fullname = "%s.%s" % (self.app, name.upper())
try:
p = UserParameter.objects.get(user=self.user, name=fullname)
except UserParameter.DoesNotExist:
p = UserParameter()
p.user = self.user
p.name = fullname
self._save_parameter(p, name, value)
def register(formclass, label):
"""Register a form class containing parameters
formclass must inherit from ``AdminParametersForm`` or
``UserParametersForm``.
:param formclass: a form class
:param string label: the label to display in parameters or settings pages
"""
from modoboa.lib.formutils import SeparatorField
if issubclass(formclass, AdminParametersForm):
level = 'A'
elif issubclass(formclass, UserParametersForm):
level = 'U'
else:
raise RuntimeError("Unknown parameter class")
_params[level][formclass.app] = {
"label": label, "form": formclass, "defaults": {}
}
form = formclass()
for name, field in form.fields.items():
if type(field) is SeparatorField:
continue
_params[level][formclass.app]["defaults"][name.upper()] = field.initial
def unregister(app=None):
"""Unregister an application
All parameters associated to this application will also be
removed.
:param app: the application's name (string)
"""
if app is None:
app = guess_extension_name()
for lvlparams in _params.values():
if app in lvlparams:
del lvlparams[app]
def __is_defined(app, level, name):
if not level in ['A', 'U'] \
or not app in _params[level] \
or not name in _params[level][app]["defaults"]:
raise NotDefined(app, name)
def save_admin(name, value, app=None):
from .models import Parameter
if app is None:
app = guess_extension_name()
__is_defined(app, 'A', name)
fullname = "%s.%s" % (app, name)
try:
p = Parameter.objects.get(name=fullname)
except Parameter.DoesNotExist:
p = Parameter()
p.name = fullname
p.value = None
f = get_parameter_form('A', name, app)
f()._save_parameter(p, name, value)
def save_user(user, name, value, app=None):
from .models import UserParameter
if app is None:
app = guess_extension_name()
__is_defined(app, 'U', name)
fullname = "%s.%s" % (app, name)
try:
p = UserParameter.objects.get(user=user, name=fullname)
except UserParameter.DoesNotExist:
p = UserParameter()
p.user = user
p.name = fullname
f = get_parameter_form('U', name, app)
f()._save_parameter(p, name, value)
def get_admin(name, app=None, raise_error=True):
"""Return an administrative parameter
A ``NotDefined`` exception if the parameter doesn't exist.
:param name: the parameter's name
:param app: the application owning the parameter
:return: the corresponding value as a string
"""
from .models import Parameter
if app is None:
app = guess_extension_name()
try:
__is_defined(app, "A", name)
except NotDefined:
if raise_error:
raise
return None
try:
p = Parameter.objects.get(name="%s.%s" % (app, name))
except Parameter.DoesNotExist:
return _params["A"][app]["defaults"][name]
return p.value.decode("unicode_escape").replace('\\r\\n', '\n')
def get_user(user, name, app=None, raise_error=True):
"""Return a parameter for a specific user
A ``NotDefined`` exception if the parameter doesn't exist.
:param ``User`` user: the desired user
:param name: the parameter's name
:param app: the application owning the parameter
:return: the corresponding value as a string
"""
from .models import UserParameter
if app is None:
app = guess_extension_name()
try:
__is_defined(app, "U", name)
except NotDefined:
if raise_error:
raise
return None
try:
p = UserParameter.objects.get(user=user, name="%s.%s" % (app, name))
except UserParameter.DoesNotExist:
return _params["U"][app]["defaults"][name]
return p.value.decode("unicode_escape")
def get_sorted_apps(level, first="core"):
"""Retrieve the sorted list of all registerd applications.
:param str level: application level
:param str first: force the first item of the result
:rtype: list
"""
sorted_apps = []
if first in _params[level]:
sorted_apps.append(first)
sorted_apps += sorted(
[app for app in _params[level].keys() if app != first],
key=lambda app: _params[level][app]["label"]
)
return sorted_apps
def get_admin_forms(*args, **kwargs):
"""Get all admin level forms.
Generates an instance of each declared form.
"""
for app in get_sorted_apps('A'):
formdef = _params['A'][app]
yield {"label": formdef["label"],
"form": formdef["form"](*args, **kwargs)}
def get_user_forms(user, *args, **kwargs):
kwargs["user"] = user
sorted_apps = get_sorted_apps('U', first="general")
def realfunc():
for app in sorted_apps:
formdef = _params['U'][app]
if not formdef["form"].has_access(user):
continue
yield {"label": formdef["label"],
"form": formdef["form"](*args, **kwargs)}
return realfunc
def get_parameter_form(level, name, app=None):
"""Return the form containing a specific parameter
:param string level: associated level
:param string name: paremeter's name
:param string app: parameter's application
:return: a form class
"""
if app is None:
app = guess_extension_name()
__is_defined(app, level, name)
return _params[level][app]["form"]
def apply_to_django_settings():
for form in get_admin_forms():
form["form"].to_django_settings()