diff --git a/HISTORY.rst b/HISTORY.rst
index 7ac4c5fff0..5ce5799a99 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -20,6 +20,8 @@
- Add support for unbinding sub menus.
- Fix mailing profile test view.
- Disregard the last 3 dots that mark the end of the YAML document.
+- Add support for multiple dashboards.
+- Add support for removing dashboard widgets.
2.6.4 (2017-07-26)
==================
diff --git a/mayan/apps/appearance/templates/appearance/home.html b/mayan/apps/appearance/templates/appearance/home.html
index 6960378faf..37af0c3f40 100644
--- a/mayan/apps/appearance/templates/appearance/home.html
+++ b/mayan/apps/appearance/templates/appearance/home.html
@@ -72,9 +72,9 @@
- {% get_dashboard_widgets as dashboard_widgets %}
+ {% get_dashboard 'main' as dashboard %}
- {% for widget in dashboard_widgets %}
+ {% for widget in dashboard.get_widgets %}
{% include 'appearance/dashboard_widget.html' %}
{% endfor %}
diff --git a/mayan/apps/checkouts/apps.py b/mayan/apps/checkouts/apps.py
index 740f3652ea..255dbc82aa 100644
--- a/mayan/apps/checkouts/apps.py
+++ b/mayan/apps/checkouts/apps.py
@@ -11,10 +11,11 @@ from django.utils.translation import ugettext_lazy as _
from acls import ModelPermission
from common import MayanAppConfig, menu_facet, menu_main, menu_sidebar
-from common.classes import DashboardWidget
+from common.dashboards import dashboard_main
from mayan.celery import app
from rest_api.classes import APIEndPoint
+from .dashboard_widgets import widget_checkouts
from .handlers import check_new_version_creation
from .links import (
link_checkin_document, link_checkout_document, link_checkout_info,
@@ -49,13 +50,6 @@ class CheckoutsApp(MayanAppConfig):
DocumentCheckout = self.get_model('DocumentCheckout')
- DashboardWidget(
- icon='fa fa-shopping-cart',
- queryset=DocumentCheckout.objects.all(),
- label=_('Checkedout documents'),
- link=reverse_lazy('checkouts:checkout_list')
- )
-
Document.add_to_class(
'check_in',
lambda document, user=None: DocumentCheckout.objects.check_in_document(document, user)
@@ -114,6 +108,8 @@ class CheckoutsApp(MayanAppConfig):
}
)
+ dashboard_main.add_widget(order=-1, widget=widget_checkouts)
+
menu_facet.bind_links(links=(link_checkout_info,), sources=(Document,))
menu_main.bind_links(links=(link_checkout_list,), position=98)
menu_sidebar.bind_links(
diff --git a/mayan/apps/checkouts/dashboard_widgets.py b/mayan/apps/checkouts/dashboard_widgets.py
new file mode 100644
index 0000000000..36152ef858
--- /dev/null
+++ b/mayan/apps/checkouts/dashboard_widgets.py
@@ -0,0 +1,21 @@
+from __future__ import absolute_import, unicode_literals
+
+from django.apps import apps
+from django.urls import reverse_lazy
+from django.utils.translation import ugettext_lazy as _
+
+from common.classes import DashboardWidget
+
+
+def checkedout_documents_queryset():
+ DocumentCheckout = apps.get_model(
+ app_label='checkouts', model_name='DocumentCheckout'
+ )
+ return DocumentCheckout.objects.all()
+
+
+widget_checkouts = DashboardWidget(
+ label=_('Checkedout documents'),
+ link=reverse_lazy('checkouts:checkout_list'),
+ icon='fa fa-shopping-cart', queryset=checkedout_documents_queryset
+)
diff --git a/mayan/apps/common/classes.py b/mayan/apps/common/classes.py
index dcbc7c652e..790122bb8e 100644
--- a/mayan/apps/common/classes.py
+++ b/mayan/apps/common/classes.py
@@ -45,6 +45,43 @@ class Collection(object):
return self._model.objects.all()
+class Dashboard(object):
+ _registry = {}
+
+ @classmethod
+ def get(cls, name):
+ return cls._registry[name]
+
+ def __init__(self, name, label):
+ self.name = name
+ self.label = label
+ self.widgets = {}
+ self.removed_widgets = []
+ self.__class__._registry[name] = self
+
+ def add_widget(self, widget, order=0):
+ self.widgets[widget] = {'widget': widget, 'order': order}
+
+ def get_widgets(self):
+ """
+ Returns a list of widgets sorted by their 'order'.
+ If two or more widgets have the same 'order', sort by label.
+ """
+ return map(
+ lambda x: x['widget'],
+ filter(
+ lambda x: x['widget'] not in self.removed_widgets,
+ sorted(
+ self.widgets.values(),
+ key=lambda x: (x['order'], x['widget'].label)
+ )
+ )
+ )
+
+ def remove_widget(self, widget):
+ self.removed_widgets.append(widget)
+
+
class DashboardWidget(object):
_registry = []
diff --git a/mayan/apps/common/dashboards.py b/mayan/apps/common/dashboards.py
new file mode 100644
index 0000000000..e825460863
--- /dev/null
+++ b/mayan/apps/common/dashboards.py
@@ -0,0 +1,7 @@
+from __future__ import unicode_literals
+
+from django.utils.translation import ugettext_lazy as _
+
+from .classes import Dashboard
+
+dashboard_main = Dashboard(name='main', label=_('Main'))
diff --git a/mayan/apps/common/mixins.py b/mayan/apps/common/mixins.py
index be752fa76e..c939826b6c 100644
--- a/mayan/apps/common/mixins.py
+++ b/mayan/apps/common/mixins.py
@@ -6,7 +6,6 @@ from django.core.exceptions import PermissionDenied
from django.db.models.query import QuerySet
from django.http import HttpResponseRedirect
from django.shortcuts import resolve_url
-from django.urls import reverse
from django.utils.translation import ungettext, ugettext_lazy as _
from permissions import Permission
diff --git a/mayan/apps/common/templatetags/common_tags.py b/mayan/apps/common/templatetags/common_tags.py
index 8a7b5a6a54..c1a35a6177 100644
--- a/mayan/apps/common/templatetags/common_tags.py
+++ b/mayan/apps/common/templatetags/common_tags.py
@@ -11,7 +11,7 @@ from django.utils.encoding import force_text
import mayan
-from ..classes import Collection, DashboardWidget
+from ..classes import Collection, Dashboard
from ..utils import return_attrib
register = Library()
@@ -30,8 +30,8 @@ def get_collections():
@register.simple_tag
-def get_dashboard_widgets():
- return DashboardWidget.get_all()
+def get_dashboard(name):
+ return Dashboard.get(name=name)
@register.filter
diff --git a/mayan/apps/documents/apps.py b/mayan/apps/documents/apps.py
index 9b5127904b..b694a46ad3 100644
--- a/mayan/apps/documents/apps.py
+++ b/mayan/apps/documents/apps.py
@@ -4,7 +4,6 @@ from datetime import timedelta
from kombu import Exchange, Queue
-from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _
from acls import ModelPermission
@@ -14,7 +13,8 @@ from common import (
MayanAppConfig, MissingItem, menu_facet, menu_main, menu_object,
menu_secondary, menu_setup, menu_sidebar, menu_multi_item, menu_tools
)
-from common.classes import DashboardWidget, ModelAttribute
+from common.classes import ModelAttribute
+from common.dashboards import dashboard_main
from common.signals import post_initial_setup
from common.widgets import two_state_template
from converter.links import link_transformation_list
@@ -31,6 +31,11 @@ from rest_api.classes import APIEndPoint
from rest_api.fields import DynamicSerializerField
from statistics.classes import StatisticNamespace, CharJSLine
+from .dashboard_widgets import (
+ widget_document_types, widget_documents_in_trash,
+ widget_new_documents_this_month, widget_pages_per_month,
+ widget_total_documents
+)
from .handlers import (
create_default_document_type, handler_scan_duplicates_for
)
@@ -81,7 +86,6 @@ from .search import document_search, document_page_search # NOQA
from .signals import post_version_upload
from .statistics import (
new_documents_per_month, new_document_pages_per_month,
- new_document_pages_this_month, new_documents_this_month,
new_document_versions_per_month, total_document_per_month,
total_document_page_per_month, total_document_version_per_month
)
@@ -113,42 +117,6 @@ class DocumentsApp(MayanAppConfig):
serializer_class='documents.serializers.DocumentSerializer'
)
- DashboardWidget(
- func=new_document_pages_this_month, icon='fa fa-calendar',
- label=_('New pages this month'),
- link=reverse_lazy(
- 'statistics:statistic_detail',
- args=('new-document-pages-per-month',)
- )
- )
-
- DashboardWidget(
- func=new_documents_this_month, icon='fa fa-calendar',
- label=_('New documents this month'),
- link=reverse_lazy(
- 'statistics:statistic_detail',
- args=('new-documents-per-month',)
- )
- )
-
- DashboardWidget(
- icon='fa fa-file', queryset=Document.objects.all(),
- label=_('Total documents'),
- link=reverse_lazy('documents:document_list')
- )
-
- DashboardWidget(
- icon='fa fa-book', queryset=DocumentType.objects.all(),
- label=_('Document types'),
- link=reverse_lazy('documents:document_type_list')
- )
-
- DashboardWidget(
- icon='fa fa-trash', queryset=DeletedDocument.objects.all(),
- label=_('Documents in trash'),
- link=reverse_lazy('documents:document_list_deleted')
- )
-
MissingItem(
label=_('Create a document type'),
description=_(
@@ -365,6 +333,12 @@ class DocumentsApp(MayanAppConfig):
}
)
+ dashboard_main.add_widget(widget=widget_document_types)
+ dashboard_main.add_widget(widget=widget_documents_in_trash)
+ dashboard_main.add_widget(widget=widget_new_documents_this_month)
+ dashboard_main.add_widget(widget=widget_pages_per_month)
+ dashboard_main.add_widget(widget=widget_total_documents)
+
menu_documents.bind_links(
links=(
link_document_list_recent, link_document_list,
diff --git a/mayan/apps/documents/dashboard_widgets.py b/mayan/apps/documents/dashboard_widgets.py
new file mode 100644
index 0000000000..aafee3c874
--- /dev/null
+++ b/mayan/apps/documents/dashboard_widgets.py
@@ -0,0 +1,71 @@
+from __future__ import absolute_import, unicode_literals
+
+from django.apps import apps
+from django.urls import reverse_lazy
+from django.utils.translation import ugettext_lazy as _
+
+from common.classes import DashboardWidget
+
+from .statistics import (
+ new_document_pages_this_month, new_documents_this_month,
+)
+
+
+def get_total_documents_queryset():
+ Document = apps.get_model(
+ app_label='documents', model_name='Document'
+ )
+ return Document.objects.all()
+
+
+def get_document_types_queryset():
+ DocumentType = apps.get_model(
+ app_label='documents', model_name='DocumentType'
+ )
+ return DocumentType.objects.all()
+
+
+def get_deleted_documents_queryset():
+ DeletedDocument = apps.get_model(
+ app_label='documents', model_name='DeletedDocument'
+ )
+ return DeletedDocument.objects.all()
+
+
+widget_pages_per_month = DashboardWidget(
+ func=new_document_pages_this_month, icon='fa fa-calendar',
+ label=_('New pages this month'),
+ link=reverse_lazy(
+ 'statistics:statistic_detail',
+ args=('new-document-pages-per-month',)
+ )
+)
+
+widget_new_documents_this_month = DashboardWidget(
+ func=new_documents_this_month, icon='fa fa-calendar',
+ label=_('New documents this month'),
+ link=reverse_lazy(
+ 'statistics:statistic_detail',
+ args=('new-documents-per-month',)
+ )
+)
+
+widget_total_documents = DashboardWidget(
+ icon='fa fa-file', queryset=get_total_documents_queryset,
+ label=_('Total documents'),
+ link=reverse_lazy('documents:document_list')
+)
+
+
+widget_document_types = DashboardWidget(
+ icon='fa fa-book', queryset=get_document_types_queryset,
+ label=_('Document types'),
+ link=reverse_lazy('documents:document_type_list')
+)
+
+
+widget_documents_in_trash = DashboardWidget(
+ icon='fa fa-trash', queryset=get_deleted_documents_queryset,
+ label=_('Documents in trash'),
+ link=reverse_lazy('documents:document_list_deleted')
+)
diff --git a/mayan/apps/navigation/classes.py b/mayan/apps/navigation/classes.py
index 5d52fcbd8a..db0cb30d3e 100644
--- a/mayan/apps/navigation/classes.py
+++ b/mayan/apps/navigation/classes.py
@@ -11,7 +11,7 @@ from django.core.exceptions import PermissionDenied
from django.shortcuts import resolve_url
from django.template import VariableDoesNotExist, Variable
from django.template.defaulttags import URLNode
-from django.urls import resolve, reverse
+from django.urls import resolve
from django.utils.encoding import force_text
from django.utils.http import urlencode, urlquote
@@ -63,7 +63,7 @@ class Menu(object):
@classmethod
def reset(cls):
- cls._registry={}
+ cls._registry = {}
def __init__(self, name, icon=None, label=None):
if name in self.__class__._registry: