mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-19 05:58:34 +02:00
Quota (#1262)
* 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:
committed by
Antoine Nguyen
parent
dbe4596d78
commit
9349b7a90a
@@ -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.
|
||||
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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(
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"),
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user