wip: Big refactoring.

This commit is contained in:
Antoine Nguyen
2013-08-20 16:10:36 +02:00
parent 1fcb3a4bb1
commit 4c86b5c05e
30 changed files with 367 additions and 229 deletions
+9 -59
View File
@@ -1,3 +1,4 @@
import os
import re
import hashlib
import crypt
@@ -15,6 +16,7 @@ from django.contrib.contenttypes import generic
from django.contrib.auth.models import (
UserManager, Group, AbstractBaseUser, PermissionsMixin
)
from django.contrib.contenttypes.models import ContentType
from modoboa.lib import events, md5crypt, parameters
from modoboa.lib.exceptions import PermDeniedException
from modoboa.core.extensions import exts_pool
@@ -43,6 +45,7 @@ class User(AbstractBaseUser, PermissionsMixin):
is_staff = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
date_joined = models.DateTimeField(default=timezone.now)
is_local = models.BooleanField(default=True)
objects = UserManager()
@@ -90,21 +93,6 @@ class User(AbstractBaseUser, PermissionsMixin):
ungrant_access_to_object(self)
super(User, self).delete(*args, **kwargs)
@staticmethod
def get_content_type():
"""An uggly hack to retrieve the appropriate content_type!
The explanation is available here:
https://code.djangoproject.com/ticket/11154
Quickly, the content_types framework does not retrieve the
appropriate content type for proxy models, it retrieves the
one of the first parent that is not a proxy.
"""
if not hasattr(User, "ct"):
User.ct = ContentType.objects.get(app_label="admin", model="user")
return User.ct
def _crypt_password(self, raw_value):
scheme = parameters.get_admin("PASSWORD_SCHEME")
if type(raw_value) is unicode:
@@ -252,9 +240,7 @@ class User(AbstractBaseUser, PermissionsMixin):
:param obj: an object inheriting from ``models.Model``
:return: a boolean
"""
from modoboa.lib.permissions import get_content_type
ct = get_content_type(obj)
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
@@ -271,12 +257,10 @@ class User(AbstractBaseUser, PermissionsMixin):
:param obj: a admin object
:return: a boolean
"""
from modoboa.lib.permissions import get_content_type
if self.is_superuser:
return True
ct = get_content_type(obj)
ct = ContentType.objects.get_for_model(obj)
try:
ooentry = self.objectaccess_set.get(content_type=ct, object_id=obj.id)
except ObjectAccess.DoesNotExist:
@@ -286,25 +270,13 @@ class User(AbstractBaseUser, PermissionsMixin):
if ct.model == "user":
return False
ct = self.get_content_type()
ct = ContentType.objects.get_for_model(self)
qs = self.objectaccess_set.filter(content_type=ct)
for ooentry in qs.all():
if ooentry.content_object.is_owner(obj):
return True
return False
def grant_access_to_all_objects(self):
"""Give access to all objects defined in the database
Must be used when an account is promoted as a super user.
"""
from modoboa.lib.permissions import grant_access_to_objects, get_content_type
grant_access_to_objects(self, User.objects.all(), get_content_type(User))
grant_access_to_objects(self, Domain.objects.all(), get_content_type(Domain))
grant_access_to_objects(self, DomainAlias.objects.all(), get_content_type(DomainAlias))
grant_access_to_objects(self, Mailbox.objects.all(), get_content_type(Mailbox))
grant_access_to_objects(self, Alias.objects.all(), get_content_type(Alias))
def set_role(self, role):
"""Set administrative role for this account
@@ -312,10 +284,10 @@ class User(AbstractBaseUser, PermissionsMixin):
"""
if role is None or self.group == role:
return
events.raiseEvent("RoleChanged", self, role)
self.groups.clear()
if role == "SuperAdmins":
self.is_superuser = True
self.grant_access_to_all_objects()
else:
if self.is_superuser:
ObjectAccess.objects.filter(user=self).delete()
@@ -372,35 +344,13 @@ class User(AbstractBaseUser, PermissionsMixin):
self.is_active = (row[5].strip() == 'True')
self.save(creator=user)
self.set_role(role)
self.email = row[7].strip()
if self.email != "":
mailbox, domname = split_mailbox(self.email)
try:
domain = Domain.objects.get(name=domname)
except Domain.DoesNotExist:
raise AdminError(
_("Account import failed (%s): domain does not exist" % self.username)
)
if not user.can_access(domain):
raise PermDeniedException
mb = Mailbox(address=mailbox, domain=domain, user=self, use_domain_quota=True)
mb.set_quota(override_rules=user.has_perm("admin.change_domain"))
mb.save(creator=user)
if self.group == "DomainAdmins":
for domname in row[8:]:
try:
dom = Domain.objects.get(name=domname.strip())
except Domain.DoesNotExist:
continue
dom.add_admin(self)
events.raiseEvent("AccountImported", user, self, row[7:])
def to_csv(self, csvwriter):
row = ["account", self.username.encode("utf-8"), self.password,
self.first_name.encode("utf-8"), self.last_name.encode("utf-8"),
self.is_active, self.group, self.email]
if self.group == "DomainAdmins":
row += [dom.name for dom in self.get_domains()]
row += events.raiseQueryEvent("AccountExported", self)
csvwriter.writerow(row)
reversion.register(User)
+95 -3
View File
@@ -3,6 +3,36 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from django.core.urlresolvers import reverse
from modoboa.core.extensions import ModoExtension, exts_pool
from modoboa.lib import parameters, events
from modoboa.lib.exceptions import PermDeniedException
from modoboa.lib.emailutils import split_mailbox
from modoboa.extensions.admin.exceptions import AdminError
from modoboa.extensions.admin.models import (
Domain, DomainAlias, Mailbox, Alias
)
admin_events = [
"CreateDomain",
"DomainModified",
"DeleteDomain",
"DomainAliasCreated",
"DomainAliasDeleted",
"CreateMailbox",
"DeleteMailbox",
"ModifyMailbox",
"MailboxAliasCreated",
"MailboxAliasDeleted",
"ExtraDomainForm",
"FillDomainInstances",
"ExtraAccountForm",
"CheckExtraAccountForm",
"FillAccountInstances",
]
class AdminConsole(ModoExtension):
@@ -13,8 +43,9 @@ class AdminConsole(ModoExtension):
always_active = True
def load(self):
from app_settings import AdminParametersForm
from modoboa.extensions.admin.app_settings import AdminParametersForm
parameters.register(AdminParametersForm, ugettext_lazy("Administration"))
events.declare(admin_events)
def destroy(self):
parameters.unregister()
@@ -25,7 +56,6 @@ exts_pool.register_extension(AdminConsole, show=False)
@events.observe("AdminMenuDisplay")
def menu(target, user):
if target != "top_menu":
print "rien"
return []
entries = []
if user.has_perm("admin.view_domains"):
@@ -40,5 +70,67 @@ def menu(target, user):
"url" : reverse("modoboa.extensions.admin.views.identity.identities"),
"label" : _("Identities")},
]
print entries
return entries
@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):
if admin.group != "DomainAdmins":
return []
return [dom.name for dom in Domain.objects.get_for_admin(admin)]
@events.observe("AccountImported")
def import_account_mailbox(user, account, row):
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 AdminError(
_("Account import failed (%s): domain does not exist" % account.username)
)
if not user.can_access(domain):
raise PermDeniedException
mb = Mailbox(address=mailbox, domain=domain,
user=account, use_domain_quota=True)
mb.set_quota(override_rules=user.has_perm("admin.change_domain"))
mb.save(creator=user)
if account.group == "DomainAdmins":
for domname in row[1:]:
try:
dom = Domain.objects.get(name=domname.strip())
except Domain.DoesNotExist:
continue
dom.add_admin(account)
@@ -1 +1 @@
[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_user", "admin", "user"], ["change_user", "admin", "user"], ["delete_user", "admin", "user"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}}]
[{"pk": 1, "model": "auth.group", "fields": {"name": "DomainAdmins", "permissions": [["add_user", "core", "user"], ["change_user", "core", "user"], ["delete_user", "core", "user"], ["view_domains", "admin", "domain"], ["add_mailbox", "admin", "mailbox"], ["change_mailbox", "admin", "mailbox"], ["delete_mailbox", "admin", "mailbox"], ["add_alias", "admin", "alias"], ["change_alias", "admin", "alias"], ["delete_alias", "admin", "alias"], ["view_mailboxes", "admin", "mailbox"], ["view_aliases", "admin", "alias"], ["view_domaliases", "admin", "domainalias"], ["add_domainalias", "admin", "domainalias"], ["change_domainalias", "admin", "domainalias"], ["delete_domainalias", "admin", "domainalias"]]}},{"pk": 2, "model": "auth.group", "fields": {"name": "SimpleUsers", "permissions": []}}]
@@ -1 +1 @@
[{"pk": 1, "model": "admin.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "admin.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["admin", "user"], "object_id": 1}}]
[{"pk": 1, "model": "core.user", "fields": {"username": "admin", "first_name": "", "last_name": "", "is_active": true, "is_superuser": true, "is_staff": false, "is_local": true, "last_login": "2012-01-26 15:19:25", "groups": [], "user_permissions": [], "password": "{CRYPT}dTTsGDkA5ZHKg", "email": "", "date_joined": "2012-01-26 15:19:25"}},{"pk": 1, "model": "core.objectaccess", "fields": {"is_owner": true, "user": 1, "content_type": ["core", "user"], "object_id": 1}}]
+68 -1
View File
@@ -1,6 +1,11 @@
# coding: utf-8
from django.db.models import Q
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.contrib.contenttypes.models import ContentType
from modoboa.core.models import User
from modoboa.lib import parameters
from modoboa.lib.emailutils import split_mailbox
from modoboa.extensions.admin.models import Alias
def get_sort_order(qdict, default, allowed_values=None):
@@ -23,9 +28,71 @@ def get_sort_order(qdict, default, allowed_values=None):
def get_listing_page(objects, pagenum):
paginator = Paginator(objects, int(parameters.get_admin("ITEMS_PER_PAGE")))
paginator = Paginator(
objects, int(parameters.get_admin("ITEMS_PER_PAGE", app="core"))
)
try:
page = paginator.page(int(pagenum))
except (EmptyPage, PageNotAnInteger, ValueError):
page = paginator.page(paginator.num_pages)
return page
def get_identities(user, args=None):
"""Return all the identities owned by a user
:param user: the desired user
:param args: a args object
:return: a queryset
"""
from itertools import chain
if args:
squery = args.get("searchquery", None)
idtfilter = args.getlist("idtfilter", None)
grpfilter = args.getlist("grpfilter", None)
else:
squery = None
idtfilter = None
grpfilter = None
accounts = []
if not idtfilter or "account" in idtfilter:
ids = user.objectaccess_set \
.filter(content_type=ContentType.objects.get_for_model(user)) \
.values_list('object_id', flat=True)
q = Q(pk__in=ids)
if squery:
q &= Q(username__icontains=squery) | Q(email__icontains=squery)
if grpfilter and len(grpfilter):
if "SuperAdmins" in grpfilter:
q &= Q(is_superuser=True)
grpfilter.remove("SuperAdmins")
if len(grpfilter):
q |= Q(groups__name__in=grpfilter)
else:
q &= Q(groups__name__in=grpfilter)
accounts = User.objects.select_related().filter(q)
aliases = []
if not idtfilter or ("alias" in idtfilter
or "forward" in idtfilter
or "dlist" in idtfilter):
alct = ContentType.objects.get_for_model(Alias)
ids = user.objectaccess_set.filter(content_type=alct) \
.values_list('object_id', flat=True)
q = Q(pk__in=ids)
if squery:
if '@' in squery:
local_part, domname = split_mailbox(squery)
if local_part:
q &= Q(address__icontains=local_part)
if domname:
q &= Q(domain__name__icontains=domname)
else:
q &= Q(address__icontains=squery) | Q(domain__name__icontains=squery)
aliases = Alias.objects.select_related().filter(q)
if idtfilter:
aliases = [al for al in aliases if al.type in idtfilter]
return chain(accounts, aliases)
@@ -142,7 +142,7 @@ class Migration(SchemaMigration):
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
user_model_name: {
'Meta': {'object_name': 'User'},
'Meta': {'object_name': 'User', 'db_table': "'admin_user'"},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
@@ -149,7 +149,7 @@ class Migration(DataMigration):
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
user_model_name: {
'Meta': {'object_name': 'User'},
'Meta': {'object_name': 'User', 'db_table': "'admin_user'"},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
@@ -93,7 +93,7 @@ class Migration(SchemaMigration):
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
user_model_name: {
'Meta': {'object_name': 'User'},
'Meta': {'object_name': 'User', 'db_table': "'admin_user'"},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
@@ -98,7 +98,7 @@ class Migration(DataMigration):
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
user_model_name: {
'Meta': {'object_name': 'User'},
'Meta': {'object_name': 'User', 'db_table': "'admin_user'"},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
@@ -97,7 +97,7 @@ class Migration(DataMigration):
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
user_model_name: {
'Meta': {'object_name': 'User'},
'Meta': {'object_name': 'User', 'db_table': "'admin_user'"},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+1
View File
@@ -36,6 +36,7 @@ class Alias(DatesAware):
)
unique_together = (("address", "domain"),)
ordering = ["domain__name", "address"]
app_label = 'admin'
def __unicode__(self):
return self.full_address
+3
View File
@@ -10,6 +10,9 @@ class ObjectDates(models.Model):
creation = models.DateTimeField(auto_now_add=True)
last_modification = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'admin'
@staticmethod
def set_for_object(obj):
"""Initialize or update dates for a given object.
+23 -1
View File
@@ -1,12 +1,27 @@
import reversion
from django.db import models
from django.db.models.manager import Manager
from django.utils.translation import ugettext as _, ugettext_lazy
from django.contrib.contenttypes import generic
from modoboa.lib import events, parameters
from modoboa.core.models import ObjectAccess
from modoboa.extensions.admin.exceptions import AdminError
from .base import DatesAware
class DomainManager(Manager):
def get_for_admin(self, admin):
"""Return the domains belonging to this admin
The result is a ``QuerySet`` object, so this function can be used
to fill ``ModelChoiceField`` objects.
"""
if admin.is_superuser:
return self.get_query_set()
return self.get_query_set().filter(owners__user=admin)
class Domain(DatesAware):
name = models.CharField(ugettext_lazy('name'), max_length=100, unique=True,
help_text=ugettext_lazy("The domain name"))
@@ -19,12 +34,15 @@ class Domain(DatesAware):
)
owners = generic.GenericRelation(ObjectAccess)
objects = DomainManager()
class Meta:
permissions = (
("view_domain", "View domain"),
("view_domains", "View domains"),
)
ordering = ["name"]
app_label = 'admin'
@property
def domainalias_count(self):
@@ -79,6 +97,7 @@ class Domain(DatesAware):
def delete(self, fromuser, keepdir=False):
from modoboa.lib.permissions import \
ungrant_access_to_object, ungrant_access_to_objects
from .mailbox import Quota
if self.domainalias_set.count():
events.raiseEvent("DomainAliasDeleted", self.domainalias_set.all())
@@ -123,7 +142,10 @@ class Domain(DatesAware):
if len(row) < 4:
raise AdminError(_("Invalid line"))
self.name = row[1].strip()
self.quota = int(row[2].strip())
try:
self.quota = int(row[2].strip())
except ValueError:
raise AdminError(_("Invalid quota value for domain '%s'" % self.name))
self.enabled = (row[3].strip() == 'True')
self.save(creator=user)
@@ -23,6 +23,7 @@ class DomainAlias(DatesAware):
permissions = (
("view_domaliases", "View domain aliases"),
)
app_label = 'admin'
def __unicode__(self):
return self.name
@@ -1,6 +1,9 @@
import os
import reversion
from django.db import models
from django.db.models import Q
from django.db.models.manager import Manager
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _, ugettext_lazy
from django.db.models.signals import pre_delete
from django.dispatch import receiver
@@ -12,6 +15,36 @@ from .base import DatesAware
from .domain import Domain
class MailboxManager(Manager):
def get_for_admin(self, admin, squery=None):
"""Return the mailboxes that belong to this admin.
The result will contain the mailboxes defined for each domain that
user can see.
:param string squery: a search query
:return: a list of ``Mailbox`` objects
"""
qf = None
if squery is not None:
if '@' in squery:
parts = squery.split('@')
addrfilter = '@'.join(parts[:-1])
domfilter = parts[-1]
qf = Q(address__contains=addrfilter) & Q(domain__name__contains=domfilter)
else:
qf = Q(address__contains=squery) | Q(domain__name__contains=squery)
ids = admin.objectaccess_set \
.filter(content_type=ContentType.objects.get_for_model(Mailbox)) \
.values_list('object_id', flat=True)
if qf is not None:
qf = Q(pk__in=ids) & qf
else:
qf = Q(pk__in=ids)
return self.get_query_set().filter(qf)
class Mailbox(DatesAware):
address = models.CharField(
ugettext_lazy('address'), max_length=252,
@@ -22,10 +55,13 @@ class Mailbox(DatesAware):
domain = models.ForeignKey(Domain)
user = models.ForeignKey(User)
objects = MailboxManager()
class Meta:
permissions = (
("view_mailboxes", "View mailboxes"),
)
app_label = 'admin'
def __init__(self, *args, **kwargs):
super(Mailbox, self).__init__(*args, **kwargs)
@@ -211,3 +247,6 @@ class Quota(models.Model):
messages = models.IntegerField(default=0)
mbox = models.OneToOneField(Mailbox, related_name="quota_value", null=True)
class Meta:
app_label = 'admin'
@@ -1,4 +1,4 @@
{% extends "common/tabforms.html" %}{% load i18n libextras admin_extras %}
{% extends "common/tabforms.html" %}{% load i18n libextras admin_tags %}
{% block extratabs %}
<li class="{% if tabs.active_id == 'admins' %}active{% endif %}">
<a href="#admins" data-toggle="tab">{% trans "Administrators" %}</a>
@@ -1,4 +1,4 @@
{% load i18n admin_extras libextras %}
{% load i18n admin_tags libextras %}
<table id="{{ tableid }}" class="table {{ styles }}">
<thead>
<tr>
@@ -1,20 +0,0 @@
{% load i18n admin_extras %}
<h2>{% trans "Logs" %} <small>{% trans "Previously on Modoboa..." %}</small></h2><hr>
<table class="table">
<thead>
<tr>
<th class="sortable" data-sort_order="date_created" width="20%">{% trans "Date" %}</th>
<th class="sortable" data-sort_order="level" width="7%">{% trans "Level" %}</th>
<th class="sortable" data-sort_order="logger" width="15%">{% trans "Logger" %}</th>
<th class="sortable" data-sort_order="message" width="">{% trans "Message" %}</th>
</tr>
</thead>
<tbody>
{% for l in logs %}<tr>
<td>{{ l.date_created }}</td>
<td>{{ l.level|colorize_level|safe }}</td>
<td>{{ l.logger }}</td>
<td>{{ l.message|tohtml|safe }}</td>
</tr>{% endfor %}
</tbody>
</table>
@@ -1,4 +1,4 @@
{% load i18n libextras admin_extras %}
{% load i18n libextras admin_tags %}
<table class="table table-fixed">
<thead>
<tr>
@@ -1,23 +0,0 @@
{% extends "twocols.html" %}
{% load i18n libextras admin_extras %}
{% block pagetitle %}{% trans "Modoboa settings" %}{% endblock %}
{% block html_head %}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}admin/css/settings.css" />
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/jquery.sortable.css" />
<script type="text/javascript" src="{{ STATIC_URL }}js/history.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/listing.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/twocols_nav.js"></script>
<script type="text/javascript" src="{{ STATIC_URL }}js/jquery.sortable.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var settings = new TwocolsNav({
deflocation: "parameters/",
formid: "settings_form",
divid: "settings_content",
reload_exceptions: ["extensions"]
});
});
</script>
{% endblock %}
{% block leftcol %}<div class="well sidebar-nav">{% settings_menu left_selection user %}</div>{% endblock %}
{% block apparea %}<div id="settings_content"></div>{% endblock %}
@@ -106,19 +106,19 @@ def identities_menu(user):
@register.simple_tag
def domain_actions(user, domid):
from modoboa.admin.models import Domain
from modoboa.extensions.admin.models import Domain
domain = Domain.objects.get(pk=domid)
actions = [
{"name": "listidentities",
"url": reverse("modoboa.admin.views.identities") + "#list/?searchquery=@%s" % domain.name,
"url": reverse("modoboa.extensions.admin.views.identity.identities") + "#list/?searchquery=@%s" % domain.name,
"title": _("View the domain's identities"),
"img": "icon-user"}
]
if user.has_perm("admin.delete_domain"):
actions.append({
"name": "deldomain",
"url": reverse("modoboa.admin.views.deldomain", args=[domid]),
"url": reverse("modoboa.extensions.admin.views.domain.deldomain", args=[domid]),
"title": _("Delete the domain"),
"img": "icon-trash"
})
@@ -132,72 +132,78 @@ def identity_actions(user, ident):
if name == "User":
actions = events.raiseQueryEvent("ExtraAccountActions", ident)
actions += [
{"name" : "delaccount",
"url" : reverse("modoboa.admin.views.delaccount", args=[objid]),
"img" : "icon-trash",
"title" : _("Delete this account")},
]
{"name": "delaccount",
"url": reverse("modoboa.extensions.admin.views.identity.delaccount", args=[objid]),
"img": "icon-trash",
"title": _("Delete this account")},
]
else:
if ident.get_recipients_count() >= 2:
actions = [
{"name" : "deldlist",
"url" : reverse("modoboa.admin.views.deldlist") + "?selection=%s" % objid,
"img" : "icon-trash",
"title" : _("Delete this distribution list")},
]
{"name": "deldlist",
"url": reverse("modoboa.extensions.admin.views.alias.deldlist") + "?selection=%s" % objid,
"img": "icon-trash",
"title": _("Delete this distribution list")},
]
elif ident.extmboxes != "":
actions = [
{"name" : "delforward",
"url" : reverse("modoboa.admin.views.delforward") + "?selection=%s" % objid,
"img" : "icon-trash",
"title" : _("Delete this forward")},
]
{"name": "delforward",
"url": reverse("modoboa.extensions.admin.views.alias.delforward") + "?selection=%s" % objid,
"img": "icon-trash",
"title": _("Delete this forward")},
]
else:
actions = [
{"name" : "delalias",
"url" : reverse("modoboa.admin.views.delalias") + "?selection=%s" % objid,
"img" : "icon-trash",
"title" : _("Delete this alias")},
]
{"name": "delalias",
"url": reverse("modoboa.extensions.admin.views.alias.delalias") + "?selection=%s" % objid,
"img": "icon-trash",
"title": _("Delete this alias")},
]
return render_actions(actions)
@register.simple_tag
def identity_modify_link(identity, active_tab='default'):
linkdef = {"label" : identity.identity, "modal" : True}
linkdef = {"label": identity.identity, "modal": True}
if identity.__class__.__name__ == "User":
linkdef["url"] = reverse("modoboa.admin.views.editaccount", args=[identity.id])
linkdef["url"] = reverse(
"modoboa.extensions.admin.views.identity.editaccount",
args=[identity.id]
)
linkdef["url"] += "?active_tab=%s" % active_tab
linkdef["modalcb"] = "admin.editaccount_cb"
else:
linkdef["url"] = reverse("modoboa.admin.views.editalias", args=[identity.id])
linkdef["url"] = reverse(
"modoboa.extensions.admin.views.alias.editalias",
args=[identity.id]
)
linkdef["modalcb"] = "admin.aliasform_cb"
return render_link(linkdef)
@register.simple_tag
def domadmin_actions(daid, domid):
actions = [dict(
name="removeperm",
url=reverse("modoboa.admin.views.remove_permission") + "?domid=%s&daid=%s" % (domid, daid),
img="icon-trash",
title=_("Remove this permission")
)]
actions = [{
"name": "removeperm",
"url": reverse("modoboa.extensions.admin.views.identity.remove_permission") + "?domid=%s&daid=%s" % (domid, daid),
"img": "icon-trash",
"title": _("Remove this permission")
}]
return render_actions(actions)
@register.simple_tag
def loadadminextmenu(user):
menu = events.raiseQueryEvent("AdminMenuDisplay", "admin_menu_box", user)
return render_to_string('common/menulist.html',
{"entries" : menu, "user" : user})
return render_to_string('common/menulist.html',
{"entries": menu, "user": user})
@register.simple_tag
def param(app, definition):
name = "%s.%s" % (app, definition["name"])
value = definition.has_key("value") \
and definition["value"] or definition["deflt"]
value = definition["value"] \
if "value" in definition else definition["deflt"]
if definition["type"] == "separator":
return "<h5>%s</h5>" % (unicode(definition["label"]))
@@ -211,8 +217,7 @@ def param(app, definition):
result = """<div class='control-group'%s>
<label class="param-label" for="%s">%s</label>
""" % (extratags, name,
definition.has_key("label") and _(definition["label"])
or definition["name"])
_(definition["label"]) if "label" in definition else definition["name"])
result += "<div class='param-controls'>"
if definition["type"] in ["string", "int"]:
@@ -222,7 +227,7 @@ def param(app, definition):
if definition["type"] == "text":
result += "<textarea name='%s' id='%s'>%s</textarea>" \
% (name, name, value)
if definition["type"] == "list_yesno":
for idx, v in enumerate([("yes", _("Yes")), ("no", _("No"))]):
checked = "checked" if value == v[0] else ""
@@ -230,13 +235,14 @@ def param(app, definition):
<input type="radio" name="%(name)s" id="%(id)s" value="%(value)s"%(checked)s />
%(label)s
</label>
""" % dict(id="%s_%d" % (name, idx), name=name, value=v[0], label=v[1], checked=checked)
""" % dict(id="%s_%d" % (name, idx), name=name, value=v[0],
label=v[1], checked=checked)
if definition["type"] == "list":
result += """
<select name='%s' id='%s'>""" % (name, name)
values = []
if definition.has_key("values"):
if "values" in definition:
values = definition["values"]
for v in values:
selected = ""
@@ -247,7 +253,7 @@ def param(app, definition):
</select>
"""
if definition.has_key("help"):
if "help" in definition:
result += """<a class="help" rel="popover" data-content="%s" data-original-title="%s">
<i class="icon-info-sign"></i>
</a>""" % (unicode(definition["help"]), _("Help"))
@@ -264,7 +270,7 @@ def param(app, definition):
@register.filter
def gender(value, target):
if genders.has_key(value):
if value in genders:
trans = target == "m" and genders[value][0] or genders[value][1]
if trans.find("_") == -1:
return trans
@@ -273,5 +279,7 @@ def gender(value, target):
@register.simple_tag
def get_extra_admin_content(user, target, currentpage):
res = events.raiseQueryEvent("ExtraAdminContent", user, target, currentpage)
res = events.raiseQueryEvent(
"ExtraAdminContent", user, target, currentpage
)
return "".join(res)
+2 -2
View File
@@ -15,10 +15,10 @@ from django.test.client import Client
from django.utils import simplejson
from django.core.urlresolvers import reverse
from django.core.files.base import ContentFile
from models import *
from modoboa.admin.lib import *
from modoboa.lib import parameters
from modoboa.lib.tests import ModoTestCase
from modoboa.extensions.admin.lib import *
from modoboa.extensions.admin.models import *
class DomainTestCase(ModoTestCase):
+1
View File
@@ -3,6 +3,7 @@ from django.conf.urls import patterns, url
urlpatterns = patterns(
'modoboa.extensions.admin.views.domain',
(r'^$', 'index'),
url(r'^domains/$', 'domains', name="domains"),
(r'^domains/list/$', '_domains'),
(r'^domains/flatlist/$', 'domains_list'),
+7 -2
View File
@@ -22,13 +22,18 @@ from modoboa.extensions.admin.forms import (
)
@login_required
def index(request):
return HttpResponseRedirect(reverse(domains))
@login_required
@user_passes_test(
lambda u: u.has_perm("admin.view_domains") or u.has_perm("admin.view_mailboxes")
)
def _domains(request):
sort_order, sort_dir = get_sort_order(request.GET, "name")
domains = request.user.get_domains()
domains = Domain.objects.get_for_admin(request.user)
squery = request.GET.get("searchquery", None)
if squery is not None:
q = Q(name__contains=squery)
@@ -60,7 +65,7 @@ def domains(request, tplname="admin/domains.html"):
@login_required
@permission_required("admin.add_user")
def domains_list(request):
doms = [dom.name for dom in request.user.get_domains()]
doms = [dom.name for dom in Domain.objects.get_for_admin(request.user)]
return ajax_simple_response(doms)
+4 -2
View File
@@ -8,6 +8,8 @@ from django.utils.translation import ugettext as _, ungettext
from django.contrib.auth.decorators import (
login_required, permission_required, user_passes_test
)
from modoboa.extensions.admin.lib import get_identities
from modoboa.extensions.admin.models import Domain
from modoboa.extensions.admin.forms import (
ExportIdentitiesForm, ExportDomainsForm
)
@@ -45,7 +47,7 @@ def export_identities(request):
form.is_valid()
fp = cStringIO.StringIO()
csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"])
for ident in request.user.get_identities():
for ident in get_identities(request.user):
ident.to_csv(csvwriter)
content = fp.getvalue()
fp.close()
@@ -71,7 +73,7 @@ def export_domains(request):
form.is_valid()
fp = cStringIO.StringIO()
csvwriter = csv.writer(fp, delimiter=form.cleaned_data["sepchar"])
for dom in request.user.get_domains():
for dom in Domain.objects.get_for_admin(request.user):
dom.to_csv(csvwriter)
for da in dom.domainalias_set.all():
da.to_csv(csvwriter)
+9 -4
View File
@@ -12,7 +12,10 @@ from modoboa.lib.webutils import ajax_simple_response, _render_to_string
from modoboa.lib.formutils import CreationWizard
from modoboa.lib.templatetags.libextras import pagination_bar
from modoboa.core.models import User
from modoboa.extensions.admin.lib import get_sort_order, get_listing_page
from modoboa.extensions.admin.models import Mailbox
from modoboa.extensions.admin.lib import (
get_sort_order, get_listing_page, get_identities
)
from modoboa.extensions.admin.exceptions import AdminError
from modoboa.extensions.admin.forms import (
AccountForm, AccountFormGeneral, AccountFormMail
@@ -24,7 +27,7 @@ from modoboa.extensions.admin.forms import (
lambda u: u.has_perm("admin.add_user") or u.has_perm("admin.add_alias")
)
def _identities(request):
idents_list = request.user.get_identities(request.GET)
idents_list = get_identities(request.user, request.GET)
sort_order, sort_dir = get_sort_order(request.GET, "identity",
["identity", "name_or_rcpt", "tags"])
if sort_order in ["identity", "name_or_rcpt"]:
@@ -73,7 +76,9 @@ def list_quotas(request, tplname="admin/quotas.html"):
from modoboa.lib.dbutils import db_type
sort_order, sort_dir = get_sort_order(request.GET, "address")
mboxes = request.user.get_mailboxes(request.GET.get("searchquery", None))
mboxes = Mailbox.objects.get_for_admin(
request.user, request.GET.get("searchquery", None)
)
mboxes = mboxes.exclude(quota=0)
if sort_order in ["address", "quota", "quota_value__bytes"]:
mboxes = mboxes.order_by("%s%s" % (sort_dir, sort_order))
@@ -175,7 +180,7 @@ def editaccount(request, accountid, tplname="common/tabforms.html"):
if not request.user.can_access(account):
raise PermDeniedException
mb = None
if account.has_mailbox:
if account.mailbox_set.count():
mb = account.mailbox_set.all()[0]
instances = dict(general=account, mail=mb, perms=account)
+1 -1
View File
@@ -5,7 +5,7 @@ from django.forms.widgets import RadioFieldRenderer, RadioSelect, RadioInput
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
from django.utils.encoding import force_unicode
from modoboa.admin.templatetags.admin_extras import gender
from modoboa.extensions.admin.templatetags.admin_tags import gender
class FiltersSetForm(forms.Form):
name = forms.CharField()
+1 -1
View File
@@ -22,7 +22,7 @@ class Stats(ModoExtension):
def load(self):
from app_settings import ParametersForm
events.registerEvent("GetGraphSets")
events.declare(["GetGraphSets"])
parameters.register(ParametersForm, ugettext_lazy("Graphical statistics"))
def destroy(self):
+31 -42
View File
@@ -16,47 +16,27 @@ from django.conf import settings
events = [
"CanCreate",
"CreateDomain",
"DomainModified",
"DeleteDomain",
"DomainAliasCreated",
"DomainAliasDeleted",
"CreateMailbox",
"DeleteMailbox",
"ModifyMailbox",
"MailboxAliasCreated",
"MailboxAliasDeleted",
"AccountCreated",
"AccountModified",
"AccountDeleted",
"AccountExported",
"AccountImported",
"PasswordUpdated",
"ExtraAccountActions",
"RoleChanged",
"GetExtraRoles",
"PasswordChange",
"UserMenuDisplay",
"AdminMenuDisplay",
"GetStaticContent",
"ExtraAccountForm",
"CheckExtraAccountForm",
"FillAccountInstances",
"ExtraDomainForm",
"FillDomainInstances",
"GetExtraRoles",
"ExtEnabled",
"ExtDisabled",
"UserLogin",
"UserLogout",
"GetAnnouncement",
"TopNotifications",
@@ -64,17 +44,20 @@ events = [
"ExtraUprefsRoutes",
"ExtraUprefsJS"
]
]
callbacks = {}
def registerEvent(name):
"""Register a new event
:param name: the event's name
def declare(nevents):
"""Declare new events
:param list nevents: a list of event names
"""
if not name in events:
events.append(name)
for evt in nevents:
if not evt in events:
events.append(evt)
def register(event, callback):
"""Register a plugin callback for a specific event
@@ -90,10 +73,11 @@ def register(event, callback):
if not event in callbacks.keys():
callbacks[event] = {}
fullname = "%s.%s" % (callback.__module__, callback.__name__)
if not callbacks[event].has_key(fullname):
if not fullname in callbacks[event]:
callbacks[event][fullname] = callback
return 1
class observe(object):
"""Event observing decorator
@@ -111,7 +95,7 @@ class observe(object):
"""
def __init__(self, *evtnames, **kwargs):
self.evtnames = evtnames
if kwargs.has_key("extname"):
if "extname" in kwargs:
self.extname = kwargs["extname"]
def __guess_extension_name(self, modname):
@@ -123,7 +107,9 @@ class observe(object):
def __call__(self, f):
modname = inspect.getmodule(inspect.stack()[1][0]).__name__
extname = self.extname if hasattr(self, "extname") else self.__guess_extension_name(modname)
extname = self.extname if hasattr(self, "extname") \
else self.__guess_extension_name(modname)
@wraps(f)
def wrapped_f(*args):
if extname:
@@ -140,12 +126,12 @@ class observe(object):
return []
elif not modname in settings.MODOBOA_APPS:
return []
return f(*args)
map(lambda evt: register(evt, wrapped_f), self.evtnames)
for evt in self.evtnames:
register(evt, wrapped_f)
return wrapped_f
def unregister(event, callback):
"""Unregister a callback for a specific event
@@ -154,7 +140,7 @@ def unregister(event, callback):
"""
if not event in events:
return False
if not callbacks.has_key(event):
if not event in callbacks:
return False
fullname = "%s.%s" % (callback.__module__, callback.__name__)
try:
@@ -162,6 +148,7 @@ def unregister(event, callback):
except KeyError:
pass
def raiseEvent(event, *args):
"""Raise a specific event
@@ -172,10 +159,11 @@ def raiseEvent(event, *args):
"""
if not event in events or not event in callbacks.keys():
return 0
for name, callback in callbacks[event].iteritems():
for callback in callbacks[event].values():
callback(*args)
return 1
def raiseQueryEvent(event, *args):
"""Raise a specific event and wait for answers from callbacks
@@ -187,10 +175,11 @@ def raiseQueryEvent(event, *args):
result = []
if not event in events or not event in callbacks.keys():
return result
for name, callback in callbacks[event].iteritems():
for callback in callbacks[event].values():
result += callback(*args)
return result
def raiseDictEvent(event, *args):
"""Raise a specific event and return result as a dictionnary
@@ -203,7 +192,7 @@ def raiseDictEvent(event, *args):
result = {}
if not event in events or not event in callbacks.keys():
return result
for name, callback in callbacks[event].iteritems():
for callback in callbacks[event].values():
tmp = callback(*args)
for k, v in tmp.iteritems():
result[k] = v
+10 -14
View File
@@ -7,18 +7,10 @@ from modoboa.core.models import ObjectAccess
import events
from exceptions import ModoboaException
def get_content_type(obj):
"""Simple function that use the right method to retrieve a content type
:param obj: a django model
:return: a ``ContentType`` object
"""
return obj.get_content_type() if hasattr(obj, "get_content_type") \
else ContentType.objects.get_for_model(obj)
def get_account_roles(user):
"""Return the list of supported account roles
This list can be extended by extensions which listen to the
``GetExtraRoles`` event.
@@ -32,6 +24,7 @@ def get_account_roles(user):
std_roles += events.raiseQueryEvent("GetExtraRoles", user)
return sorted(std_roles, key=lambda role: role[1])
def grant_access_to_object(user, obj, is_owner=False):
"""Grant access to an object for a given user
@@ -48,7 +41,7 @@ def grant_access_to_object(user, obj, is_owner=False):
:param obj: an admin. object (Domain, Mailbox, ...)
:param is_owner: the user is the unique object's owner
"""
ct = get_content_type(obj)
ct = ContentType.objects.get_for_model(obj)
try:
entry = user.objectaccess_set.get(content_type=ct, object_id=obj.id)
entry.is_owner = is_owner
@@ -62,7 +55,7 @@ def grant_access_to_object(user, obj, is_owner=False):
ObjectAccess.objects.create(user=user, content_type=ct,
object_id=obj.id, is_owner=is_owner)
if is_owner:
from modoboa.admin.models import User
from modoboa.core.models import User
for su in User.objects.filter(is_superuser=True):
if su == user:
continue
@@ -74,6 +67,7 @@ def grant_access_to_object(user, obj, is_owner=False):
except IntegrityError, e:
raise ModoboaException(_("Failed to grant access (%s)" % str(e)))
def grant_access_to_objects(user, objects, ct):
"""Grant access to a collection of objects
@@ -100,7 +94,7 @@ def ungrant_access_to_object(obj, user=None):
:param obj: an object inheriting from ``models.Model``
:param user: a ``User`` object
"""
ct = get_content_type(obj)
ct = ContentType.objects.get_for_model(obj)
if user:
try:
ObjectAccess.objects.get(user=user, content_type=ct, object_id=obj.id).delete()
@@ -109,22 +103,24 @@ def ungrant_access_to_object(obj, user=None):
else:
ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete()
def ungrant_access_to_objects(objects):
"""Cancel all accesses for a given objects list
:param objects: a list of objects inheriting from ``model.Model``
"""
for obj in objects:
ct = get_content_type(obj)
ct = ContentType.objects.get_for_model(obj)
ObjectAccess.objects.filter(content_type=ct, object_id=obj.id).delete()
def get_object_owner(obj):
"""Return the unique owner of this object
:param obj: an object inheriting from ``model.Model``
:return: a ``User`` object
"""
ct = get_content_type(obj)
ct = ContentType.objects.get_for_model(obj)
try:
entry = ObjectAccess.objects.get(content_type=ct, object_id=obj.id, is_owner=True)
except ObjectAccess.DoesNotExist: