diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py index cc5e8f4f3..c31147e56 100644 --- a/modoboa/admin/models/alias.py +++ b/modoboa/admin/models/alias.py @@ -1,5 +1,6 @@ """Models related to aliases management.""" +from django.core.urlresolvers import reverse from django.db import models from django.utils.encoding import python_2_unicode_compatible, smart_text from django.utils.translation import ugettext as _, ugettext_lazy @@ -84,6 +85,10 @@ class Alias(AdminObject): altype = self.type return [{"name": altype, "label": labels[altype], "type": "idt"}] + def get_absolute_url(self): + """Return detail url for this alias.""" + return reverse("admin:alias_detail", args=[self.pk]) + def post_create(self, creator): from modoboa.lib.permissions import grant_access_to_object super(Alias, self).post_create(creator) diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py index 31d6adfce..107e8f939 100644 --- a/modoboa/admin/models/mailbox.py +++ b/modoboa/admin/models/mailbox.py @@ -155,13 +155,11 @@ class Mailbox(AdminObject): :rtype: list of string """ - aliases = [] qset = ( self.aliasrecipient_set.select_related("alias") .filter(alias__internal=False) ) - for alr in qset: - aliases += [alr.alias.address] + aliases = [alr.alias.address for alr in qset] return aliases @property @@ -235,8 +233,8 @@ class Mailbox(AdminObject): self.quota = 0 elif int(value) > self.domain.quota and not override_rules: raise lib_exceptions.BadRequest( - _("Quota is greater than the allowed domain's limit (%dM)") - % self.domain.quota + _("Quota is greater than the allowed domain's limit (%dM)") % + self.domain.quota ) else: self.quota = value diff --git a/modoboa/admin/signals.py b/modoboa/admin/signals.py index db0f98c35..7d9be0803 100644 --- a/modoboa/admin/signals.py +++ b/modoboa/admin/signals.py @@ -7,3 +7,5 @@ extra_domain_actions = django.dispatch.Signal( providing_args=["user", "domain"]) extra_domain_dashboard_widgets = django.dispatch.Signal( providing_args=["user", "domain"]) +extra_account_dashboard_widgets = django.dispatch.Signal( + providing_args=["user", "account"]) diff --git a/modoboa/admin/templates/admin/account_detail.html b/modoboa/admin/templates/admin/account_detail.html new file mode 100644 index 000000000..fa85b5339 --- /dev/null +++ b/modoboa/admin/templates/admin/account_detail.html @@ -0,0 +1,91 @@ +{% extends "admin/header.html" %} + +{% load i18n %} +{% load admin_tags %} +{% load lib_tags %} + +{% block leftcol %} + + + {% get_extra_admin_content user "leftcol" selection %} +{% endblock %} + +{% block apparea %} +

{{ object }}{% if not object.is_active %} {% trans "(disabled)" %}{% endif %}

+
+
+
+
+
+

{% trans "Summary" %}

+
+ + + + + + + + + + + + + + + + + + + + {% if object.mailbox %} + + + + + {% endif %} +
{% trans "Full name" %}{{ object.fullname }}
{% trans "Role" %}{{ object.role }}
{% trans "Created" %}{{ object.date_joined|date:"SHORT_DATETIME_FORMAT" }}
{% trans "Last login" %}{{ object.last_login|date:"SHORT_DATETIME_FORMAT" }}
{% trans "Secondary email" %}{{ object.secondary_email|default:_("Not defined") }}
{% trans "Phone number" %}{{ object.phone_number|default:_("Not defined") }}
{% trans "Quota" %}{% include "common/progressbar.html" with id='quotabar' value=object.mailbox.get_quota_in_percent %}
+
+ + {% if domains %} +
+
+

{% trans "Administered domains" %}

+
+ + {% for domain in domains %} + + {% empty %} + + {% endfor %} +
{{ domain }}
{% trans "No domain defined." %}
+
+ {% endif %} + + {% for template in templates.left %} + {% include template %} + {% endfor %} +
+
+ {% if object.mailbox %} +
+
+

{% trans "Aliases" %}

+
+ + {% for alias in object.mailbox.alias_addresses %} + + {% empty %} + + {% endfor %} +
{{ alias }}
{% trans "No alias defined" %}
+
+ {% endif %} + {% for template in templates.right %} + {% include template %} + {% endfor %} +
+
+{% endblock %} diff --git a/modoboa/admin/templates/admin/alias_detail.html b/modoboa/admin/templates/admin/alias_detail.html new file mode 100644 index 000000000..01d49a6dc --- /dev/null +++ b/modoboa/admin/templates/admin/alias_detail.html @@ -0,0 +1,39 @@ +{% extends "admin/header.html" %} + +{% load i18n %} +{% load admin_tags %} +{% load lib_tags %} + +{% block leftcol %} + + + {% get_extra_admin_content user "leftcol" selection %} +{% endblock %} + +{% block apparea %} +

{% trans "Alias" %} {{ object }}{% if not object.enabled %} {% trans "(disabled)" %}{% endif %}

+
+
+
+
+
+

{% trans "Summary" %}

+
+
+
+
{% trans "Created" %}
{{ alias.creation|date:"SHORT_DATETIME_FORMAT" }}
+
{% trans "Modified" %}
{{ alias.last_modification|date:"SHORT_DATETIME_FORMAT" }}
+
+
+ + + {% for recipient in object.recipients %} + + {% endfor %} +
{% trans "Recipients" %}
{{ recipient }}
+
+
+
+{% endblock %} diff --git a/modoboa/admin/templates/admin/domain_detail.html b/modoboa/admin/templates/admin/domain_detail.html index cbac67289..3b71ed193 100644 --- a/modoboa/admin/templates/admin/domain_detail.html +++ b/modoboa/admin/templates/admin/domain_detail.html @@ -12,13 +12,13 @@ {% endblock %} {% block apparea %} -

{{ object }} {% trans "Overview" %}

+

{{ object }} {% if not object.enabled %} {% trans "(disabled)" %}{% endif %}


-

{% trans "Information" %}

+

{% trans "Summary" %}

@@ -35,12 +35,21 @@
- {% for template in templates.left %} -
-
- {% include template %} -
+ {% if object.domainalias_set.exists %} +
+
+

{% trans "Domain aliases" %}

+ + {% for domainalias in object.domainalias_set.all %} + + {% endfor %} +
{{ domainalias }}
+
+ {% endif %} + + {% for template in templates.left %} + {% include template %} {% endfor %}
@@ -54,7 +63,11 @@ {{ da.username }} {{ da.fullname }} - {% if perms.admin.change_domain %}{% domadmin_actions da.id domain.id %}{% endif %} + + {% if perms.admin.change_domain %} + + {% endif %} + {% empty %} {% trans "No domain administrator defined yet." %} diff --git a/modoboa/admin/templates/admin/identities.html b/modoboa/admin/templates/admin/identities.html index a8bfa985b..d336aa7ad 100644 --- a/modoboa/admin/templates/admin/identities.html +++ b/modoboa/admin/templates/admin/identities.html @@ -23,4 +23,16 @@ $(document).ready(function() { {% extra_static_content "identities" "js" user %} {% endblock %} +{% block toolbar_right %} +
+ + +
+{% endblock %} + {% block actions %}{% identities_menu user "identities" %}{% endblock %} diff --git a/modoboa/admin/templates/admin/identities_table.html b/modoboa/admin/templates/admin/identities_table.html index 15fad1e00..eb8c96700 100644 --- a/modoboa/admin/templates/admin/identities_table.html +++ b/modoboa/admin/templates/admin/identities_table.html @@ -1,8 +1,11 @@ {% load i18n admin_tags lib_tags %} + {% for ident in identities %} {% check_identity_status ident as identity_is_enabled %} - {% identity_modify_link ident %}{% if not identity_is_enabled %} (disabled){% endif %} + + {{ ident }}{% if not identity_is_enabled %} {% trans "(disabled)" %}{% endif %} + {{ ident.name_or_rcpt }} {% render_tags ident.tags %} {% identity_actions user ident %} diff --git a/modoboa/admin/templates/admin/listing.html b/modoboa/admin/templates/admin/listing.html index a5c5c7dbb..8caca0a91 100644 --- a/modoboa/admin/templates/admin/listing.html +++ b/modoboa/admin/templates/admin/listing.html @@ -30,7 +30,10 @@ var admin;
{% include "admin/searchform.html" %}
-
+
+ {% block toolbar_right %}{% endblock %} +
+
diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py index dc9fdbb67..be7e380d9 100644 --- a/modoboa/admin/templatetags/admin_tags.py +++ b/modoboa/admin/templatetags/admin_tags.py @@ -1,6 +1,4 @@ -""" -Admin extension tags. -""" +"""Admin extension tags.""" from django import template from django.core.urlresolvers import reverse @@ -81,48 +79,32 @@ def domains_menu(selection, user, ajax_mode=True): @register.simple_tag -def identities_menu(user, selection=None): +def identities_menu(user, selection=None, ajax_mode=True): """Menu specific to the Identities page. :param ``User`` user: the connecter user :rtype: str :return: the rendered menu """ + nav_classes = "navigation" + if ajax_mode: + identity_list_url = "list/" + quota_list_url = "quotas/" + nav_classes += " ajaxnav" + else: + identity_list_url = reverse("admin:identity_list") + quota_list_url = identity_list_url + "#quotas/" entries = [ {"name": "identities", "label": _("List identities"), "img": "fa fa-user", - "class": "ajaxnav navigation", - "url": "list/"}, + "class": nav_classes, + "url": identity_list_url}, {"name": "quotas", "label": _("List quotas"), "img": "fa fa-hdd-o", - "class": "ajaxnav navigation", - "url": "quotas/"}, - {"name": "newaccount", - "label": _("Add account"), - "img": "fa fa-plus", - "modal": True, - "modalcb": "admin.newaccount_cb", - "url": reverse("admin:account_add")}, - {"name": "newalias", - "label": _("Add alias"), - "img": "fa fa-plus", - "modal": True, - "modalcb": "admin.aliasform_cb", - "url": reverse("admin:alias_add")}, - {"name": "newforward", - "label": _("Add forward"), - "img": "fa fa-plus", - "modal": True, - "modalcb": "admin.aliasform_cb", - "url": reverse("admin:forward_add")}, - {"name": "newdlist", - "label": _("Add distribution list"), - "img": "fa fa-plus", - "modal": True, - "modalcb": "admin.aliasform_cb", - "url": reverse("admin:dlist_add")}, + "class": nav_classes, + "url": quota_list_url}, {"name": "import", "label": _("Import"), "img": "fa fa-folder-open", @@ -134,8 +116,7 @@ def identities_menu(user, selection=None): "img": "fa fa-share-alt", "url": reverse("admin:identity_export"), "modal": True, - "modalcb": "admin.exportform_cb" - } + "modalcb": "admin.exportform_cb"} ] return render_to_string('common/menulist.html', { @@ -185,7 +166,17 @@ def identity_actions(user, ident): objid = ident.id if name == "User": actions = events.raiseQueryEvent("ExtraAccountActions", ident) + url = ( + reverse("admin:account_change", args=[objid]) + + "?active_tab=default" + ) actions += [ + {"name": "changeaccount", + "url": url, + "img": "fa fa-edit", + "modal": True, + "modalcb": "admin.editaccount_cb", + "title": _("Edit {}").format(ident.username)}, {"name": "delaccount", "url": reverse("admin:account_delete", args=[objid]), "img": "fa fa-trash", @@ -193,6 +184,12 @@ def identity_actions(user, ident): ] else: actions = [ + {"name": "changealias", + "url": reverse("admin:alias_change", args=[objid]), + "img": "fa fa-edit", + "modal": True, + "modalcb": "admin.aliasform_cb", + "title": _("Edit {}").format(ident)}, {"name": "delalias", "url": "{}?selection={}".format( reverse("admin:alias_delete"), objid), @@ -216,20 +213,6 @@ def check_identity_status(identity): return True -@register.simple_tag -def domain_modify_link(domain): - linkdef = {"label": domain.name, "modal": True} - if domain.__class__.__name__ == "Domain": - linkdef["url"] = reverse( - "admin:domain_change", args=[domain.id]) - linkdef["modalcb"] = "admin.domainform_cb" - else: - tmp = events.raiseDictEvent('GetDomainModifyLink', domain) - for key in ['url', 'modalcb']: - linkdef[key] = tmp[key] - return render_link(linkdef) - - @register.simple_tag def domain_aliases(domain): """Display domain aliases of this domain. diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py index 2c47ea9f4..84787ff0c 100644 --- a/modoboa/admin/tests/test_account.py +++ b/modoboa/admin/tests/test_account.py @@ -154,6 +154,22 @@ class AccountTestCase(ModoTestCase): reverse("admin:account_add"), values, status=400 ) + def test_account_detail_view(self): + """Test account detail view.""" + parameters.save_admin( + "ENABLE_ADMIN_LIMITS", "no", app="limits") + account = User.objects.get(username="admin@test.com") + url = reverse("admin:account_detail", args=[account.pk]) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertIn("Summary", response.content) + self.assertIn("Administered domains", response.content) + self.assertNotIn("Resources usage", response.content) + parameters.save_admin( + "ENABLE_ADMIN_LIMITS", "yes", app="limits") + response = self.client.get(url) + self.assertIn("Resources usage", response.content) + @override_settings(AUTHENTICATION_BACKENDS=( 'modoboa.lib.authbackends.LDAPBackend', @@ -305,7 +321,7 @@ class PermissionsTestCase(ModoTestCase): recipients_1="user@test2.com", enabled=True ) - self.ajax_post(reverse("admin:dlist_add"), values) + self.ajax_post(reverse("admin:alias_add"), values) def test_domainadmin_master_user(self): """Check domain administrator is not allowed to access this feature.""" diff --git a/modoboa/admin/tests/test_alias.py b/modoboa/admin/tests/test_alias.py index ad9ceec7a..3a7a4b162 100644 --- a/modoboa/admin/tests/test_alias.py +++ b/modoboa/admin/tests/test_alias.py @@ -55,7 +55,7 @@ class AliasTestCase(ModoTestCase): "enabled": True } self.ajax_post( - reverse("admin:dlist_add"), values + reverse("admin:alias_add"), values ) alias = Alias.objects.get(address="badalias@test.com") self.assertEqual(alias.recipients_count, 1) @@ -132,7 +132,7 @@ class AliasTestCase(ModoTestCase): recipients_2="ext@titi.com", enabled=True) self.ajax_post( - reverse("admin:dlist_add"), values + reverse("admin:alias_add"), values ) user = User.objects.get(username="user@test.com") mb = user.mailbox @@ -164,7 +164,7 @@ class AliasTestCase(ModoTestCase): def test_forward(self): values = dict(address="forward2@test.com", recipients="rcpt@dest.com") self.ajax_post( - reverse("admin:forward_add"), values + reverse("admin:alias_add"), values ) fwd = Alias.objects.get(address="forward2@test.com") self.assertEqual(fwd.recipients_count, 1) @@ -187,7 +187,7 @@ class AliasTestCase(ModoTestCase): def test_forward_and_local_copies(self): values = dict(address="user@test.com", recipients="rcpt@dest.com") self.ajax_post( - reverse("admin:forward_add"), values + reverse("admin:alias_add"), values ) fwd = Alias.objects.get(address="user@test.com", internal=False) self.assertEqual(fwd.recipients_count, 1) @@ -211,18 +211,21 @@ class AliasTestCase(ModoTestCase): "enabled": True } self.ajax_post( - reverse("admin:dlist_add"), values + reverse("admin:alias_add"), values ) def test_distribution_list_deletion_on_user_update_bug(self): - """This test demonstrates an issue with distribution list being deleted - when one of the users which belong to that list is changed.""" + """This test demonstrates an issue with distribution list being + deleted when one of the users which belong to that list is + changed. + + """ values = dict(address="list@test.com", recipients="user@test.com", recipients_1="admin@test.com", enabled=True) self.ajax_post( - reverse("admin:dlist_add"), values + reverse("admin:alias_add"), values ) user = User.objects.get(username="user@test.com") @@ -233,4 +236,14 @@ class AliasTestCase(ModoTestCase): ) self.ajax_post(reverse("admin:account_change", args=[user.id]), values) - self.assertEqual(Alias.objects.filter(address="list@test.com").count(), 1) + self.assertEqual( + Alias.objects.filter(address="list@test.com").count(), 1) + + def test_alias_detail_view(self): + """Test alias detail view.""" + account = Alias.objects.get(address="postmaster@test.com") + url = reverse("admin:alias_detail", args=[account.pk]) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + self.assertIn("Summary", response.content) + self.assertIn("Recipients", response.content) diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index 57c11c2ac..cef7c0f54 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -155,7 +155,7 @@ class DomainTestCase(ModoTestCase): url = reverse("admin:domain_detail", args=[domain.pk]) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Information", response.content) + self.assertIn("Summary", response.content) self.assertIn("Administrators", response.content) self.assertNotIn("Resources usage", response.content) parameters.save_admin( diff --git a/modoboa/admin/urls.py b/modoboa/admin/urls.py index fb5ae7376..58dbee490 100644 --- a/modoboa/admin/urls.py +++ b/modoboa/admin/urls.py @@ -37,20 +37,21 @@ urlpatterns += patterns( url(r'^accounts/list/$', 'accounts_list', name="account_list"), url(r'^accounts/new/$', 'newaccount', name="account_add"), - url(r'^accounts/edit/(?P\d+)/$', 'editaccount', + url(r'^accounts/(?P\d+)/$', views.AccountDetailView.as_view(), + name="account_detail"), + url(r'^accounts/(?P\d+)/edit/$', 'editaccount', name="account_change"), - url(r'^accounts/delete/(?P\d+)/$', 'delaccount', + url(r'^accounts/(?P\d+)/delete/$', 'delaccount', name="account_delete"), ) urlpatterns += patterns( 'modoboa.admin.views.alias', - url(r'^distriblists/new/$', 'newdlist', name="dlist_add"), - url(r'^forwards/new/$', 'newforward', name="forward_add"), - url(r'^aliases/new/$', 'newalias', name="alias_add"), - url(r'^aliases/edit/(?P\d+)/$', 'editalias', name="alias_change"), + url(r'^aliases/(?P\d+)/$', views.AliasDetailView.as_view(), + name="alias_detail"), + url(r'^aliases/(?P\d+)/edit/$', 'editalias', name="alias_change"), url(r'^aliases/delete/$', 'delalias', name="alias_delete"), ) diff --git a/modoboa/admin/views/__init__.py b/modoboa/admin/views/__init__.py index 008fb6858..9fa96c833 100644 --- a/modoboa/admin/views/__init__.py +++ b/modoboa/admin/views/__init__.py @@ -1,10 +1,14 @@ """Shortcuts.""" +from .alias import AliasDetailView +from .identity import AccountDetailView from .dnsbl import DNSBLDomainDetailView from .domain import DomainDetailView __all__ = [ + "AccountDetailView", + "AliasDetailView", "DNSBLDomainDetailView", "DomainDetailView", ] diff --git a/modoboa/admin/views/alias.py b/modoboa/admin/views/alias.py index 571eada92..7332822d1 100644 --- a/modoboa/admin/views/alias.py +++ b/modoboa/admin/views/alias.py @@ -1,12 +1,15 @@ """Alias related views.""" -from django.contrib.auth.decorators import ( - login_required, permission_required -) from django.core.urlresolvers import reverse from django.db import IntegrityError from django.shortcuts import render from django.utils.translation import ugettext as _, ungettext +from django.views import generic + +from django.contrib.auth import mixins as auth_mixins +from django.contrib.auth.decorators import ( + login_required, permission_required +) from reversion import revisions as reversion @@ -59,17 +62,6 @@ def _new_alias(request, title, action, successmsg, return render(request, tplname, ctx) -@login_required -@permission_required("admin.add_alias") -@reversion.create_revision() -def newdlist(request): - return _new_alias( - request, _("New distribution list"), - reverse("admin:dlist_add"), - _("Distribution list created") - ) - - @login_required @permission_required("admin.add_alias") @reversion.create_revision() @@ -80,16 +72,6 @@ def newalias(request): ) -@login_required -@permission_required("admin.add_alias") -@reversion.create_revision() -def newforward(request): - return _new_alias( - request, _("New forward"), reverse("admin:forward_add"), - _("Forward created") - ) - - @login_required @permission_required("admin.change_alias") @reversion.create_revision() @@ -140,3 +122,24 @@ def delalias(request): msg = ungettext(msg, msgs, len(selection)) return render_to_json_response(msg) + + +class AliasDetailView( + auth_mixins.PermissionRequiredMixin, generic.DetailView): + """DetailView for Alias.""" + + model = Alias + permission_required = "admin.add_alias" + + def has_permission(self): + """Check object-level access.""" + result = super(AliasDetailView, self).has_permission() + if not result: + return result + return self.request.user.can_access(self.get_object()) + + def get_context_data(self, **kwargs): + """Add information to context.""" + context = super(AliasDetailView, self).get_context_data(**kwargs) + context["selection"] = "identities" + return context diff --git a/modoboa/admin/views/identity.py b/modoboa/admin/views/identity.py index 093a25dce..9aebe1050 100644 --- a/modoboa/admin/views/identity.py +++ b/modoboa/admin/views/identity.py @@ -1,11 +1,14 @@ """Identity related views.""" +from django.shortcuts import render +from django.utils.translation import ugettext as _, ungettext +from django.views import generic +from django.views.decorators.csrf import ensure_csrf_cookie + +from django.contrib.auth import mixins as auth_mixins from django.contrib.auth.decorators import ( login_required, permission_required, user_passes_test ) -from django.shortcuts import render -from django.utils.translation import ugettext as _, ungettext -from django.views.decorators.csrf import ensure_csrf_cookie from reversion import revisions as reversion @@ -24,6 +27,7 @@ from modoboa.lib.web_utils import ( from ..forms import AccountForm, AccountWizard from ..lib import get_identities from ..models import Mailbox, Domain +from .. import signals @login_required @@ -170,8 +174,8 @@ def newaccount(request): @login_required @permission_required("core.change_user") @reversion.create_revision() -def editaccount(request, accountid, tplname="common/tabforms.html"): - account = User.objects.get(pk=accountid) +def editaccount(request, pk, tplname="common/tabforms.html"): + account = User.objects.get(pk=pk) if not request.user.can_access(account): raise PermDeniedException mb = account.mailbox if hasattr(account, "mailbox") else None @@ -183,9 +187,9 @@ def editaccount(request, accountid, tplname="common/tabforms.html"): @login_required @permission_required("core.delete_user") -def delaccount(request, accountid): +def delaccount(request, pk): keepdir = True if request.POST.get("keepdir", "false") == "true" else False - User.objects.get(pk=accountid).delete(request.user, keep_mb_dir=keepdir) + User.objects.get(pk=pk).delete(request.user, keep_mb_dir=keepdir) return render_to_json_response( ungettext("Account deleted", "Accounts deleted", 1) ) @@ -208,3 +212,36 @@ def remove_permission(request): raise PermDeniedException domain.remove_admin(account) return render_to_json_response({}) + + +class AccountDetailView( + auth_mixins.PermissionRequiredMixin, generic.DetailView): + """DetailView for Account.""" + + model = User + permission_required = "core.add_user" + template_name = "admin/account_detail.html" + + def has_permission(self): + """Check object-level access.""" + result = super(AccountDetailView, self).has_permission() + if not result: + return result + return self.request.user.can_access(self.get_object()) + + def get_context_data(self, **kwargs): + """Add information to context.""" + context = super(AccountDetailView, self).get_context_data(**kwargs) + del context["user"] + result = signals.extra_account_dashboard_widgets.send( + self.__class__, user=self.request.user, account=self.object) + context["templates"] = {"left": [], "right": []} + for receiver, widgets in result: + for widget in widgets: + context["templates"][widget["column"]].append( + widget["template"]) + context.update(widget["context"]) + if self.object.role in ["Resellers", "DomainAdmins"]: + context["domains"] = Domain.objects.get_for_admin(self.object) + context["selection"] = "identities" + return context diff --git a/modoboa/core/models.py b/modoboa/core/models.py index a7f782f02..a7bac652e 100644 --- a/modoboa/core/models.py +++ b/modoboa/core/models.py @@ -2,16 +2,18 @@ import re +from django.core.urlresolvers import reverse +from django.db import models +from django.utils import timezone +from django.utils.encoding import python_2_unicode_compatible, smart_text +from django.utils.translation import ugettext as _, ugettext_lazy + from django.contrib.auth.hashers import make_password, is_password_usable from django.contrib.auth.models import ( UserManager, Group, PermissionsMixin ) from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType -from django.db import models -from django.utils import timezone -from django.utils.encoding import python_2_unicode_compatible, smart_text -from django.utils.translation import ugettext as _, ugettext_lazy import jsonfield from reversion import revisions as reversion @@ -208,6 +210,10 @@ class User(PermissionsMixin): def has_usable_password(self): return is_password_usable(self.password) + def get_absolute_url(self): + """Return detail url for this user.""" + return reverse("admin:account_detail", args=[self.pk]) + @property def tags(self): return [{"name": "account", "label": _("account"), "type": "idt"}, @@ -314,12 +320,16 @@ class User(PermissionsMixin): @property def role(self): """Return user role.""" - if self.is_superuser: - return "SuperAdmins" - try: - return self.groups.all()[0].name - except IndexError: - return "---" + if not hasattr(self, "_role"): + self._role = None + if self._role is None: + if self.is_superuser: + self._role = "SuperAdmins" + try: + self._role = self.groups.all()[0].name + except IndexError: + self._role = "---" + return self._role @role.setter def role(self, role): @@ -345,6 +355,7 @@ class User(PermissionsMixin): from modoboa.lib.permissions import grant_access_to_object grant_access_to_object(self, self) self.save() + self._role = role def post_create(self, creator): """Grant permission on this user to creator.""" diff --git a/modoboa/core/password_hashers/advanced.py b/modoboa/core/password_hashers/advanced.py index 55915e6db..e4c4c54b9 100644 --- a/modoboa/core/password_hashers/advanced.py +++ b/modoboa/core/password_hashers/advanced.py @@ -7,6 +7,7 @@ from passlib.hash import bcrypt, md5_crypt, sha256_crypt, sha512_crypt from modoboa.core.password_hashers.base import PasswordHasher from modoboa.lib import parameters + class BLFCRYPTHasher(PasswordHasher): """ BLF-CRYPT password hasher. @@ -25,7 +26,7 @@ class BLFCRYPTHasher(PasswordHasher): # Using the parameter for rounds will generate the error # ValueError: rounds too high (bcrypt requires <= 31 rounds) # when using the bcrypt hasher. - #rounds = int(parameters.get_admin('ROUNDS_NUMBER')) + # rounds = int(parameters.get_admin('ROUNDS_NUMBER')) # To get around this, I use the default of 12. rounds = 12 return bcrypt.encrypt(clearvalue, rounds=rounds) diff --git a/modoboa/core/templatetags/core_tags.py b/modoboa/core/templatetags/core_tags.py index fa836c41c..b1ef9afc5 100644 --- a/modoboa/core/templatetags/core_tags.py +++ b/modoboa/core/templatetags/core_tags.py @@ -26,7 +26,6 @@ def core_menu(selection, user): "label": _("Modoboa"), "url": reverse("core:index")} ] - if not len(entries): return "" return render_to_string("common/menulist.html", { diff --git a/modoboa/limits/general_callbacks.py b/modoboa/limits/general_callbacks.py index 3d7ab6780..88bc51dd5 100644 --- a/modoboa/limits/general_callbacks.py +++ b/modoboa/limits/general_callbacks.py @@ -1,5 +1,6 @@ """General event callbacks.""" +from django.template.loader import render_to_string from django.utils.translation import ugettext as _ from modoboa.lib import events, parameters @@ -10,8 +11,6 @@ from . import utils @events.observe("ExtraAdminContent") def display_pool_usage(user, target, currentpage): - from django.template.loader import render_to_string - condition = ( parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or target != "leftcol" or user.is_superuser) @@ -103,11 +102,12 @@ def fill_account_instances(user, account, instances): @events.observe("GetStaticContent") def get_static_content(caller, st_type, user): - if parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no": - return [] - if caller not in ['domains', 'identities']: - return [] - if user.group == "SimpleUsers": + condition = ( + parameters.get_admin("ENABLE_ADMIN_LIMITS") == "no" or + caller not in ["domains", "identities"] or + user.role in ["SuperAdmins", "SimpleUsers"] + ) + if condition: return [] if st_type == "css": return ["""