From d640c65a11a6307388e04082fe0c809c95f2261b Mon Sep 17 00:00:00 2001 From: Antoine Nguyen Date: Tue, 7 Feb 2017 13:56:18 +0100 Subject: [PATCH] Move global statistics to admin dashboard. see #1020 --- modoboa/admin/handlers.py | 23 +++++++ .../admin/_global_statistics_widget.html | 20 ++++++ .../admin/_per_domain_statistics_widget.html | 17 +++++ .../templates/admin/domain_statistics.html | 63 ------------------- modoboa/admin/templatetags/admin_tags.py | 5 -- modoboa/admin/tests/test_domain.py | 13 ++-- modoboa/admin/urls.py | 2 - modoboa/admin/views/__init__.py | 3 +- modoboa/admin/views/domain.py | 29 +-------- modoboa/core/signals.py | 2 + .../templates/core/_latest_news_widget.html | 23 +++++++ modoboa/core/templates/core/dashboard.html | 29 +++------ modoboa/core/views/dashboard.py | 19 +++++- 13 files changed, 119 insertions(+), 129 deletions(-) create mode 100644 modoboa/admin/templates/admin/_global_statistics_widget.html create mode 100644 modoboa/admin/templates/admin/_per_domain_statistics_widget.html delete mode 100644 modoboa/admin/templates/admin/domain_statistics.html create mode 100644 modoboa/core/templates/core/_latest_news_widget.html diff --git a/modoboa/admin/handlers.py b/modoboa/admin/handlers.py index 4787cd343..1d75e8c53 100644 --- a/modoboa/admin/handlers.py +++ b/modoboa/admin/handlers.py @@ -300,3 +300,26 @@ def grant_access_to_all_objects(sender, account, role, **kwargs): account, model.objects.all(), ContentType.objects.get_for_model(model) ) + + +@receiver(core_signals.extra_admin_dashboard_widgets) +def add_widgets_to_admin_dashboard(sender, user, **kwargs): + """Add admin widgets to dashboard.""" + if user.is_superuser: + context = { + "domains_counter": models.Domain.objects.count(), + "domain_aliases_counter": models.DomainAlias.objects.count(), + "identities_counter": ( + core_models.User.objects.count() + + models.Alias.objects.filter(internal=False).count()), + } + template = "admin/_global_statistics_widget.html" + else: + context = { + "domains": models.Domain.objects.get_for_admin(user)} + template = "admin/_per_domain_statistics_widget.html" + return [{ + "column": "right", + "template": template, + "context": context + }] diff --git a/modoboa/admin/templates/admin/_global_statistics_widget.html b/modoboa/admin/templates/admin/_global_statistics_widget.html new file mode 100644 index 000000000..61dfe9ef8 --- /dev/null +++ b/modoboa/admin/templates/admin/_global_statistics_widget.html @@ -0,0 +1,20 @@ +{% load i18n %} + +
+

{% trans "Global statistics" %}

+ + + + + + + + + + + + + + +
{% trans "Type" %}{% trans "Quantity" %}
{% trans "Domains" %}{{ domains_counter }}
{% trans "Domain aliases" %}{{ domain_aliases_counter }}
{% trans "Identities" %}{{ identities_counter }}
+
diff --git a/modoboa/admin/templates/admin/_per_domain_statistics_widget.html b/modoboa/admin/templates/admin/_per_domain_statistics_widget.html new file mode 100644 index 000000000..871b4a49d --- /dev/null +++ b/modoboa/admin/templates/admin/_per_domain_statistics_widget.html @@ -0,0 +1,17 @@ +{% load i18n %} + +
+

{% trans "Per-domain statistics" %}

+ + + + + + {% for domain in domains %} + + + + + {% endfor %} +
{% trans "Domain" %}{% trans "Identities" %}
{{ domain.name }}{{ domain.identities_count }}
+
diff --git a/modoboa/admin/templates/admin/domain_statistics.html b/modoboa/admin/templates/admin/domain_statistics.html deleted file mode 100644 index 08eceb08c..000000000 --- a/modoboa/admin/templates/admin/domain_statistics.html +++ /dev/null @@ -1,63 +0,0 @@ -{% extends "admin/header.html" %} - -{% load i18n %} -{% load admin_tags %} - -{% block leftcol %} - - -{% get_extra_admin_content user "leftcol" selection %} -{% endblock %} - -{% block apparea %} -

{% trans "Statistics" %} {% trans "Domains and identities" %}

-
- -
- {% if user.role == "SuperAdmins" %} -
-
-

{% trans "Global counters" %}

-
- - - - - - - - - - - - - - -
{% trans "Type" %}{% trans "Quantity" %}
{% trans "Domains" %}{{ domains_counter }}
{% trans "Domain aliases" %}{{ domain_aliases_counter }}
{% trans "Identities" %}{{ identities_counter }}
-
-
- {% endif %} - -
-
-

