diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py
index 96e84978b..c5579c1a3 100644
--- a/modoboa/admin/models/domain.py
+++ b/modoboa/admin/models/domain.py
@@ -179,6 +179,24 @@ class Domain(AdminObject):
return 0
return int(self.allocated_quota / float(self.quota) * 100)
+ @cached_property
+ def used_quota(self):
+ """Return current quota usage."""
+ from .mailbox import Quota
+
+ if not self.quota:
+ return 0
+ if not self.mailbox_set.exists():
+ return 0
+ return int(Quota.objects.get_domain_usage(self) / 1048576)
+
+ @cached_property
+ def used_quota_in_percent(self):
+ """Return used quota in percent."""
+ if not self.allocated_quota:
+ return 0
+ return int(self.used_quota / float(self.quota) * 100)
+
def add_admin(self, account):
"""Add a new administrator to this domain.
diff --git a/modoboa/admin/static/admin/js/admin.js b/modoboa/admin/static/admin/js/admin.js
index f3cd6cf26..c74081545 100644
--- a/modoboa/admin/static/admin/js/admin.js
+++ b/modoboa/admin/static/admin/js/admin.js
@@ -166,6 +166,14 @@ Domains.prototype = {
* @param {Object} data - response of the ajax call (JSON)
*/
list_cb: function(data) {
+ if ($(".sidebar li.active").length === 0) {
+ var menu = this.navobj.getbaseurl();
+ if (menu === "list") {
+ menu = "domains";
+ }
+ $("a[name={0}]".format(menu)).parent().addClass("active");
+ }
+ $("#objects_table thead tr").html(data.headers);
Admin.prototype.list_cb.call(this, data);
this.init_domain_links(data);
},
diff --git a/modoboa/admin/templates/admin/domain_headers.html b/modoboa/admin/templates/admin/domain_headers.html
new file mode 100644
index 000000000..e17749a05
--- /dev/null
+++ b/modoboa/admin/templates/admin/domain_headers.html
@@ -0,0 +1,10 @@
+{% load i18n %}
+{% block table_header %}
+
{% trans "Name" %} |
+ {% trans "Alias(es)" %} |
+ {% trans "Tags" %} |
+ {% if enable_mx_checks or enable_dnsbl_checks %}
+ {% trans "DNS status" %} |
+ {% endif %}
+ {% trans "Actions" %} |
+{% endblock %}
diff --git a/modoboa/admin/templates/admin/domains.html b/modoboa/admin/templates/admin/domains.html
index f9e0a6b4e..2493a6caf 100644
--- a/modoboa/admin/templates/admin/domains.html
+++ b/modoboa/admin/templates/admin/domains.html
@@ -29,13 +29,3 @@ $(document).ready(function() {
{% endblock %}
{% block actions %}{% domains_menu "domains" user %}{% endblock %}
-
-{% block table_header %}
- {% trans "Name" %} |
- {% trans "Alias(es)" %} |
- {% trans "Tags" %} |
- {% if enable_mx_checks or enable_dnsbl_checks %}
- {% trans "DNS status" %} |
- {% endif %}
- {% trans "Actions" %} |
-{% endblock %}
diff --git a/modoboa/admin/templates/admin/domains_quota_headers.html b/modoboa/admin/templates/admin/domains_quota_headers.html
new file mode 100644
index 000000000..df254a7a0
--- /dev/null
+++ b/modoboa/admin/templates/admin/domains_quota_headers.html
@@ -0,0 +1,6 @@
+{% load i18n %}
+{% trans "Domain" %} |
+{% trans "Limit" %} |
+{% trans "Used" %} |
+{% trans "Allocated" %} |
+{% trans "Usage (%)" %} |
diff --git a/modoboa/admin/templates/admin/domains_quotas.html b/modoboa/admin/templates/admin/domains_quotas.html
new file mode 100644
index 000000000..126039b7a
--- /dev/null
+++ b/modoboa/admin/templates/admin/domains_quotas.html
@@ -0,0 +1,16 @@
+{% load lib_tags i18n admin_tags %}
+
+{% for domain in domains %}
+
+ | {{ domain }} |
+ {{ domain.quota }}M |
+ {{ domain.used_quota }}M |
+ {{ domain.allocated_quota }}M |
+
+
+ |
+
+{% endfor %}
diff --git a/modoboa/admin/templates/admin/quota_headers.html b/modoboa/admin/templates/admin/identities_quota_headers.html
similarity index 100%
rename from modoboa/admin/templates/admin/quota_headers.html
rename to modoboa/admin/templates/admin/identities_quota_headers.html
diff --git a/modoboa/admin/templates/admin/quotas.html b/modoboa/admin/templates/admin/identities_quotas.html
similarity index 100%
rename from modoboa/admin/templates/admin/quotas.html
rename to modoboa/admin/templates/admin/identities_quotas.html
diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py
index 60285fa0a..2b566db17 100644
--- a/modoboa/admin/templatetags/admin_tags.py
+++ b/modoboa/admin/templatetags/admin_tags.py
@@ -35,15 +35,25 @@ def domains_menu(selection, user, ajax_mode=True):
:rtype: str
:return: rendered menu (as HTML)
"""
- domain_list_url = (
- "list/" if ajax_mode else reverse("admin:domain_list")
- )
+ nav_classes = "navigation"
+ if ajax_mode:
+ domain_list_url = "list/"
+ quota_list_url = "quotas/"
+ nav_classes += " ajaxnav"
+ else:
+ domain_list_url = reverse("admin:domain_list")
+ quota_list_url = domain_list_url + "#quotas/"
entries = [
{"name": "domains",
"label": _("List domains"),
"img": "fa fa-user",
"class": "ajaxnav navigation",
"url": domain_list_url},
+ {"name": "quotas",
+ "label": _("List quotas"),
+ "img": "fa fa-hdd-o",
+ "class": "ajaxnav navigation",
+ "url": quota_list_url},
]
if user.has_perm("admin.add_domain"):
extra_entries = signals.extra_domain_menu_entries.send(
diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py
index a2cd9b300..acf1baf9d 100644
--- a/modoboa/admin/tests/test_account.py
+++ b/modoboa/admin/tests/test_account.py
@@ -418,7 +418,7 @@ class AccountTestCase(ModoTestCase):
"""Test quota list view."""
models.Quota.objects.filter(username="user@test.com").update(
bytes=5 * 1048576)
- url = reverse("admin:quota_list")
+ url = reverse("admin:identity_quota_list")
response = self.ajax_get(url)
self.assertIn("5M", response["rows"])
self.assertIn('title="50%"', response["rows"])
diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py
index 19e7893d0..eaee09efa 100644
--- a/modoboa/admin/tests/test_domain.py
+++ b/modoboa/admin/tests/test_domain.py
@@ -308,6 +308,14 @@ class DomainTestCase(ModoTestCase):
self.assertEqual(response.status_code, 200)
self.assertIn("Usage", response.content.decode())
+ def test_domain_quota_list_view(self):
+ """Test quota list view."""
+ url = reverse("admin:domain_quota_list")
+ response = self.ajax_get(url)
+ self.assertIn("50M", response["rows"])
+ self.assertIn('title="40% (20 MB) allocated"', response["rows"])
+ self.assertIn("test.com", response["rows"])
+
def test_statitics_widget(self):
"""Test statistics display in dashboard."""
url = reverse("core:dashboard")
diff --git a/modoboa/admin/urls.py b/modoboa/admin/urls.py
index 63958d202..eff6fc2bb 100644
--- a/modoboa/admin/urls.py
+++ b/modoboa/admin/urls.py
@@ -23,6 +23,8 @@ urlpatterns = [
url(r'^domains/(?P\d+)/mx/$', views.MXDomainDetailView.as_view(),
name="mx_domain_detail"),
url(r'^domains/list/$', domain_views._domains, name="_domain_list"),
+ url(r'^domains/quotas/$', domain_views.list_quotas,
+ name="domain_quota_list"),
url(r'^domains/flatlist/$', domain_views.domains_list,
name="domain_flat_list"),
url(r'^domains/new/', domain_views.newdomain, name='domain_add'),
@@ -40,7 +42,7 @@ urlpatterns += [
url(r'^identities/list/$', identity_views._identities,
name="_identity_list"),
url(r'^identities/quotas/$', identity_views.list_quotas,
- name="quota_list"),
+ name="identity_quota_list"),
url(r'^identities/page/$', identity_views.get_next_page,
name="identity_page"),
diff --git a/modoboa/admin/views/domain.py b/modoboa/admin/views/domain.py
index 7f0eb12b9..159cbfe8e 100644
--- a/modoboa/admin/views/domain.py
+++ b/modoboa/admin/views/domain.py
@@ -70,10 +70,17 @@ def _domains(request):
"auto_account_removal"),
}
page = get_listing_page(domainlist, request.GET.get("page", 1))
+ parameters = request.localconfig.parameters
+ context["headers"] = render_to_string(
+ "admin/domain_headers.html", {
+ "enable_mx_checks": parameters.get_value("enable_mx_checks"),
+ "enable_dnsbl_checks": (
+ parameters.get_value("enable_dnsbl_checks"))
+ }, request
+ )
if page is None:
context["length"] = 0
else:
- parameters = request.localconfig.parameters
context["rows"] = render_to_string(
"admin/domains_table.html", {
"domains": page.object_list,
@@ -110,6 +117,30 @@ def domains_list(request):
return render_to_json_response(doms)
+@login_required
+@permission_required("admin.add_domain")
+def list_quotas(request):
+ from modoboa.lib.db_utils import db_type
+
+ sort_order, sort_dir = get_sort_order(request.GET, "domain")
+ domains = Domain.objects.get_for_admin(request.user)
+ domains = domains.exclude(quota=0)
+ page = get_listing_page(domains, request.GET.get("page", 1))
+ context = {
+ "headers": render_to_string(
+ "admin/domains_quota_headers.html", {}, request
+ )
+ }
+ if page is None:
+ context["length"] = 0
+ else:
+ context["rows"] = render_to_string(
+ "admin/domains_quotas.html", {"domains": page}, request
+ )
+ context["pages"] = [page.number]
+ return render_to_json_response(context)
+
+
@login_required
@permission_required("admin.add_domain")
@reversion.create_revision()
diff --git a/modoboa/admin/views/identity.py b/modoboa/admin/views/identity.py
index 9d31f3def..669a49b62 100644
--- a/modoboa/admin/views/identity.py
+++ b/modoboa/admin/views/identity.py
@@ -53,11 +53,11 @@ def _identities(request):
"handle_mailboxes", raise_exception=False)
}
page = get_listing_page(objects, request.GET.get("page", 1))
+ context["headers"] = render_to_string(
+ "admin/identity_headers.html", {}, request)
if page is None:
context["length"] = 0
else:
- context["headers"] = render_to_string(
- "admin/identity_headers.html", {}, request)
context["rows"] = render_to_string(
"admin/identities_table.html", {
"identities": page.object_list
@@ -114,14 +114,14 @@ def list_quotas(request):
page = get_listing_page(mboxes, request.GET.get("page", 1))
context = {
"headers": render_to_string(
- "admin/quota_headers.html", {}, request
+ "admin/identities_quota_headers.html", {}, request
)
}
if page is None:
context["length"] = 0
else:
context["rows"] = render_to_string(
- "admin/quotas.html", {"mboxes": page}, request
+ "admin/identities_quotas.html", {"mboxes": page}, request
)
context["pages"] = [page.number]
return render_to_json_response(context)