mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-18 05:51:26 +02:00
WIP.
This commit is contained in:
@@ -75,10 +75,6 @@ class AccountViewSet(viewsets.ModelViewSet):
|
||||
queryset = queryset.filter(mailbox__domain__name=domain)
|
||||
return queryset
|
||||
|
||||
def perform_destroy(self, instance):
|
||||
"""Add custom args to delete call."""
|
||||
instance.delete(self.request.user)
|
||||
|
||||
@detail_route(methods=["put"])
|
||||
def password(self, request, pk=None):
|
||||
"""Change account password."""
|
||||
|
||||
@@ -18,7 +18,7 @@ ADMIN_EVENTS = [
|
||||
"DomainDeleted",
|
||||
"DomainOwnershipRemoved",
|
||||
"GetTagsForDomain",
|
||||
"ExtraDomainMenuEntries",
|
||||
|
||||
"ExtraDomainFilters",
|
||||
"ExtraDomainQsetFilters",
|
||||
"ExtraDomainTypes",
|
||||
|
||||
@@ -1,208 +1,7 @@
|
||||
"""Event callbacks for modoboa admin."""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.lib.exceptions import PermDeniedException, BadRequest, Conflict
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .models import (
|
||||
Domain, DomainAlias, Mailbox, Alias
|
||||
)
|
||||
|
||||
|
||||
@events.observe("AdminMenuDisplay")
|
||||
def admin_menu(target, user):
|
||||
if target != "top_menu":
|
||||
return []
|
||||
entries = []
|
||||
if user.has_perm("admin.view_domains"):
|
||||
entries += [
|
||||
{"name": "domains",
|
||||
"url": reverse("admin:domain_list"),
|
||||
"label": _("Domains")}
|
||||
]
|
||||
conditions = (
|
||||
user.has_perm("core.add_user"),
|
||||
user.has_perm("admin.add_alias")
|
||||
)
|
||||
if any(conditions):
|
||||
entries += [
|
||||
{"name": "identities",
|
||||
"url": reverse("admin:identity_list"),
|
||||
"label": _("Identities")},
|
||||
]
|
||||
return entries
|
||||
|
||||
|
||||
@events.observe("UserMenuDisplay")
|
||||
def user_menu(target, user):
|
||||
if target != "uprefs_menu":
|
||||
return []
|
||||
if not hasattr(user, "mailbox"):
|
||||
return []
|
||||
return [
|
||||
{"name": "forward",
|
||||
"class": "ajaxnav",
|
||||
"url": "forward/",
|
||||
"label": ugettext_lazy("Forward")}
|
||||
]
|
||||
|
||||
|
||||
@events.observe("RoleChanged")
|
||||
def grant_access_to_all_objects(user, role):
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from modoboa.lib.permissions import grant_access_to_objects
|
||||
from modoboa.core.models import User
|
||||
|
||||
if role != "SuperAdmins":
|
||||
return
|
||||
grant_access_to_objects(
|
||||
user, User.objects.all(),
|
||||
ContentType.objects.get_for_model(User)
|
||||
)
|
||||
grant_access_to_objects(
|
||||
user, Domain.objects.all(),
|
||||
ContentType.objects.get_for_model(Domain)
|
||||
)
|
||||
grant_access_to_objects(
|
||||
user, DomainAlias.objects.all(),
|
||||
ContentType.objects.get_for_model(DomainAlias)
|
||||
)
|
||||
grant_access_to_objects(
|
||||
user, Mailbox.objects.all(),
|
||||
ContentType.objects.get_for_model(Mailbox)
|
||||
)
|
||||
grant_access_to_objects(
|
||||
user, Alias.objects.all(),
|
||||
ContentType.objects.get_for_model(Alias)
|
||||
)
|
||||
|
||||
|
||||
@events.observe("AccountExported")
|
||||
def export_admin_domains(admin):
|
||||
result = [admin.mailbox.quota] \
|
||||
if hasattr(admin, "mailbox") else [""]
|
||||
if admin.role != "DomainAdmins":
|
||||
return result
|
||||
return result + [dom.name for dom in Domain.objects.get_for_admin(admin)]
|
||||
|
||||
|
||||
@events.observe("AccountImported")
|
||||
def import_account_mailbox(user, account, row):
|
||||
"""Handle extra fields when an account is imported.
|
||||
|
||||
Expected fields:
|
||||
|
||||
email address; quota; [domain; ...]
|
||||
|
||||
:param User user: user importing the account
|
||||
:param User account: account being imported
|
||||
:param list rom: list of fields (strings)
|
||||
"""
|
||||
account.email = row[0].strip()
|
||||
if account.email:
|
||||
mailbox, domname = split_mailbox(account.email)
|
||||
try:
|
||||
domain = Domain.objects.get(name=domname)
|
||||
except Domain.DoesNotExist:
|
||||
raise BadRequest(
|
||||
_("Account import failed (%s): domain does not exist"
|
||||
% account.username)
|
||||
)
|
||||
if not user.can_access(domain):
|
||||
raise PermDeniedException
|
||||
core_signals.can_create_object.send(
|
||||
sender="import", context=user, object_type="mailboxes")
|
||||
core_signals.can_create_object.send(
|
||||
sender="import", context=domain, object_type="mailboxes")
|
||||
account.save()
|
||||
if Mailbox.objects.filter(address=mailbox, domain=domain).exists():
|
||||
raise Conflict(
|
||||
_("Mailbox {} already exists").format(account.email))
|
||||
if len(row) == 1:
|
||||
quota = None
|
||||
else:
|
||||
try:
|
||||
quota = int(row[1].strip())
|
||||
except ValueError:
|
||||
raise BadRequest(
|
||||
_("Account import failed (%s): wrong quota value"
|
||||
% account.username)
|
||||
)
|
||||
use_domain_quota = True if not quota else False
|
||||
mb = Mailbox(address=mailbox, domain=domain,
|
||||
user=account, use_domain_quota=use_domain_quota)
|
||||
mb.set_quota(
|
||||
quota, override_rules=user.has_perm("admin.change_domain")
|
||||
)
|
||||
mb.save(creator=user)
|
||||
if account.role == "DomainAdmins":
|
||||
for domname in row[2:]:
|
||||
try:
|
||||
dom = Domain.objects.get(name=domname.strip())
|
||||
except Domain.DoesNotExist:
|
||||
continue
|
||||
dom.add_admin(account)
|
||||
|
||||
|
||||
@events.observe("AccountAutoCreated")
|
||||
def account_auto_created(user):
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
from .lib import check_if_domain_exists
|
||||
|
||||
if not param_tools.get_global_parameter("auto_create_domain_and_mailbox"):
|
||||
return
|
||||
localpart, domname = split_mailbox(user.username)
|
||||
if user.role != 'SimpleUsers' and domname is None:
|
||||
return
|
||||
sadmins = User.objects.filter(is_superuser=True)
|
||||
try:
|
||||
domain = Domain.objects.get(name=domname)
|
||||
except Domain.DoesNotExist:
|
||||
label = check_if_domain_exists(
|
||||
domname, [(DomainAlias, _('domain alias'))])
|
||||
if label is not None:
|
||||
return
|
||||
domain = Domain(name=domname, enabled=True, quota=0)
|
||||
domain.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, domain)
|
||||
try:
|
||||
mb = Mailbox.objects.get(domain=domain, address=localpart)
|
||||
except Mailbox.DoesNotExist:
|
||||
mb = Mailbox(
|
||||
address=localpart, domain=domain, user=user, use_domain_quota=True
|
||||
)
|
||||
mb.set_quota(override_rules=True)
|
||||
mb.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, mb)
|
||||
|
||||
|
||||
@events.observe("UserLogin")
|
||||
def user_logged_in(request, username, password):
|
||||
from modoboa.lib.cryptutils import encrypt
|
||||
|
||||
if hasattr(request.user, "mailbox"):
|
||||
request.session["password"] = encrypt(password)
|
||||
|
||||
|
||||
@events.observe("AccountDeleted")
|
||||
def account_deleted(account, byuser, **kwargs):
|
||||
"""'AccountDeleted' listener.
|
||||
|
||||
When an account is deleted, we also need to remove its mailbox (if
|
||||
any).
|
||||
"""
|
||||
if not hasattr(account, "mailbox"):
|
||||
return
|
||||
mb = account.mailbox
|
||||
if not byuser.can_access(mb):
|
||||
raise PermDeniedException
|
||||
keep_mb_dir = kwargs.get("keep_mb_dir", True)
|
||||
mb.delete(keepdir=keep_mb_dir)
|
||||
|
||||
@@ -229,10 +229,14 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
"quota": 3
|
||||
}
|
||||
self.extra_fields = []
|
||||
result = events.raiseQueryEvent('ExtraFormFields', 'mailform', self.mb)
|
||||
for fname, field in result:
|
||||
self.fields[fname] = field
|
||||
self.extra_fields.append(fname)
|
||||
results = core_signals.extra_form_fields.send(
|
||||
sender=self.__class__, form="mailform", instance=self.mb)
|
||||
if results:
|
||||
results = reduce(
|
||||
lambda a, b: a + b, [result for result in results])
|
||||
for fname, field in results:
|
||||
self.fields[fname] = field
|
||||
self.extra_fields.append(fname)
|
||||
if self.mb is not None:
|
||||
self.fields["email"].required = True
|
||||
qset = self.mb.aliasrecipient_set.filter(alias__internal=False)
|
||||
@@ -380,9 +384,9 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
self.cleaned_data["use_domain_quota"] = (
|
||||
self.cleaned_data["quota_act"])
|
||||
self.mb.update_from_dict(user, self.cleaned_data)
|
||||
events.raiseEvent(
|
||||
'SaveExtraFormFields', 'mailform', self.mb, self.cleaned_data
|
||||
)
|
||||
core_signals.extra_form_fields.send(
|
||||
sender=self.__class__, form="mailform", instance=self.mb,
|
||||
cleaned_data=self.cleaned_data)
|
||||
|
||||
account.email = self.cleaned_data["email"]
|
||||
account.save()
|
||||
|
||||
@@ -193,8 +193,9 @@ class DomainFormOptions(forms.Form):
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(DomainFormOptions, self).__init__(*args, **kwargs)
|
||||
result = events.raiseQueryEvent("UserCanSetRole", user, "DomainAdmins")
|
||||
if False in result:
|
||||
results = core_signals.user_can_set_role.send(
|
||||
sender=self.__class__, user=user, role="DomainAdmins")
|
||||
if False in [result[1] for result in results]:
|
||||
self.fields = {}
|
||||
return
|
||||
|
||||
|
||||
+186
-2
@@ -1,10 +1,22 @@
|
||||
"""Django signal handlers for admin."""
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import signals
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from modoboa.core import models as core_models
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib.cryptutils import encrypt
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
from modoboa.lib import exceptions
|
||||
from modoboa.lib import permissions
|
||||
from modoboa.lib import signals as lib_signals
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import lib
|
||||
from . import models
|
||||
from . import postfix_maps
|
||||
|
||||
@@ -74,7 +86,7 @@ def manage_alias_for_mailbox(sender, instance, **kwargs):
|
||||
|
||||
@receiver(signals.pre_delete, sender=models.Mailbox)
|
||||
def mailbox_deleted_handler(sender, **kwargs):
|
||||
"""``Mailbox`` pre_delete signal receiver
|
||||
"""``Mailbox`` pre_delete signal receiver.
|
||||
|
||||
In order to properly handle deletions (ie. we don't want to leave
|
||||
orphan records into the db), we define this custom receiver.
|
||||
@@ -85,7 +97,7 @@ def mailbox_deleted_handler(sender, **kwargs):
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.permissions import ungrant_access_to_object
|
||||
|
||||
mb = kwargs['instance']
|
||||
mb = kwargs["instance"]
|
||||
events.raiseEvent("MailboxDeleted", mb)
|
||||
ungrant_access_to_object(mb)
|
||||
for ralias in mb.aliasrecipient_set.select_related("alias"):
|
||||
@@ -93,6 +105,15 @@ def mailbox_deleted_handler(sender, **kwargs):
|
||||
ralias.delete()
|
||||
if not alias.aliasrecipient_set.exists():
|
||||
alias.delete()
|
||||
models.Quota.objects.filter(username=mb.full_address).delete()
|
||||
request = lib_signals.get_request()
|
||||
if not request.localconfig.parameters.get_value(
|
||||
"handle_mailboxes", raise_exception=False):
|
||||
return
|
||||
keepdir = request.POST.get("keepdir", "false") == "true"
|
||||
if keepdir:
|
||||
return
|
||||
mb.delete_dir()
|
||||
|
||||
|
||||
@receiver(signals.post_delete, sender=models.Mailbox)
|
||||
@@ -111,3 +132,166 @@ def register_postfix_maps(sender, **kwargs):
|
||||
postfix_maps.SenderLoginAliasMap, postfix_maps.SenderLoginMailboxMap,
|
||||
postfix_maps.SenderLoginMailboxExtraMap
|
||||
]
|
||||
|
||||
|
||||
@receiver(core_signals.account_auto_created)
|
||||
def account_auto_created(sender, user, **kwargs):
|
||||
"""New account has been auto-created, build the rest."""
|
||||
if not param_tools.get_global_parameter("auto_create_domain_and_mailbox"):
|
||||
return
|
||||
localpart, domname = split_mailbox(user.username)
|
||||
if user.role != 'SimpleUsers' and domname is None:
|
||||
return
|
||||
sadmins = core_models.User.objects.filter(is_superuser=True)
|
||||
try:
|
||||
domain = models.Domain.objects.get(name=domname)
|
||||
except models.Domain.DoesNotExist:
|
||||
label = lib.check_if_domain_exists(
|
||||
domname, [(models.DomainAlias, _("domain alias"))])
|
||||
if label is not None:
|
||||
return
|
||||
domain = models.Domain(name=domname, enabled=True, quota=0)
|
||||
domain.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
permissions.grant_access_to_object(su, domain)
|
||||
qset = models.Mailbox.objects.filter(domain=domain, address=localpart)
|
||||
if not qset.exists():
|
||||
mb = models.Mailbox(
|
||||
address=localpart, domain=domain, user=user, use_domain_quota=True
|
||||
)
|
||||
mb.set_quota(override_rules=True)
|
||||
mb.save(creator=sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
permissions.grant_access_to_object(su, mb)
|
||||
|
||||
|
||||
@receiver(core_signals.account_exported)
|
||||
def export_admin_domains(sender, user, **kwargs):
|
||||
"""Export administered domains too."""
|
||||
result = [user.mailbox.quota] if hasattr(user, "mailbox") else [""]
|
||||
if user.role != "DomainAdmins":
|
||||
return result
|
||||
return result + [
|
||||
dom.name for dom in models.Domain.objects.get_for_admin(user)]
|
||||
|
||||
|
||||
@receiver(core_signals.account_imported)
|
||||
def import_account_mailbox(sender, user, account, row, **kwargs):
|
||||
"""Handle extra fields when an account is imported.
|
||||
|
||||
Expected fields:
|
||||
|
||||
email address; quota; [domain; ...]
|
||||
|
||||
:param User user: user importing the account
|
||||
:param User account: account being imported
|
||||
:param list rom: list of fields (strings)
|
||||
"""
|
||||
account.email = row[0].strip()
|
||||
if account.email:
|
||||
mailbox, domname = split_mailbox(account.email)
|
||||
domain = models.Domain.objects.filter(name=domname).first()
|
||||
if not domain:
|
||||
raise exceptions.BadRequest(
|
||||
_("Account import failed (%s): domain does not exist")
|
||||
% account.username
|
||||
)
|
||||
if not user.can_access(domain):
|
||||
raise exceptions.PermDeniedException
|
||||
core_signals.can_create_object.send(
|
||||
sender="import", context=user, object_type="mailboxes")
|
||||
core_signals.can_create_object.send(
|
||||
sender="import", context=domain, object_type="mailboxes")
|
||||
account.save()
|
||||
qset = models.Mailbox.objects.filter(address=mailbox, domain=domain)
|
||||
if qset.exists():
|
||||
raise exceptions.Conflict(
|
||||
_("Mailbox {} already exists").format(account.email))
|
||||
if len(row) == 1:
|
||||
quota = None
|
||||
else:
|
||||
try:
|
||||
quota = int(row[1].strip())
|
||||
except ValueError:
|
||||
raise exceptions.BadRequest(
|
||||
_("Account import failed (%s): wrong quota value")
|
||||
% account.username
|
||||
)
|
||||
use_domain_quota = True if not quota else False
|
||||
mb = models.Mailbox(
|
||||
address=mailbox, domain=domain,
|
||||
user=account, use_domain_quota=use_domain_quota)
|
||||
mb.set_quota(
|
||||
quota, override_rules=user.has_perm("admin.change_domain")
|
||||
)
|
||||
mb.save(creator=user)
|
||||
if account.role == "DomainAdmins":
|
||||
for domname in row[2:]:
|
||||
try:
|
||||
dom = models.Domain.objects.get(name=domname.strip())
|
||||
except models.Domain.DoesNotExist:
|
||||
continue
|
||||
dom.add_admin(account)
|
||||
|
||||
|
||||
@receiver(core_signals.extra_admin_menu_entries)
|
||||
def admin_menu(sender, location, user, **kwargs):
|
||||
"""Add extra menu entries for admins."""
|
||||
if location != "top_menu":
|
||||
return []
|
||||
entries = []
|
||||
if user.has_perm("admin.view_domains"):
|
||||
entries += [
|
||||
{"name": "domains",
|
||||
"url": reverse("admin:domain_list"),
|
||||
"label": _("Domains")}
|
||||
]
|
||||
conditions = (
|
||||
user.has_perm("core.add_user"),
|
||||
user.has_perm("admin.add_alias")
|
||||
)
|
||||
if any(conditions):
|
||||
entries += [
|
||||
{"name": "identities",
|
||||
"url": reverse("admin:identity_list"),
|
||||
"label": _("Identities")},
|
||||
]
|
||||
return entries
|
||||
|
||||
|
||||
@receiver(core_signals.extra_user_menu_entries)
|
||||
def user_menu(sender, location, user, **kwargs):
|
||||
"""Add extra menu entries for users."""
|
||||
if location != "uprefs_menu":
|
||||
return []
|
||||
if not hasattr(user, "mailbox"):
|
||||
return []
|
||||
return [
|
||||
{"name": "forward",
|
||||
"class": "ajaxnav",
|
||||
"url": "forward/",
|
||||
"label": _("Forward")}
|
||||
]
|
||||
|
||||
|
||||
@receiver(core_signals.user_login)
|
||||
def user_logged_in(sender, username, password, **kwargs):
|
||||
"""Store user password in session."""
|
||||
request = lib_signals.get_request()
|
||||
if hasattr(request.user, "mailbox"):
|
||||
request.session["password"] = encrypt(password)
|
||||
|
||||
|
||||
@receiver(core_signals.account_role_changed)
|
||||
def grant_access_to_all_objects(sender, account, role, **kwargs):
|
||||
"""Grant all permissions if new role is SuperAdmin."""
|
||||
if role != "SuperAdmins":
|
||||
return
|
||||
perm_models = [
|
||||
core_models.User, models.Domain, models.DomainAlias, models.Mailbox,
|
||||
models.Alias]
|
||||
for model in perm_models:
|
||||
permissions.grant_access_to_objects(
|
||||
account, model.objects.all(),
|
||||
ContentType.objects.get_for_model(model)
|
||||
)
|
||||
|
||||
@@ -75,7 +75,6 @@ class MailboxManager(Manager):
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Mailbox(AdminObject):
|
||||
|
||||
"""User mailbox."""
|
||||
|
||||
address = models.CharField(
|
||||
@@ -326,21 +325,6 @@ class Mailbox(AdminObject):
|
||||
username=self.full_address)
|
||||
super(Mailbox, self).save(*args, **kwargs)
|
||||
|
||||
def delete(self, keepdir=False):
|
||||
"""Custom delete method
|
||||
|
||||
We try to delete the associated quota in the same time (it may
|
||||
has already been removed if we're deleting a domain).
|
||||
|
||||
:param bool keepdir: delete the mailbox home dir on the
|
||||
filesystem or not
|
||||
|
||||
"""
|
||||
Quota.objects.filter(username=self.full_address).delete()
|
||||
if not keepdir:
|
||||
self.delete_dir()
|
||||
super(Mailbox, self).delete()
|
||||
|
||||
reversion.register(Mailbox)
|
||||
|
||||
|
||||
|
||||
@@ -2,10 +2,14 @@
|
||||
|
||||
import django.dispatch
|
||||
|
||||
use_external_recipients = django.dispatch.Signal(providing_args=["recipients"])
|
||||
extra_admin_content = django.dispatch.Signal(
|
||||
providing_args=["user", "location", "currentpage"])
|
||||
extra_domain_actions = django.dispatch.Signal(
|
||||
providing_args=["user", "domain"])
|
||||
extra_domain_dashboard_widgets = django.dispatch.Signal(
|
||||
providing_args=["user", "domain"])
|
||||
extra_domain_menu_entries = django.dispatch.Signal(
|
||||
providing_args=["user"])
|
||||
extra_account_dashboard_widgets = django.dispatch.Signal(
|
||||
providing_args=["user", "account"])
|
||||
use_external_recipients = django.dispatch.Signal(providing_args=["recipients"])
|
||||
|
||||
@@ -6,7 +6,7 @@ from django.template.loader import render_to_string
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from modoboa.lib import events
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib.templatetags.lib_tags import render_link
|
||||
from modoboa.lib.web_utils import render_actions
|
||||
|
||||
@@ -47,7 +47,10 @@ def domains_menu(selection, user, ajax_mode=True):
|
||||
"url": reverse("admin:domain_statistics")}
|
||||
]
|
||||
if user.has_perm("admin.add_domain"):
|
||||
entries += events.raiseQueryEvent("ExtraDomainMenuEntries", user)
|
||||
extra_entries = signals.extra_domain_menu_entries.send(
|
||||
sender="domains_menu", user=user)
|
||||
for entry in extra_entries:
|
||||
entries += entry[1]
|
||||
entries += [
|
||||
{"name": "import",
|
||||
"label": _("Import"),
|
||||
@@ -157,7 +160,11 @@ def identity_actions(user, ident):
|
||||
name = ident.__class__.__name__
|
||||
objid = ident.id
|
||||
if name == "User":
|
||||
actions = events.raiseQueryEvent("ExtraAccountActions", ident)
|
||||
actions = []
|
||||
result = core_signals.extra_account_actions.send(
|
||||
sender="identity_actions", account=ident)
|
||||
for action in result:
|
||||
actions += result[1]
|
||||
url = (
|
||||
reverse("admin:account_change", args=[objid]) +
|
||||
"?active_tab=default"
|
||||
@@ -265,7 +272,10 @@ def gender(value, target):
|
||||
|
||||
@register.simple_tag
|
||||
def get_extra_admin_content(user, target, currentpage):
|
||||
res = events.raiseQueryEvent(
|
||||
"ExtraAdminContent", user, target, currentpage
|
||||
)
|
||||
return mark_safe("".join(res))
|
||||
results = signals.extra_admin_content.send(
|
||||
sender="get_extra_admin_content",
|
||||
user=user, location=target, currentpage=currentpage)
|
||||
if not results:
|
||||
return ""
|
||||
results = reduce(lambda a, b: a + b, [result[1] for result in results])
|
||||
return mark_safe("".join(results))
|
||||
|
||||
@@ -188,8 +188,7 @@ def editaccount(request, pk):
|
||||
@login_required
|
||||
@permission_required("core.delete_user")
|
||||
def delaccount(request, pk):
|
||||
keepdir = True if request.POST.get("keepdir", "false") == "true" else False
|
||||
User.objects.get(pk=pk).delete(request.user, keep_mb_dir=keepdir)
|
||||
User.objects.get(pk=pk).delete()
|
||||
return render_to_json_response(
|
||||
ungettext("Account deleted", "Accounts deleted", 1)
|
||||
)
|
||||
|
||||
@@ -1,67 +1,3 @@
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.lib import events
|
||||
|
||||
|
||||
PERMISSIONS = {
|
||||
"SimpleUsers": [],
|
||||
"DomainAdmins": [
|
||||
["core", "user", "add_user"],
|
||||
["core", "user", "change_user"],
|
||||
["core", "user", "delete_user"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "mailbox", "add_mailbox"],
|
||||
["admin", "mailbox", "change_mailbox"],
|
||||
["admin", "mailbox", "delete_mailbox"],
|
||||
["admin", "alias", "add_alias"],
|
||||
["admin", "alias", "change_alias"],
|
||||
["admin", "alias", "delete_alias"],
|
||||
["admin", "mailbox", "view_mailboxes"],
|
||||
["admin", "alias", "view_aliases"],
|
||||
],
|
||||
"Resellers": [
|
||||
["core", "user", "add_user"],
|
||||
["core", "user", "change_user"],
|
||||
["core", "user", "delete_user"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "mailbox", "add_mailbox"],
|
||||
["admin", "mailbox", "change_mailbox"],
|
||||
["admin", "mailbox", "delete_mailbox"],
|
||||
["admin", "alias", "add_alias"],
|
||||
["admin", "alias", "change_alias"],
|
||||
["admin", "alias", "delete_alias"],
|
||||
["admin", "mailbox", "view_mailboxes"],
|
||||
["admin", "alias", "view_aliases"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "add_domain"],
|
||||
["admin", "domain", "change_domain"],
|
||||
["admin", "domain", "delete_domain"],
|
||||
["admin", "domainalias", "add_domainalias"],
|
||||
["admin", "domainalias", "change_domainalias"],
|
||||
["admin", "domainalias", "delete_domainalias"],
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@events.observe("TopNotifications")
|
||||
def check_for_new_versions(request, include_all):
|
||||
"""Check if new versions are available."""
|
||||
from modoboa.core.utils import check_for_updates
|
||||
|
||||
if not request.user.is_superuser:
|
||||
return []
|
||||
status, extensions = check_for_updates(request)
|
||||
if not status:
|
||||
return [{"id": "newversionavailable"}] if include_all else []
|
||||
return [{
|
||||
"id": "newversionavailable",
|
||||
"url": reverse("core:index") + "#info/",
|
||||
"text": _("One or more updates are available"),
|
||||
"level": "info",
|
||||
}]
|
||||
"""App config for core."""
|
||||
|
||||
default_app_config = "modoboa.core.apps.CoreConfig"
|
||||
|
||||
+1
-32
@@ -6,38 +6,8 @@ from django.utils.translation import ugettext_lazy
|
||||
|
||||
BASE_EVENTS = [
|
||||
"AccountCreated",
|
||||
"AccountAutoCreated",
|
||||
|
||||
"AccountModified",
|
||||
"AccountDeleted",
|
||||
"AccountExported",
|
||||
"AccountImported",
|
||||
"PasswordUpdated",
|
||||
"ExtraAccountActions",
|
||||
"RoleChanged",
|
||||
"GetExtraRoles",
|
||||
"GetExtraRolePermissions",
|
||||
"PasswordChange",
|
||||
"UserCanSetRole",
|
||||
|
||||
"InitialDataLoaded",
|
||||
|
||||
"UserMenuDisplay",
|
||||
"AdminMenuDisplay",
|
||||
"GetStaticContent",
|
||||
|
||||
"UserLogin",
|
||||
"UserLogout",
|
||||
|
||||
"GetAnnouncement",
|
||||
|
||||
"TopNotifications",
|
||||
"ExtraAdminContent",
|
||||
|
||||
"ExtraUprefsRoutes",
|
||||
"ExtraUprefsJS",
|
||||
|
||||
"ExtraFormFields",
|
||||
"SaveExtraFormFields",
|
||||
]
|
||||
|
||||
|
||||
@@ -57,7 +27,6 @@ def load_core_settings():
|
||||
|
||||
|
||||
class CoreConfig(AppConfig):
|
||||
|
||||
"""App configuration."""
|
||||
|
||||
name = "modoboa.core"
|
||||
|
||||
@@ -42,3 +42,44 @@ LDAP_GROUP_TYPES = (
|
||||
("posixgroup", "PosixGroup"),
|
||||
("groupofnames", "GroupOfNames"),
|
||||
)
|
||||
|
||||
PERMISSIONS = {
|
||||
"SimpleUsers": [],
|
||||
"DomainAdmins": [
|
||||
["core", "user", "add_user"],
|
||||
["core", "user", "change_user"],
|
||||
["core", "user", "delete_user"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "mailbox", "add_mailbox"],
|
||||
["admin", "mailbox", "change_mailbox"],
|
||||
["admin", "mailbox", "delete_mailbox"],
|
||||
["admin", "alias", "add_alias"],
|
||||
["admin", "alias", "change_alias"],
|
||||
["admin", "alias", "delete_alias"],
|
||||
["admin", "mailbox", "view_mailboxes"],
|
||||
["admin", "alias", "view_aliases"],
|
||||
],
|
||||
"Resellers": [
|
||||
["core", "user", "add_user"],
|
||||
["core", "user", "change_user"],
|
||||
["core", "user", "delete_user"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "view_domain"],
|
||||
["admin", "mailbox", "add_mailbox"],
|
||||
["admin", "mailbox", "change_mailbox"],
|
||||
["admin", "mailbox", "delete_mailbox"],
|
||||
["admin", "alias", "add_alias"],
|
||||
["admin", "alias", "change_alias"],
|
||||
["admin", "alias", "delete_alias"],
|
||||
["admin", "mailbox", "view_mailboxes"],
|
||||
["admin", "alias", "view_aliases"],
|
||||
["admin", "domain", "view_domains"],
|
||||
["admin", "domain", "add_domain"],
|
||||
["admin", "domain", "change_domain"],
|
||||
["admin", "domain", "delete_domain"],
|
||||
["admin", "domainalias", "add_domainalias"],
|
||||
["admin", "domainalias", "change_domainalias"],
|
||||
["admin", "domainalias", "delete_domainalias"],
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Custom context processors."""
|
||||
|
||||
from modoboa.lib import events
|
||||
from . import signals
|
||||
|
||||
|
||||
def top_notifications(request):
|
||||
@@ -9,8 +9,11 @@ def top_notifications(request):
|
||||
return {}
|
||||
interval = request.localconfig.parameters.get_value(
|
||||
"top_notifications_check_interval")
|
||||
notifications = signals.get_top_notifications.send(
|
||||
sender="top_notifications", include_all=False)
|
||||
notifications = reduce(
|
||||
lambda a, b: a + b, [notif[1] for notif in notifications])
|
||||
return {
|
||||
"notifications_check_interval": interval * 1000,
|
||||
"top_notifications": events.raiseQueryEvent(
|
||||
"TopNotifications", request, False)
|
||||
"top_notifications": notifications
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import logging
|
||||
|
||||
from django.db.models.signals import post_delete
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.db.models import signals
|
||||
from django.dispatch import receiver
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
@@ -10,7 +11,13 @@ from django.contrib.sites import models as sites_models
|
||||
|
||||
from reversion import revisions as reversion
|
||||
|
||||
from modoboa.lib import exceptions
|
||||
from modoboa.lib import permissions
|
||||
from modoboa.lib.signals import get_request
|
||||
|
||||
from . import models
|
||||
from . import signals as core_signals
|
||||
from . import utils
|
||||
|
||||
|
||||
@receiver(reversion.post_revision_commit)
|
||||
@@ -44,14 +51,13 @@ def post_revision_commit(sender, **kwargs):
|
||||
getattr(logger, level)(message)
|
||||
|
||||
|
||||
@receiver(post_delete)
|
||||
@receiver(signals.post_delete)
|
||||
def log_object_removal(sender, instance, **kwargs):
|
||||
"""Custom post-delete hook.
|
||||
|
||||
We want to know who was responsible for an object deletion.
|
||||
"""
|
||||
from reversion.models import Version
|
||||
from modoboa.lib.signals import get_request
|
||||
|
||||
if not reversion.is_registered(sender):
|
||||
return
|
||||
@@ -75,3 +81,50 @@ def create_local_config(sender, **kwargs):
|
||||
return
|
||||
models.LocalConfig.objects.create(
|
||||
site=sites_models.Site.objects.get_current())
|
||||
|
||||
|
||||
@receiver(signals.pre_delete, sender=models.User)
|
||||
def update_permissions(sender, instance, **kwargs):
|
||||
"""Permissions cleanup."""
|
||||
from_user = get_request().user
|
||||
if from_user == instance:
|
||||
raise exceptions.PermDeniedException(
|
||||
_("You can't delete your own account")
|
||||
)
|
||||
|
||||
if not from_user.can_access(instance):
|
||||
raise exceptions.PermDeniedException
|
||||
|
||||
# We send an additional signal before permissions are removed
|
||||
core_signals.account_deleted.send(
|
||||
sender="update_permissions", user=instance)
|
||||
owner = permissions.get_object_owner(instance)
|
||||
if owner == instance:
|
||||
# The default admin is being removed...
|
||||
owner = from_user
|
||||
# Change ownership of existing objects
|
||||
for ooentry in instance.objectaccess_set.filter(is_owner=True):
|
||||
if ooentry.content_object is not None:
|
||||
permissions.grant_access_to_object(
|
||||
owner, ooentry.content_object, True)
|
||||
permissions.ungrant_access_to_object(
|
||||
ooentry.content_object, instance)
|
||||
# Remove existing permissions on this user
|
||||
permissions.ungrant_access_to_object(instance)
|
||||
|
||||
|
||||
@receiver(core_signals.get_top_notifications)
|
||||
def check_for_new_versions(sender, include_all, **kwargs):
|
||||
"""Check if new versions are available."""
|
||||
request = get_request()
|
||||
if not request.user.is_superuser:
|
||||
return []
|
||||
status, extensions = utils.check_for_updates(request)
|
||||
if not status:
|
||||
return [{"id": "newversionavailable"}] if include_all else []
|
||||
return [{
|
||||
"id": "newversionavailable",
|
||||
"url": reverse("core:index") + "#info/",
|
||||
"text": _("One or more updates are available"),
|
||||
"level": "info",
|
||||
}]
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
from django.contrib.auth.models import Group
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from modoboa.core import PERMISSIONS
|
||||
from modoboa.core.extensions import exts_pool
|
||||
from modoboa.lib.cryptutils import random_key
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.permissions import add_permissions_to_group
|
||||
import modoboa.relaydomains.models as relay_models
|
||||
|
||||
from ... import constants
|
||||
from ... import extensions
|
||||
from ... import models
|
||||
from ... import signals
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
@@ -54,27 +54,26 @@ class Command(BaseCommand):
|
||||
for service_name in ["relay", "smtp"]:
|
||||
relay_models.Service.objects.get_or_create(name=service_name)
|
||||
|
||||
exts_pool.load_all()
|
||||
extensions.exts_pool.load_all()
|
||||
|
||||
superadmin = models.User.objects.filter(is_superuser=True).first()
|
||||
groups = PERMISSIONS.keys() + [
|
||||
role[0] for role
|
||||
in events.raiseQueryEvent("GetExtraRoles", superadmin, None)
|
||||
]
|
||||
groups = constants.PERMISSIONS.keys()
|
||||
for groupname in groups:
|
||||
group, created = Group.objects.get_or_create(name=groupname)
|
||||
results = signals.extra_role_permissions.send(
|
||||
sender=self.__class__, role=groupname)
|
||||
permissions = (
|
||||
PERMISSIONS.get(groupname, []) +
|
||||
events.raiseQueryEvent("GetExtraRolePermissions", groupname)
|
||||
constants.PERMISSIONS.get(groupname, []) +
|
||||
reduce(lambda a, b: a + b, [result[1] for result in results])
|
||||
)
|
||||
if not permissions:
|
||||
continue
|
||||
add_permissions_to_group(group, permissions)
|
||||
|
||||
for extname in exts_pool.extensions.keys():
|
||||
extension = exts_pool.get_extension(extname)
|
||||
for extname in extensions.exts_pool.extensions.keys():
|
||||
extension = extensions.exts_pool.get_extension(extname)
|
||||
extension.load_initial_data()
|
||||
events.raiseEvent("InitialDataLoaded", extname)
|
||||
signals.initial_data_loaded.send(
|
||||
sender=self.__class__, extname=extname)
|
||||
|
||||
if options["extra_fixtures"]:
|
||||
from modoboa.admin import factories
|
||||
|
||||
+15
-43
@@ -27,6 +27,7 @@ from modoboa.lib.exceptions import (
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import constants
|
||||
from . import signals
|
||||
|
||||
|
||||
try:
|
||||
@@ -103,41 +104,6 @@ class User(PermissionsMixin):
|
||||
super(User, self).__init__(*args, **kwargs)
|
||||
self.parameters = param_tools.Manager("user", self._parameters)
|
||||
|
||||
def delete(self, fromuser, *args, **kwargs):
|
||||
"""Custom delete method
|
||||
|
||||
To check permissions properly, we need to make a distinction
|
||||
between 2 cases:
|
||||
|
||||
* If the user owns a mailbox, the check is made on that object
|
||||
(useful for domain admins)
|
||||
|
||||
* Otherwise, the check is made on the user
|
||||
"""
|
||||
from modoboa.lib.permissions import \
|
||||
get_object_owner, grant_access_to_object, ungrant_access_to_object
|
||||
|
||||
if fromuser == self:
|
||||
raise PermDeniedException(
|
||||
_("You can't delete your own account")
|
||||
)
|
||||
|
||||
if not fromuser.can_access(self):
|
||||
raise PermDeniedException
|
||||
|
||||
owner = get_object_owner(self)
|
||||
if owner == self:
|
||||
# The default admin is being removed...
|
||||
owner = fromuser
|
||||
for ooentry in self.objectaccess_set.filter(is_owner=True):
|
||||
if ooentry.content_object is not None:
|
||||
grant_access_to_object(owner, ooentry.content_object, True)
|
||||
ungrant_access_to_object(ooentry.content_object, self)
|
||||
|
||||
events.raiseEvent("AccountDeleted", self, fromuser, **kwargs)
|
||||
ungrant_access_to_object(self)
|
||||
super(User, self).delete()
|
||||
|
||||
def _crypt_password(self, raw_value):
|
||||
"""Crypt the local password using the appropriate scheme.
|
||||
|
||||
@@ -177,9 +143,9 @@ class User(PermissionsMixin):
|
||||
LDAPAuthBackend().update_user_password(
|
||||
self.username, curvalue, raw_value
|
||||
)
|
||||
events.raiseEvent(
|
||||
"PasswordUpdated", self, raw_value, self.pk is None
|
||||
)
|
||||
signals.account_password_updated.send(
|
||||
sender=self.__class__,
|
||||
account=self, password=raw_value, created=self.pk is None)
|
||||
|
||||
def check_password(self, raw_value):
|
||||
"""Compare raw_value to current password."""
|
||||
@@ -329,7 +295,8 @@ class User(PermissionsMixin):
|
||||
"""
|
||||
if role is None or self.role == role:
|
||||
return
|
||||
events.raiseEvent("RoleChanged", self, role)
|
||||
signals.account_role_changed.send(
|
||||
sender=self.__class__, account=self, role=role)
|
||||
self.groups.clear()
|
||||
if role == "SuperAdmins":
|
||||
self.is_superuser = True
|
||||
@@ -378,7 +345,7 @@ class User(PermissionsMixin):
|
||||
|
||||
"account", loginname, password, first name, last name, enabled, role
|
||||
|
||||
Additional fields can be added using the *AccountImported* event.
|
||||
Additional fields can be added using the *account_imported* signal.
|
||||
|
||||
:param user: a ``core.User`` instance
|
||||
:param row: a list containing the expected information
|
||||
@@ -431,7 +398,8 @@ class User(PermissionsMixin):
|
||||
self.post_create(user)
|
||||
if len(row) < 8:
|
||||
return
|
||||
events.raiseEvent("AccountImported", user, self, row[7:])
|
||||
signals.account_imported.send(
|
||||
sender=self.__class__, user=user, account=self, row=row[7:])
|
||||
|
||||
def to_csv(self, csvwriter):
|
||||
"""Export this account.
|
||||
@@ -450,7 +418,10 @@ class User(PermissionsMixin):
|
||||
self.role,
|
||||
self.email.encode("utf-8")
|
||||
]
|
||||
row += events.raiseQueryEvent("AccountExported", self)
|
||||
results = signals.account_exported.send(
|
||||
sender=self.__class__, user=self)
|
||||
for result in results:
|
||||
row += result[1]
|
||||
csvwriter.writerow(row)
|
||||
|
||||
reversion.register(User)
|
||||
@@ -473,7 +444,8 @@ def populate_callback(user, group="SimpleUsers"):
|
||||
user.post_create(sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, user)
|
||||
events.raiseEvent("AccountAutoCreated", user)
|
||||
signals.account_auto_created.send(
|
||||
sender="populate_callback", user=user)
|
||||
|
||||
|
||||
class ObjectAccess(models.Model):
|
||||
|
||||
@@ -2,6 +2,37 @@
|
||||
|
||||
import django.dispatch
|
||||
|
||||
account_auto_created = django.dispatch.Signal(providing_args=["user"])
|
||||
account_exported = django.dispatch.Signal(providing_args=["account"])
|
||||
account_imported = django.dispatch.Signal(
|
||||
providing_args=["user", "account", "row"])
|
||||
account_deleted = django.dispatch.Signal(providing_args=["user"])
|
||||
account_role_changed = django.dispatch.Signal(
|
||||
providing_args=["account", "role"])
|
||||
account_password_updated = django.dispatch.Signal(
|
||||
providing_args=["account", "password", "created"])
|
||||
allow_password_change = django.dispatch.Signal(providing_args=["user"])
|
||||
can_create_object = django.dispatch.Signal(
|
||||
providing_args=["context", "object_type", "count"])
|
||||
extra_account_actions = django.dispatch.Signal(providing_args=["account"])
|
||||
extra_admin_menu_entries = django.dispatch.Signal(
|
||||
providing_args=["location", "user"])
|
||||
extra_form_fields = django.dispatch.Signal(
|
||||
providing_args=["form", "instance"])
|
||||
extra_role_permissions = django.dispatch.Signal(providing_args=["role"])
|
||||
extra_static_content = django.dispatch.Signal(
|
||||
providing_args=["caller", "st_type", "user"])
|
||||
extra_uprefs_routes = django.dispatch.Signal()
|
||||
extra_user_menu_entries = django.dispatch.Signal(
|
||||
providing_args=["location", "user"])
|
||||
get_announcements = django.dispatch.Signal(providing_args=["location"])
|
||||
get_top_notifications = django.dispatch.Signal(
|
||||
providing_args=["include_all"])
|
||||
initial_data_loaded = django.dispatch.Signal(providing_args=["extname"])
|
||||
register_postfix_maps = django.dispatch.Signal()
|
||||
save_extra_form_fields = django.dispatch.Signal(
|
||||
providing_args=["form", "instance", "cleaned_data"])
|
||||
user_can_set_role = django.dispatch.Signal(
|
||||
providing_args=["user", "role", "account"])
|
||||
user_login = django.dispatch.Signal(
|
||||
providing_args=["username", "password"])
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<script type="text/javascript" src="{% static 'js/listing.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'js/twocols_nav.js' %}"></script>
|
||||
<script type="text/javascript">
|
||||
{{ extrajs|safe }}
|
||||
{% extra_static_content "user_index" "js" user %}
|
||||
$(document).ready(function() {
|
||||
var userprefs = new TwocolsNav({
|
||||
deflocation: "profile/",
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
"""Custom tags for Core application."""
|
||||
|
||||
import os
|
||||
import pkg_resources
|
||||
import re
|
||||
|
||||
from django import template
|
||||
from django.contrib.sessions.models import Session
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import timezone
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.lib import events
|
||||
from django.contrib.sessions.models import Session
|
||||
|
||||
from .. import signals
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -19,8 +22,9 @@ register = template.Library()
|
||||
@register.simple_tag
|
||||
def core_menu(selection, user):
|
||||
"""Build the top level menu."""
|
||||
entries = (
|
||||
events.raiseQueryEvent("AdminMenuDisplay", "top_menu", user))
|
||||
entries = signals.extra_admin_menu_entries.send(
|
||||
sender="core_menu", location="top_menu", user=user)
|
||||
entries = reduce(lambda a, b: a + b, [entry[1] for entry in entries])
|
||||
if user.is_superuser:
|
||||
entries += [
|
||||
{"name": "settings",
|
||||
@@ -38,8 +42,10 @@ def core_menu(selection, user):
|
||||
|
||||
@register.simple_tag
|
||||
def extensions_menu(selection, user):
|
||||
menu = events.raiseQueryEvent("UserMenuDisplay", "top_menu", user)
|
||||
return render_to_string('common/menulist.html', {
|
||||
menu = signals.extra_user_menu_entries.send(
|
||||
sender="core_menu", location="top_menu", user=user)
|
||||
menu = reduce(lambda a, b: a + b, [entry[1] for entry in menu])
|
||||
return render_to_string("common/menulist.html", {
|
||||
"selection": selection, "entries": menu, "user": user
|
||||
})
|
||||
|
||||
@@ -83,13 +89,16 @@ def user_menu(user, selection):
|
||||
]}
|
||||
]
|
||||
|
||||
entries[0]["menu"] += \
|
||||
events.raiseQueryEvent("UserMenuDisplay", "options_menu", user) \
|
||||
+ [{"name": "logout",
|
||||
"url": reverse("core:logout"),
|
||||
"label": _("Logout"),
|
||||
"img": "fa fa-sign-out"}]
|
||||
|
||||
extra_entries = signals.extra_user_menu_entries.send(
|
||||
sender="user_menu", location="options_menu", user=user)
|
||||
extra_entries = reduce(
|
||||
lambda a, b: a + b, [entry[1] for entry in extra_entries])
|
||||
entries[0]["menu"] += (
|
||||
extra_entries + [{"name": "logout",
|
||||
"url": reverse("core:logout"),
|
||||
"label": _("Logout"),
|
||||
"img": "fa fa-sign-out"}]
|
||||
)
|
||||
return render_to_string("common/menulist.html", {
|
||||
"selection": selection, "entries": entries, "user": user
|
||||
})
|
||||
@@ -114,9 +123,13 @@ def uprefs_menu(selection, user):
|
||||
"url": "api/",
|
||||
"label": _("API"),
|
||||
})
|
||||
entries += events.raiseQueryEvent("UserMenuDisplay", "uprefs_menu", user)
|
||||
extra_entries = signals.extra_user_menu_entries.send(
|
||||
sender="user_menu", location="uprefs_menu", user=user)
|
||||
extra_entries = reduce(
|
||||
lambda a, b: a + b, [entry[1] for entry in extra_entries])
|
||||
entries += extra_entries
|
||||
entries = sorted(entries, key=lambda e: e["label"])
|
||||
return render_to_string('common/menu.html', {
|
||||
return render_to_string("common/menu.html", {
|
||||
"entries": entries,
|
||||
"css": "nav nav-sidebar",
|
||||
"selection": selection,
|
||||
@@ -157,7 +170,6 @@ def visirule(field):
|
||||
|
||||
@register.simple_tag
|
||||
def get_version():
|
||||
import pkg_resources
|
||||
return pkg_resources.get_distribution("modoboa").version
|
||||
|
||||
|
||||
@@ -201,9 +213,6 @@ def connected_users(parser, token):
|
||||
|
||||
@register.simple_tag
|
||||
def get_modoboa_logo():
|
||||
import os
|
||||
from django.conf import settings
|
||||
|
||||
try:
|
||||
logo = settings.MODOBOA_CUSTOM_LOGO
|
||||
except AttributeError:
|
||||
@@ -215,9 +224,12 @@ def get_modoboa_logo():
|
||||
|
||||
@register.simple_tag
|
||||
def load_optionalmenu(user):
|
||||
menu = events.raiseQueryEvent("UserMenuDisplay", "top_menu_middle", user)
|
||||
menu = signals.extra_user_menu_entries.send(
|
||||
sender="user_menu", location="top_menu_middle", user=user)
|
||||
menu = reduce(
|
||||
lambda a, b: a + b, [entry[1] for entry in menu])
|
||||
return template.loader.render_to_string(
|
||||
'common/menulist.html',
|
||||
"common/menulist.html",
|
||||
{"entries": menu, "user": user}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""
|
||||
Views available to super administrators only.
|
||||
"""
|
||||
"""Views available to super administrators only."""
|
||||
|
||||
from django.contrib.auth.decorators import (
|
||||
login_required, user_passes_test
|
||||
)
|
||||
@@ -10,13 +9,14 @@ from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.core.models import Log
|
||||
from modoboa.core.utils import check_for_updates
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.listing import get_sort_order, get_listing_page
|
||||
from modoboa.lib.web_utils import (
|
||||
_render_to_string, render_to_json_response
|
||||
)
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from .. import signals
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
@@ -117,6 +117,8 @@ def check_top_notifications(request):
|
||||
"""
|
||||
AJAX service to check for new top notifications to display.
|
||||
"""
|
||||
return render_to_json_response(
|
||||
events.raiseQueryEvent("TopNotifications", request, True)
|
||||
)
|
||||
notifications = signals.get_top_notifications.send(
|
||||
sender="top_notifications", include_all=True)
|
||||
notifications = reduce(
|
||||
lambda a, b: a + b, [notif[1] for notif in notifications])
|
||||
return render_to_json_response(notifications)
|
||||
|
||||
@@ -3,18 +3,20 @@
|
||||
|
||||
import logging
|
||||
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.utils import translation
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.cache import never_cache
|
||||
|
||||
from django.contrib.auth import authenticate, login, logout
|
||||
|
||||
from modoboa.core.forms import LoginForm
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.web_utils import _render_to_string
|
||||
|
||||
from .base import find_nextlocation
|
||||
from .. import signals
|
||||
|
||||
logger = logging.getLogger("modoboa.auth")
|
||||
|
||||
@@ -40,9 +42,10 @@ def dologin(request):
|
||||
logger.info(
|
||||
_("User '%s' successfully logged in") % user.username
|
||||
)
|
||||
events.raiseEvent("UserLogin", request,
|
||||
form.cleaned_data["username"],
|
||||
form.cleaned_data["password"])
|
||||
signals.user_login.send(
|
||||
sender="dologin",
|
||||
username=form.cleaned_data["username"],
|
||||
password=form.cleaned_data["password"])
|
||||
return HttpResponseRedirect(find_nextlocation(request, user))
|
||||
error = _(
|
||||
"Your username and password didn't match. Please try again.")
|
||||
@@ -59,20 +62,22 @@ def dologin(request):
|
||||
nextlocation = request.GET.get("next", None)
|
||||
httpcode = 200
|
||||
|
||||
announcements = signals.get_announcements.send(
|
||||
sender="login", location="loginpage")
|
||||
announcements = [announcement[1] for announcement in announcements]
|
||||
return HttpResponse(_render_to_string(request, "registration/login.html", {
|
||||
"form": form, "error": error, "next": nextlocation,
|
||||
"annoucements": events.raiseQueryEvent("GetAnnouncement", "loginpage")
|
||||
"annoucements": announcements
|
||||
}), status=httpcode)
|
||||
|
||||
dologin = never_cache(dologin)
|
||||
|
||||
|
||||
def dologout(request):
|
||||
"""Logout the current user.
|
||||
"""
|
||||
"""Logout current user."""
|
||||
if not request.user.is_anonymous():
|
||||
events.raiseEvent("UserLogout", request)
|
||||
signals.user_logout.send(sender="dologout")
|
||||
logger = logging.getLogger("modoboa.auth")
|
||||
logger.info(_("User '%s' logged out") % request.user.username)
|
||||
logger.info(_("User {} logged out").format(request.user.username))
|
||||
logout(request)
|
||||
return HttpResponseRedirect(reverse("core:login"))
|
||||
|
||||
@@ -16,22 +16,23 @@ from modoboa.lib.web_utils import (
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from ..forms import ProfileForm, APIAccessForm
|
||||
from .. import signals
|
||||
|
||||
|
||||
@login_required
|
||||
def index(request, tplname="core/user_index.html"):
|
||||
extrajs = events.raiseQueryEvent("ExtraUprefsJS", request.user)
|
||||
return render(request, tplname, {
|
||||
"selection": "user",
|
||||
"extrajs": "".join(extrajs)
|
||||
})
|
||||
"""Render user index page."""
|
||||
# FIXME: replace ExtraUprefsJS in autoreply
|
||||
return render(request, tplname, {"selection": "user"})
|
||||
|
||||
|
||||
@login_required
|
||||
def profile(request, tplname='core/user_profile.html'):
|
||||
"""Profile detail/update view."""
|
||||
update_password = True
|
||||
if True in events.raiseQueryEvent("PasswordChange", request.user):
|
||||
results = signals.allow_password_change.send(
|
||||
sender="profile", user=request.user)
|
||||
if True in [result[1] for result in results]:
|
||||
update_password = False
|
||||
|
||||
if request.method == "POST":
|
||||
|
||||
@@ -5,31 +5,31 @@ from django.contrib.auth.models import Group, Permission
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from modoboa.core import constants as core_constants
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.core.models import ObjectAccess, User
|
||||
from modoboa.lib import events
|
||||
|
||||
|
||||
def get_account_roles(user, account=None):
|
||||
"""Return the list of available account roles.
|
||||
|
||||
This function is used to create or modify an account. The returned
|
||||
list can be extended by listening to the ``GetExtraRoles`` event.
|
||||
This function is used to create or modify an account.
|
||||
|
||||
:param ``User`` user: connected user
|
||||
:param ``User`` account: account beeing modified (None on creation)
|
||||
:return: list of strings
|
||||
"""
|
||||
result = [core_constants.SIMPLEUSERS_ROLE]
|
||||
filters = events.raiseQueryEvent(
|
||||
"UserCanSetRole", user, "DomainAdmins", account
|
||||
)
|
||||
if user.has_perm("admin.add_domain") and \
|
||||
(not filters or True in filters):
|
||||
filters = core_signals.user_can_set_role.send(
|
||||
sender="get_account_roles", user=user, role="DomainAdmins",
|
||||
account=account)
|
||||
condition = (
|
||||
user.has_perm("admin.add_domain") and
|
||||
(not filters or True in [flt[1] for flt in filters]))
|
||||
if condition:
|
||||
result += [core_constants.DOMAINADMINS_ROLE]
|
||||
if user.is_superuser:
|
||||
result += [
|
||||
core_constants.RESELLERS_ROLE, core_constants.SUPERADMINS_ROLE]
|
||||
result += events.raiseQueryEvent("GetExtraRoles", user, account)
|
||||
return sorted(result, key=lambda role: role[1])
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ from django.template import Template, Context
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from modoboa.lib import events
|
||||
from modoboa.core import signals as core_signals
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -89,11 +89,12 @@ def extra_static_content(caller, st_type, user):
|
||||
tpl = template.Template(
|
||||
"{% for sc in static_content %}{{ sc|safe }}{% endfor %}"
|
||||
)
|
||||
static_content = core_signals.extra_static_content.send(
|
||||
sender="extra_static_content",
|
||||
caller=caller, st_type=st_type, user=user)
|
||||
static_content = [result[1] for result in static_content]
|
||||
return tpl.render(
|
||||
template.Context({
|
||||
'static_content': events.raiseQueryEvent(
|
||||
"GetStaticContent", caller, st_type, user)
|
||||
})
|
||||
template.Context({"static_content": static_content})
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ def load_limits_settings():
|
||||
|
||||
param_tools.registry.add("global", ParametersForm, _("Limits"))
|
||||
events.declare(EVENTS)
|
||||
from . import controls
|
||||
from . import general_callbacks
|
||||
|
||||
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
"""Event handlers that check if limits are reached."""
|
||||
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib.permissions import get_object_owner
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
def move_pool_resource(owner, user):
|
||||
"""Move resource from one pool to another.
|
||||
|
||||
When an account doesn't need a pool anymore, we move the
|
||||
associated resource to the pool of its owner.
|
||||
"""
|
||||
if not owner.is_superuser:
|
||||
for name, ltpl in utils.get_user_limit_templates():
|
||||
l = user.userobjectlimit_set.get(name=name)
|
||||
if l.max_value < 0:
|
||||
continue
|
||||
ol = owner.userobjectlimit_set.get(name=name)
|
||||
ol.max_value += l.max_value
|
||||
ol.save()
|
||||
|
||||
|
||||
@events.observe("UserCanSetRole")
|
||||
def user_can_set_role(user, role, account=None):
|
||||
"""Check if the user can still set this role.
|
||||
|
||||
The only interesting case concerns resellers defining new domain
|
||||
administrators. We want to check if they are allowed to do this
|
||||
operation before any modification is made to :keyword:`account`.
|
||||
|
||||
:param ``User`` user: connected user
|
||||
:param ``User`` account: account modified (None on creation)
|
||||
:param str newrole: role to check
|
||||
"""
|
||||
condition = (
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
role != "DomainAdmins")
|
||||
if condition:
|
||||
return [True]
|
||||
lname = "domain_admins"
|
||||
condition = (
|
||||
user.is_superuser or
|
||||
not user.userobjectlimit_set.get(name=lname).is_exceeded()
|
||||
)
|
||||
if condition:
|
||||
return [True]
|
||||
if account is not None and account.role == role:
|
||||
return [True]
|
||||
return [False]
|
||||
|
||||
|
||||
@events.observe("AccountModified")
|
||||
def on_account_modified(old, new):
|
||||
"""Update limits when roles are updated"""
|
||||
owner = get_object_owner(old)
|
||||
if owner.role not in ["SuperAdmins", "Resellers"]:
|
||||
# Domain admins can't change the role so nothing to check.
|
||||
return
|
||||
|
||||
if new.role not in ["DomainAdmins", "Resellers"]:
|
||||
move_pool_resource(owner, new)
|
||||
|
||||
|
||||
@events.observe("AccountDeleted")
|
||||
def on_account_deleted(account, byuser, **kwargs):
|
||||
owner = get_object_owner(account)
|
||||
if owner.role not in ["SuperAdmins", "Resellers"]:
|
||||
return
|
||||
|
||||
move_pool_resource(owner, account)
|
||||
@@ -10,35 +10,6 @@ from . import forms
|
||||
from . import utils
|
||||
|
||||
|
||||
@events.observe("ExtraAdminContent")
|
||||
def display_pool_usage(user, target, currentpage):
|
||||
condition = (
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
target != "leftcol" or user.is_superuser)
|
||||
if condition:
|
||||
return []
|
||||
if currentpage == "identities":
|
||||
names = ["mailboxes", "mailbox_aliases"]
|
||||
if user.has_perm("admin.add_domain"):
|
||||
names += ["domain_admins"]
|
||||
else:
|
||||
exceptions = ["domain_admins", "mailboxes", "mailbox_aliases"]
|
||||
names = [
|
||||
name for name, tpl in utils.get_user_limit_templates()
|
||||
if name not in exceptions and
|
||||
("required_role" not in tpl or
|
||||
tpl["required_role"] == user.role)
|
||||
]
|
||||
|
||||
limits = user.userobjectlimit_set.filter(name__in=names, max_value__gt=0)
|
||||
if len(limits) == 0:
|
||||
return []
|
||||
return [
|
||||
render_to_string("limits/poolusage.html",
|
||||
dict(limits=limits))
|
||||
]
|
||||
|
||||
|
||||
@events.observe("ExtraAccountForm")
|
||||
def extra_account_form(user, account=None):
|
||||
if not param_tools.get_global_parameter("enable_admin_limits"):
|
||||
@@ -102,36 +73,3 @@ def fill_account_instances(user, account, instances):
|
||||
if account.role not in ["Resellers", "DomainAdmins"]:
|
||||
return
|
||||
instances["resources"] = account
|
||||
|
||||
|
||||
@events.observe("GetStaticContent")
|
||||
def get_static_content(caller, st_type, user):
|
||||
condition = (
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
caller not in ["domains", "identities"] or
|
||||
user.role in ["SuperAdmins", "SimpleUsers"]
|
||||
)
|
||||
if condition:
|
||||
return []
|
||||
if st_type == "css":
|
||||
return ["""<style>
|
||||
.resource {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.resource .progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.resource .progress .bar {
|
||||
color: #000000;
|
||||
}
|
||||
</style>
|
||||
"""]
|
||||
return ["""
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$(".progress").tooltip();
|
||||
});
|
||||
</script>
|
||||
"""]
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from django.db.models import signals
|
||||
from django.dispatch import receiver
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
@@ -10,6 +11,7 @@ from modoboa.admin import signals as admin_signals
|
||||
from modoboa.core import models as core_models
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib import signals as lib_signals
|
||||
from modoboa.lib import permissions
|
||||
from modoboa.parameters import tools as param_tools
|
||||
|
||||
from . import lib
|
||||
@@ -99,3 +101,117 @@ def display_admin_limits(sender, user, account, **kwargs):
|
||||
account.userobjectlimit_set.select_related("content_type"))
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
@receiver(core_signals.account_deleted)
|
||||
def move_resource(sender, user, **kwargs):
|
||||
"""Move remaining resource to another user."""
|
||||
owner = permissions.get_object_owner(user)
|
||||
if owner.is_superuser or owner.role != "Resellers":
|
||||
return
|
||||
utils.move_pool_resource(owner, user)
|
||||
|
||||
|
||||
@receiver(core_signals.user_can_set_role)
|
||||
def user_can_set_role(sender, user, role, account=None, **kwargs):
|
||||
"""Check if the user can still set this role.
|
||||
|
||||
The only interesting case concerns resellers defining new domain
|
||||
administrators. We want to check if they are allowed to do this
|
||||
operation before any modification is made to :keyword:`account`.
|
||||
|
||||
:param ``User`` user: connected user
|
||||
:param str role: role to check
|
||||
:param ``User`` account: account modified (None on creation)
|
||||
"""
|
||||
condition = (
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
role != "DomainAdmins")
|
||||
if condition:
|
||||
return True
|
||||
lname = "domain_admins"
|
||||
condition = (
|
||||
user.is_superuser or
|
||||
not user.userobjectlimit_set.get(name=lname).is_exceeded()
|
||||
)
|
||||
if condition:
|
||||
return True
|
||||
if account is not None and account.role == role:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@receiver(core_signals.extra_static_content)
|
||||
def get_static_content(sender, caller, st_type, user, **kwargs):
|
||||
"""Add extra static content."""
|
||||
condition = (
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
caller not in ["domains", "identities"] or
|
||||
user.role in ["SuperAdmins", "SimpleUsers"]
|
||||
)
|
||||
if condition:
|
||||
return []
|
||||
if st_type == "css":
|
||||
return ["""<style>
|
||||
.resource {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
.resource .progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.resource .progress .bar {
|
||||
color: #000000;
|
||||
}
|
||||
</style>
|
||||
"""]
|
||||
return ["""
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$(".progress").tooltip();
|
||||
});
|
||||
</script>
|
||||
"""]
|
||||
|
||||
|
||||
@receiver(admin_signals.extra_admin_content)
|
||||
def display_pool_usage(sender, user, location, currentpage, **kwargs):
|
||||
"""Display current usage."""
|
||||
condition = (
|
||||
not param_tools.get_global_parameter("enable_admin_limits") or
|
||||
location != "leftcol" or user.is_superuser)
|
||||
if condition:
|
||||
return []
|
||||
if currentpage == "identities":
|
||||
names = ["mailboxes", "mailbox_aliases"]
|
||||
if user.has_perm("admin.add_domain"):
|
||||
names += ["domain_admins"]
|
||||
else:
|
||||
exceptions = ["domain_admins", "mailboxes", "mailbox_aliases"]
|
||||
names = [
|
||||
name for name, tpl in utils.get_user_limit_templates()
|
||||
if name not in exceptions and
|
||||
("required_role" not in tpl or
|
||||
tpl["required_role"] == user.role)
|
||||
]
|
||||
|
||||
limits = user.userobjectlimit_set.filter(name__in=names, max_value__gt=0)
|
||||
if len(limits) == 0:
|
||||
return []
|
||||
return [
|
||||
render_to_string("limits/poolusage.html",
|
||||
dict(limits=limits))
|
||||
]
|
||||
|
||||
|
||||
@receiver(core_signals.account_role_changed)
|
||||
def move_pool_resource(sender, account, role, **kwargs):
|
||||
"""Move remaining resource to owner if needed."""
|
||||
owner = permissions.get_object_owner(account)
|
||||
if not owner or owner.is_superuser or owner.role != "Resellers":
|
||||
# Domain admins can't change the role so nothing to check.
|
||||
return
|
||||
|
||||
if role not in ["DomainAdmins", "Resellers"]:
|
||||
utils.move_pool_resource(owner, account)
|
||||
|
||||
@@ -12,3 +12,18 @@ def get_user_limit_templates():
|
||||
def get_domain_limit_templates():
|
||||
"""Return defined templates."""
|
||||
return constants.DEFAULT_DOMAIN_LIMITS.items()
|
||||
|
||||
|
||||
def move_pool_resource(owner, user):
|
||||
"""Move resource from one pool to another.
|
||||
|
||||
When an account doesn't need a pool anymore, we move the
|
||||
associated resource to the pool of its owner.
|
||||
"""
|
||||
for name, ltpl in get_user_limit_templates():
|
||||
l = user.userobjectlimit_set.get(name=name)
|
||||
if l.max_value < 0:
|
||||
continue
|
||||
ol = owner.userobjectlimit_set.get(name=name)
|
||||
ol.max_value += l.max_value
|
||||
ol.save()
|
||||
|
||||
@@ -174,7 +174,6 @@ def get_global_parameter(name, app=None, **kwargs):
|
||||
:param name: the parameter's name
|
||||
:param app: the application owning the parameter
|
||||
:return: the corresponding value
|
||||
|
||||
"""
|
||||
if app is None:
|
||||
app = guess_extension_name()
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
"""Relaydomains app constants."""
|
||||
|
||||
PERMISSIONS = {
|
||||
"Resellers": [
|
||||
("relaydomains", "relaydomain", "add_relaydomain"),
|
||||
("relaydomains", "relaydomain", "change_relaydomain"),
|
||||
("relaydomains", "relaydomain", "delete_relaydomain"),
|
||||
("relaydomains", "service", "add_service"),
|
||||
("relaydomains", "service", "change_service"),
|
||||
("relaydomains", "service", "delete_service")
|
||||
]
|
||||
}
|
||||
@@ -1,46 +1,11 @@
|
||||
"""General event callbacks."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import Template, Context
|
||||
from django.utils.translation import ugettext_lazy, ugettext as _
|
||||
|
||||
from modoboa.lib import events
|
||||
|
||||
from .forms import RelayDomainWizardStep, RelayDomainFormGeneral
|
||||
|
||||
PERMISSIONS = {
|
||||
"Resellers": [
|
||||
("relaydomains", "relaydomain", "add_relaydomain"),
|
||||
("relaydomains", "relaydomain", "change_relaydomain"),
|
||||
("relaydomains", "relaydomain", "delete_relaydomain"),
|
||||
("relaydomains", "service", "add_service"),
|
||||
("relaydomains", "service", "change_service"),
|
||||
("relaydomains", "service", "delete_service")
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@events.observe("GetExtraRolePermissions")
|
||||
def extra_permissions(rolename):
|
||||
"""Return extra permissions for Resellers."""
|
||||
return PERMISSIONS.get(rolename, [])
|
||||
|
||||
|
||||
@events.observe('GetStaticContent')
|
||||
def static_content(caller, st_type, user):
|
||||
if caller != 'domains' or st_type != 'js':
|
||||
return []
|
||||
|
||||
t = Template("""<script src="{{ STATIC_URL }}relaydomains/js/relay_domains.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
var rdomain;
|
||||
$(document).ready(function() {
|
||||
rdomain = new RelayDomains({});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [t.render(Context({'STATIC_URL': settings.STATIC_URL}))]
|
||||
|
||||
|
||||
@events.observe('ExtraDomainFilters')
|
||||
def extra_domain_filters():
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
"""Django signal handlers for relaydomains."""
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import signals
|
||||
from django.dispatch import receiver
|
||||
from django.template import Template, Context
|
||||
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.admin.signals import use_external_recipients
|
||||
from modoboa.core import signals as core_signals
|
||||
from modoboa.lib.email_utils import split_mailbox
|
||||
|
||||
from . import constants
|
||||
from . import models
|
||||
from . import postfix_maps
|
||||
|
||||
@@ -52,3 +55,26 @@ def register_postfix_maps(sender, **kwargs):
|
||||
postfix_maps.SplitedDomainsTransportMap,
|
||||
postfix_maps.RelayRecipientVerification
|
||||
]
|
||||
|
||||
|
||||
@receiver(core_signals.extra_role_permissions)
|
||||
def extra_role_permissions(sender, role, **kwargs):
|
||||
"""Add permissions to the Resellers group."""
|
||||
return constants.PERMISSIONS.get(role, [])
|
||||
|
||||
|
||||
@receiver(core_signals.extra_static_content)
|
||||
def static_content(sender, caller, st_type, user, **kwargs):
|
||||
"""Add extra static content."""
|
||||
if caller != "domains" or st_type != "js":
|
||||
return []
|
||||
|
||||
t = Template("""<script src="{{ STATIC_URL }}relaydomains/js/relay_domains.js" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
var rdomain;
|
||||
$(document).ready(function() {
|
||||
rdomain = new RelayDomains({});
|
||||
});
|
||||
</script>
|
||||
""")
|
||||
return [t.render(Context({"STATIC_URL": settings.STATIC_URL}))]
|
||||
|
||||
+9
-5
@@ -2,7 +2,7 @@ from django.conf import settings
|
||||
from django.conf.urls import patterns, include, url
|
||||
|
||||
from modoboa.core.extensions import exts_pool
|
||||
from modoboa.lib import events
|
||||
from modoboa.core import signals as core_signals
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
@@ -22,10 +22,14 @@ urlpatterns += patterns(
|
||||
*exts_pool.load_all()
|
||||
)
|
||||
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
*events.raiseQueryEvent("ExtraUprefsRoutes")
|
||||
)
|
||||
extra_routes = core_signals.extra_uprefs_routes.send(sender="urls")
|
||||
if extra_routes:
|
||||
extra_routes = reduce(
|
||||
lambda a, b: a + b, [route for route in extra_routes])
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
*extra_routes
|
||||
)
|
||||
|
||||
# API urls
|
||||
urlpatterns += patterns(
|
||||
|
||||
Reference in New Issue
Block a user