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: