* Add domain quota listing
See #1135

* Add permission on domains quotas listing

* Always send context headers
even if domains or identities are empty

* rename identities quotas view in the tests

* Added unit test for domain quota list
This commit is contained in:
Anthony Bourguignon
2017-10-06 17:11:55 +02:00
committed by Antoine Nguyen
parent dbe4596d78
commit 9349b7a90a
14 changed files with 119 additions and 20 deletions
+18
View File
@@ -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.
+8
View File
@@ -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);
},
@@ -0,0 +1,10 @@
{% load i18n %}
{% block table_header %}
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
{% if enable_mx_checks or enable_dnsbl_checks %}
<th id="dns" width="">{% trans "DNS status" %}</th>
{% endif %}
<th id="actions" width="">{% trans "Actions" %}</th>
{% endblock %}
@@ -29,13 +29,3 @@ $(document).ready(function() {
{% endblock %}
{% block actions %}{% domains_menu "domains" user %}{% endblock %}
{% block table_header %}
<th id="name" class="sortable" data-sort_order="name" width="">{% trans "Name" %}</th>
<th id="aliases" width="">{% trans "Alias(es)" %}</th>
<th id="tags" class="sortable" data-sort_order="tags" width="">{% trans "Tags" %}</th>
{% if enable_mx_checks or enable_dnsbl_checks %}
<th id="dns" width="">{% trans "DNS status" %}</th>
{% endif %}
<th id="actions" width="">{% trans "Actions" %}</th>
{% endblock %}
@@ -0,0 +1,6 @@
{% load i18n %}
<th width="50%" class="sortable" data-sort_order="domain">{% trans "Domain" %}</th>
<th class="sortable" data-sort_order="quota">{% trans "Limit" %}</th>
<th class="sortable" data-sort_order="quota_value__used">{% trans "Used" %}</th>
<th class="sortable" data-sort_order="quota_value__allocated">{% trans "Allocated" %}</th>
<th class="sortable" data-sort_order="quota_usage" id="quotabar">{% trans "Usage (%)" %}</th>
@@ -0,0 +1,16 @@
{% load lib_tags i18n admin_tags %}
{% for domain in domains %}
<tr>
<td name="name"><a href="{% url 'admin:domain_detail' domain.id %}">{{ domain }}</a></td>
<td>{{ domain.quota }}M</td>
<td>{{ domain.used_quota }}M</td>
<td>{{ domain.allocated_quota }}M</td>
<td name="quota">
<div class="progress">
<div class="{% progress_color domain.used_quota_in_percent %}" style="width: {{ domain.used_quota_in_percent }}%" title="{{ domain.used_quota_in_percent }}% ({{ domain.used_quota }} {% trans "MB" %}) {% trans 'used' context 'quota' %}"></div>
<div class="{% progress_color domain.allocated_quota_in_percent %}" style="width: {{ domain.allocated_quota_in_percent }}%" title="{{ domain.allocated_quota_in_percent }}% ({{ domain.allocated_quota }} {% trans "MB" %}) {% trans 'allocated' %}"></div>
</div>
</td>
</tr>
{% endfor %}
+13 -3
View File
@@ -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(
+1 -1
View File
@@ -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"])
+8
View File
@@ -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")
+3 -1
View File
@@ -23,6 +23,8 @@ urlpatterns = [
url(r'^domains/(?P<pk>\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"),
+32 -1
View File
@@ -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()
+4 -4
View File
@@ -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)