{% trans "Per-domain counters" %}

-
- - - - - - {% for domain in domains %} - - - - {% endfor %} -
{% trans "Domain" %}{% trans "Identities" %}
{{ domain.name }}{{ domain.identities_count }}
-
-
- -
- -{% endblock %} diff --git a/modoboa/admin/templatetags/admin_tags.py b/modoboa/admin/templatetags/admin_tags.py index 5ade4c00b..38948c6a7 100644 --- a/modoboa/admin/templatetags/admin_tags.py +++ b/modoboa/admin/templatetags/admin_tags.py @@ -40,11 +40,6 @@ def domains_menu(selection, user, ajax_mode=True): "img": "fa fa-user", "class": "ajaxnav navigation", "url": domain_list_url}, - {"name": "statistics", - "label": _("Statistics"), - "img": "fa fa-line-chart", - "class": "navigation", - "url": reverse("admin:domain_statistics")} ] if user.has_perm("admin.add_domain"): extra_entries = signals.extra_domain_menu_entries.send( diff --git a/modoboa/admin/tests/test_domain.py b/modoboa/admin/tests/test_domain.py index 69dc8c775..406a529a7 100644 --- a/modoboa/admin/tests/test_domain.py +++ b/modoboa/admin/tests/test_domain.py @@ -203,17 +203,16 @@ class DomainTestCase(ModoTestCase): response = self.client.get(url) self.assertIn("Resources usage", response.content) - def test_domain_statitics_view(self): - """Test statistics display.""" - url = reverse("admin:domain_statistics") + def test_statitics_widget(self): + """Test statistics display in dashboard.""" + url = reverse("admin:dashboard") response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertIn("Global counters", response.content) - self.assertIn("Per-domain counters", response.content) + self.assertIn("Global statistics", response.content) self.client.force_login( User.objects.get(username="admin@test.com")) response = self.client.get(url) self.assertEqual(response.status_code, 200) - self.assertNotIn("Global counters", response.content) - self.assertIn("Per-domain counters", response.content) + self.assertNotIn("Global statistics", response.content) + self.assertIn("Per-domain statistics", response.content) diff --git a/modoboa/admin/urls.py b/modoboa/admin/urls.py index 347efea62..0398485ed 100644 --- a/modoboa/admin/urls.py +++ b/modoboa/admin/urls.py @@ -18,8 +18,6 @@ urlpatterns = patterns( name="mx_domain_detail"), url(r'^domains/list/$', '_domains', name="_domain_list"), url(r'^domains/flatlist/$', 'domains_list', name="domain_flat_list"), - url(r'^domains/stats/$', views.DomainStatisticsView.as_view(), - name="domain_statistics"), url(r'^domains/new/', 'newdomain', name='domain_add'), url(r'^domains/(?P\d+)/edit/$', 'editdomain', name="domain_change"), diff --git a/modoboa/admin/views/__init__.py b/modoboa/admin/views/__init__.py index 492f84da6..e63b5b5e1 100644 --- a/modoboa/admin/views/__init__.py +++ b/modoboa/admin/views/__init__.py @@ -3,7 +3,7 @@ from .alias import AliasDetailView from .identity import AccountDetailView from .dns import DNSBLDomainDetailView, MXDomainDetailView -from .domain import DomainDetailView, DomainStatisticsView +from .domain import DomainDetailView __all__ = [ @@ -11,6 +11,5 @@ __all__ = [ "AliasDetailView", "DNSBLDomainDetailView", "DomainDetailView", - "DomainStatisticsView", "MXDomainDetailView", ] diff --git a/modoboa/admin/views/domain.py b/modoboa/admin/views/domain.py index 81083ff93..4014897d9 100644 --- a/modoboa/admin/views/domain.py +++ b/modoboa/admin/views/domain.py @@ -14,7 +14,6 @@ from django.contrib.auth.decorators import ( from reversion import revisions as reversion -from modoboa.core.models import User from modoboa.core import signals as core_signals from modoboa.lib.web_utils import ( _render_to_string, render_to_json_response @@ -26,9 +25,7 @@ from modoboa.lib.listing import get_sort_order, get_listing_page from ..forms import DomainForm, DomainWizard from ..lib import get_domains -from ..models import ( - Domain, DomainAlias, Mailbox, Alias -) +from ..models import Domain, Mailbox from .. import signals @@ -156,29 +153,6 @@ def deldomain(request, dom_id): return render_to_json_response(msg) -class DomainStatisticsView( - auth_mixins.PermissionRequiredMixin, generic.TemplateView): - """A simple page to present domain statistics.""" - - template_name = "admin/domain_statistics.html" - permission_required = "admin.view_domains" - - def get_context_data(self, **kwargs): - """Add counters.""" - context = super(DomainStatisticsView, self).get_context_data(**kwargs) - if self.request.user.role == "SuperAdmins": - context.update({ - "domains_counter": Domain.objects.count(), - "domain_aliases_counter": DomainAlias.objects.count(), - "identities_counter": ( - User.objects.count() + - Alias.objects.filter(internal=False).count()), - }) - context.update({ - "domains": Domain.objects.get_for_admin(self.request.user)}) - return context - - class DomainDetailView( auth_mixins.PermissionRequiredMixin, generic.DetailView): """DetailView for Domain.""" @@ -208,6 +182,7 @@ class DomainDetailView( for widget in widgets: context["templates"][widget["column"]].append( widget["template"]) + # FIXME: can raise conflicts... context.update(widget["context"]) return context diff --git a/modoboa/core/signals.py b/modoboa/core/signals.py index 6bce716f5..c14a0dd8d 100644 --- a/modoboa/core/signals.py +++ b/modoboa/core/signals.py @@ -15,6 +15,8 @@ allow_password_change = django.dispatch.Signal(providing_args=["user"]) can_create_object = django.dispatch.Signal( providing_args=["context", "object_type", "count"]) extra_account_actions = django.dispatch.Signal(providing_args=["account"]) +extra_admin_dashboard_widgets = django.dispatch.Signal( + providing_args=["user"]) extra_admin_menu_entries = django.dispatch.Signal( providing_args=["location", "user"]) extra_role_permissions = django.dispatch.Signal(providing_args=["role"]) diff --git a/modoboa/core/templates/core/_latest_news_widget.html b/modoboa/core/templates/core/_latest_news_widget.html new file mode 100644 index 000000000..ca979e066 --- /dev/null +++ b/modoboa/core/templates/core/_latest_news_widget.html @@ -0,0 +1,23 @@ +{% load i18n %} + +
+
+

{% trans "Latest news" %}

+
+ + + {% for post in news %} + + + + + {% endfor %} + +
{{ post.title }}{{ post.published|date:"DATE_FORMAT" }}
+ +
+ {% blocktrans trimmed %} + Visit the official weblog for more information. + {% endblocktrans %} +
+
diff --git a/modoboa/core/templates/core/dashboard.html b/modoboa/core/templates/core/dashboard.html index 935e0b42e..bc765e7c3 100644 --- a/modoboa/core/templates/core/dashboard.html +++ b/modoboa/core/templates/core/dashboard.html @@ -10,27 +10,14 @@
-
-
-

{% trans "Latest news" %}

-
- - - {% for post in news %} - - - - - {% endfor %} - -
{{ post.title }}{{ post.published|date:"DATE_FORMAT" }}
- -
- {% blocktrans trimmed %} - Visit the official weblog for more information. - {% endblocktrans %} -
-
+ {% for widget in widgets.left %} + {% include widget %} + {% endfor %} +
+
+ {% for widget in widgets.right %} + {% include widget %} + {% endfor %}
diff --git a/modoboa/core/views/dashboard.py b/modoboa/core/views/dashboard.py index a375a65c5..25cfa8d99 100644 --- a/modoboa/core/views/dashboard.py +++ b/modoboa/core/views/dashboard.py @@ -7,6 +7,8 @@ from django.views import generic from django.contrib.auth import mixins as auth_mixins +from .. import signals + class DashboardView(auth_mixins.AccessMixin, generic.TemplateView): """Dashboard view.""" @@ -15,14 +17,17 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView): def dispatch(self, request, *args, **kwargs): """Check if user can access dashboard.""" - if not request.user.is_admin: + if not request.user.is_authenticated() or not request.user.is_admin: return self.handle_no_permission() return super(DashboardView, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): """Add context variables.""" context = super(DashboardView, self).get_context_data(**kwargs) - context.update({"selection": "dashboard"}) + context.update({ + "selection": "dashboard", "widgets": {"left": [], "right": []} + }) + # Fetch latest news if self.request.user.language == "fr": lang = "fr" else: @@ -33,5 +38,15 @@ class DashboardView(auth_mixins.AccessMixin, generic.TemplateView): for entry in posts["entries"][:5]: entry["published"] = parser.parse(entry["published"]) entries.append(entry) + context["widgets"]["left"].append("core/_latest_news_widget.html") context.update({"news": entries}) + # Extra widgets + result = signals.extra_admin_dashboard_widgets.send( + sender=self.__class__, user=self.request.user) + for receiver, widgets in result: + for widget in widgets: + context["widgets"][widget["column"]].append( + widget["template"]) + # FIXME: can raise conflicts... + context.update(widget["context"]) return context