Add new class based dashboard widget. This new widget supports subclassing and is template based. All exising widgets have been converted. ACL filtering was added to the widget results.
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -97,6 +97,9 @@
|
|||||||
ActionError exception.
|
ActionError exception.
|
||||||
- Add favorite documents per user. Adds new setting option
|
- Add favorite documents per user. Adds new setting option
|
||||||
DOCUMENTS_FAVORITE_COUNT.
|
DOCUMENTS_FAVORITE_COUNT.
|
||||||
|
- Add new class based dashboard widget. This new widget supports
|
||||||
|
subclassing and is template based. All exising widgets have been
|
||||||
|
converted. ACL filtering was added to the widget results.
|
||||||
|
|
||||||
3.0.3 (2018-08-17)
|
3.0.3 (2018-08-17)
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -67,11 +67,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% get_dashboard 'main' as dashboard %}
|
{% render_dashboard 'main' %}
|
||||||
<div class="row">
|
|
||||||
{% for widget in dashboard.get_widgets %}
|
|
||||||
{% include 'appearance/dashboard_widget.html' %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
5
mayan/apps/appearance/templates/dashboard/dashboard.html
Normal file
5
mayan/apps/appearance/templates/dashboard/dashboard.html
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<div class="row">
|
||||||
|
{% for widget in widgets %}
|
||||||
|
{{ widget }}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
@@ -5,28 +5,24 @@
|
|||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-2">
|
<div class="col-xs-2">
|
||||||
{% if widget.icon %}
|
{% if icon %}
|
||||||
<i class="dashboard-widget-icon {{ widget.icon }}"></i>
|
<i class="dashboard-widget-icon {{ icon }}"></i>
|
||||||
{% elif widget.icon_class %}
|
{% elif icon_class %}
|
||||||
<div class="dashboard-widget-icon">
|
<div class="dashboard-widget-icon">
|
||||||
{{ widget.icon_class.render }}
|
{{ icon_class.render }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-10 text-right">
|
<div class="col-xs-10 text-right">
|
||||||
<strong>{{ widget.label }}</strong>
|
<strong>{{ label }}</strong>
|
||||||
<div class="huge">
|
<div class="huge">
|
||||||
{% if widget.func %}
|
{{ count }}
|
||||||
{{ widget.func }}
|
|
||||||
{% else %}
|
|
||||||
{{ widget.queryset.count }}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% if widget.link %}
|
{% if link %}
|
||||||
<a href="{{ widget.link }}">
|
<a href="{{ link }}">
|
||||||
<div class="panel-footer">
|
<div class="panel-footer">
|
||||||
<span class="pull-left">{% trans 'View details' %}</span>
|
<span class="pull-left">{% trans 'View details' %}</span>
|
||||||
<span class="pull-right"><i class="fa fa-external-link-alt"></i></span>
|
<span class="pull-right"><i class="fa fa-external-link-alt"></i></span>
|
||||||
@@ -36,3 +32,4 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ from common.dashboards import dashboard_main
|
|||||||
from events import ModelEventType
|
from events import ModelEventType
|
||||||
from mayan.celery import app
|
from mayan.celery import app
|
||||||
|
|
||||||
from .dashboard_widgets import widget_checkouts
|
from .dashboard_widgets import DashboardWidgetTotalCheckouts
|
||||||
from .events import (
|
from .events import (
|
||||||
event_document_auto_check_in, event_document_check_in,
|
event_document_auto_check_in, event_document_check_in,
|
||||||
event_document_check_out, event_document_forceful_check_in
|
event_document_check_out, event_document_forceful_check_in
|
||||||
@@ -117,7 +117,9 @@ class CheckoutsApp(MayanAppConfig):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
dashboard_main.add_widget(order=-1, widget=widget_checkouts)
|
dashboard_main.add_widget(
|
||||||
|
widget=DashboardWidgetTotalCheckouts, order=-1
|
||||||
|
)
|
||||||
|
|
||||||
menu_facet.bind_links(links=(link_checkout_info,), sources=(Document,))
|
menu_facet.bind_links(links=(link_checkout_info,), sources=(Document,))
|
||||||
menu_main.bind_links(links=(link_checkout_list,), position=98)
|
menu_main.bind_links(links=(link_checkout_list,), position=98)
|
||||||
|
|||||||
@@ -4,21 +4,33 @@ from django.apps import apps
|
|||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.classes import DashboardWidget
|
from common.classes import DashboardWidgetNumeric
|
||||||
|
from documents.permissions import permission_document_view
|
||||||
|
|
||||||
from .icons import icon_dashboard_checkouts
|
from .icons import icon_dashboard_checkouts
|
||||||
|
from .permissions import permission_document_checkout_detail_view
|
||||||
|
|
||||||
|
|
||||||
def checkedout_documents_queryset():
|
class DashboardWidgetTotalCheckouts(DashboardWidgetNumeric):
|
||||||
|
icon_class = icon_dashboard_checkouts
|
||||||
|
label = _('Checkedout documents')
|
||||||
|
link = reverse_lazy('checkouts:checkout_list')
|
||||||
|
|
||||||
|
def render(self, request):
|
||||||
|
AccessControlList = apps.get_model(
|
||||||
|
app_label='acls', model_name='AccessControlList'
|
||||||
|
)
|
||||||
DocumentCheckout = apps.get_model(
|
DocumentCheckout = apps.get_model(
|
||||||
app_label='checkouts', model_name='DocumentCheckout'
|
app_label='checkouts', model_name='DocumentCheckout'
|
||||||
)
|
)
|
||||||
return DocumentCheckout.objects.all()
|
queryset = AccessControlList.objects.filter_by_access(
|
||||||
|
permission=permission_document_checkout_detail_view,
|
||||||
|
user=request.user,
|
||||||
widget_checkouts = DashboardWidget(
|
queryset=DocumentCheckout.objects.checked_out_documents()
|
||||||
icon_class=icon_dashboard_checkouts,
|
|
||||||
label=_('Checkedout documents'),
|
|
||||||
link=reverse_lazy('checkouts:checkout_list'),
|
|
||||||
queryset=checkedout_documents_queryset
|
|
||||||
)
|
)
|
||||||
|
queryset = AccessControlList.objects.filter_by_access(
|
||||||
|
permission=permission_document_view, user=request.user,
|
||||||
|
queryset=queryset
|
||||||
|
)
|
||||||
|
self.count = queryset.count()
|
||||||
|
return super(DashboardWidgetTotalCheckouts, self).render(request)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.template import loader
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||||
from django.utils.translation import ugettext
|
from django.utils.translation import ugettext
|
||||||
@@ -83,24 +84,57 @@ class Dashboard(object):
|
|||||||
def remove_widget(self, widget):
|
def remove_widget(self, widget):
|
||||||
self.removed_widgets.append(widget)
|
self.removed_widgets.append(widget)
|
||||||
|
|
||||||
|
def render(self, request):
|
||||||
|
rendered_widgets = [widget().render(request=request) for widget in self.get_widgets()]
|
||||||
|
|
||||||
class DashboardWidget(object):
|
return loader.render_to_string(
|
||||||
_registry = []
|
template_name='dashboard/dashboard.html', context={
|
||||||
|
'widgets': rendered_widgets
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseDashboardWidget(object):
|
||||||
|
_registry = {}
|
||||||
|
context = {}
|
||||||
|
template_name = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, name):
|
||||||
|
return cls._registry[name]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_all(cls):
|
def get_all(cls):
|
||||||
return cls._registry
|
return cls._registry.items()
|
||||||
|
|
||||||
def __init__(self, label, func=None, icon=None, icon_class=None, link=None, queryset=None, statistic_slug=None):
|
@classmethod
|
||||||
self.label = label
|
def register(cls, klass):
|
||||||
self.icon = icon
|
cls._registry[klass.name] = klass
|
||||||
self.icon_class = icon_class
|
|
||||||
self.link = link
|
|
||||||
self.queryset = queryset
|
|
||||||
self.func = func
|
|
||||||
self.statistic_slug = statistic_slug
|
|
||||||
|
|
||||||
self.__class__._registry.append(self)
|
def get_context(self):
|
||||||
|
return self.context
|
||||||
|
|
||||||
|
def render(self, request):
|
||||||
|
if self.template_name:
|
||||||
|
return loader.render_to_string(
|
||||||
|
template_name=self.template_name, context=self.get_context(),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardWidgetNumeric(BaseDashboardWidget):
|
||||||
|
count = 0
|
||||||
|
icon_class = None
|
||||||
|
label = None
|
||||||
|
link = None
|
||||||
|
template_name = 'dashboard/numeric_widget.html'
|
||||||
|
|
||||||
|
def get_context(self):
|
||||||
|
return {
|
||||||
|
'count': self.count,
|
||||||
|
'icon_class': self.icon_class,
|
||||||
|
'label': self.label,
|
||||||
|
'link': self.link,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
|
|||||||
@@ -45,12 +45,6 @@ def check_sqlite():
|
|||||||
def get_collections():
|
def get_collections():
|
||||||
return Collection.get_all()
|
return Collection.get_all()
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag
|
|
||||||
def get_dashboard(name):
|
|
||||||
return Dashboard.get(name=name)
|
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def get_encoded_parameter(item, parameters_dict):
|
def get_encoded_parameter(item, parameters_dict):
|
||||||
result = {}
|
result = {}
|
||||||
@@ -74,6 +68,11 @@ def project_information(attribute_name):
|
|||||||
return getattr(mayan, attribute_name)
|
return getattr(mayan, attribute_name)
|
||||||
|
|
||||||
|
|
||||||
|
@register.simple_tag(takes_context=True)
|
||||||
|
def render_dashboard(context, name):
|
||||||
|
return Dashboard.get(name=name).render(request=context.request)
|
||||||
|
|
||||||
|
|
||||||
@register.simple_tag(takes_context=True)
|
@register.simple_tag(takes_context=True)
|
||||||
def render_subtemplate(context, template_name, template_context):
|
def render_subtemplate(context, template_name, template_context):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ from navigation import SourceColumn
|
|||||||
from rest_api.fields import DynamicSerializerField
|
from rest_api.fields import DynamicSerializerField
|
||||||
|
|
||||||
from .dashboard_widgets import (
|
from .dashboard_widgets import (
|
||||||
widget_document_types, widget_documents_in_trash,
|
DashboardWidgetDocumentsInTrash, DashboardWidgetDocumentsNewThisMonth,
|
||||||
widget_new_documents_this_month, widget_pages_per_month,
|
DashboardWidgetDocumentsPagesNewThisMonth, DashboardWidgetDocumentsTotal,
|
||||||
widget_total_documents
|
DashboardWidgetDocumentsTypesTotal,
|
||||||
)
|
)
|
||||||
from .events import (
|
from .events import (
|
||||||
event_document_create, event_document_download,
|
event_document_create, event_document_download,
|
||||||
@@ -195,7 +195,7 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
model=Document, related='document_type',
|
model=Document, related='document_type',
|
||||||
)
|
)
|
||||||
ModelPermission.register_inheritance(
|
ModelPermission.register_inheritance(
|
||||||
model=DocumentPage, related='document',
|
model=DocumentPage, related='document_version__document',
|
||||||
)
|
)
|
||||||
ModelPermission.register_inheritance(
|
ModelPermission.register_inheritance(
|
||||||
model=DocumentPageResult, related='document_version__document',
|
model=DocumentPageResult, related='document_version__document',
|
||||||
@@ -389,11 +389,21 @@ class DocumentsApp(MayanAppConfig):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
dashboard_main.add_widget(widget=widget_document_types)
|
dashboard_main.add_widget(
|
||||||
dashboard_main.add_widget(widget=widget_documents_in_trash)
|
widget=DashboardWidgetDocumentsTotal, order=0
|
||||||
dashboard_main.add_widget(widget=widget_new_documents_this_month)
|
)
|
||||||
dashboard_main.add_widget(widget=widget_pages_per_month)
|
dashboard_main.add_widget(
|
||||||
dashboard_main.add_widget(widget=widget_total_documents)
|
widget=DashboardWidgetDocumentsInTrash, order=1
|
||||||
|
)
|
||||||
|
dashboard_main.add_widget(
|
||||||
|
widget=DashboardWidgetDocumentsTypesTotal, order=2
|
||||||
|
)
|
||||||
|
dashboard_main.add_widget(
|
||||||
|
widget=DashboardWidgetDocumentsNewThisMonth, order=3
|
||||||
|
)
|
||||||
|
dashboard_main.add_widget(
|
||||||
|
widget=DashboardWidgetDocumentsPagesNewThisMonth, order=4
|
||||||
|
)
|
||||||
|
|
||||||
menu_documents.bind_links(
|
menu_documents.bind_links(
|
||||||
links=(
|
links=(
|
||||||
|
|||||||
@@ -4,78 +4,99 @@ from django.apps import apps
|
|||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from common.classes import DashboardWidget
|
from common.classes import DashboardWidgetNumeric
|
||||||
|
|
||||||
from .icons import (
|
from .icons import (
|
||||||
icon_dashboard_documents_in_trash, icon_dashboard_document_types,
|
icon_dashboard_documents_in_trash, icon_dashboard_document_types,
|
||||||
icon_dashboard_pages_per_month, icon_dashboard_new_documents_this_month,
|
icon_dashboard_pages_per_month, icon_dashboard_new_documents_this_month,
|
||||||
icon_dashboard_total_document
|
icon_dashboard_total_document
|
||||||
)
|
)
|
||||||
|
from .permissions import (
|
||||||
|
permission_document_view, permission_document_type_view
|
||||||
|
)
|
||||||
from .statistics import (
|
from .statistics import (
|
||||||
new_document_pages_this_month, new_documents_this_month,
|
new_document_pages_this_month, new_documents_this_month,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_total_documents_queryset():
|
class DashboardWidgetDocumentsTotal(DashboardWidgetNumeric):
|
||||||
|
icon_class = icon_dashboard_total_document
|
||||||
|
label = _('Total documents')
|
||||||
|
link = reverse_lazy('documents:document_list')
|
||||||
|
|
||||||
|
def render(self, request):
|
||||||
|
AccessControlList = apps.get_model(
|
||||||
|
app_label='acls', model_name='AccessControlList'
|
||||||
|
)
|
||||||
Document = apps.get_model(
|
Document = apps.get_model(
|
||||||
app_label='documents', model_name='Document'
|
app_label='documents', model_name='Document'
|
||||||
)
|
)
|
||||||
return Document.objects.all()
|
self.count = AccessControlList.objects.filter_by_access(
|
||||||
|
permission=permission_document_view, user=request.user,
|
||||||
|
queryset=Document.objects.filter(is_stub=False)
|
||||||
|
).count()
|
||||||
|
return super(DashboardWidgetDocumentsTotal, self).render(request)
|
||||||
|
|
||||||
|
|
||||||
def get_document_types_queryset():
|
class DashboardWidgetDocumentsInTrash(DashboardWidgetNumeric):
|
||||||
DocumentType = apps.get_model(
|
icon_class = icon_dashboard_documents_in_trash
|
||||||
app_label='documents', model_name='DocumentType'
|
label = _('Documents in trash')
|
||||||
|
link = reverse_lazy('documents:document_list_deleted')
|
||||||
|
|
||||||
|
def render(self, request):
|
||||||
|
AccessControlList = apps.get_model(
|
||||||
|
app_label='acls', model_name='AccessControlList'
|
||||||
)
|
)
|
||||||
return DocumentType.objects.all()
|
|
||||||
|
|
||||||
|
|
||||||
def get_deleted_documents_queryset():
|
|
||||||
DeletedDocument = apps.get_model(
|
DeletedDocument = apps.get_model(
|
||||||
app_label='documents', model_name='DeletedDocument'
|
app_label='documents', model_name='DeletedDocument'
|
||||||
)
|
)
|
||||||
return DeletedDocument.objects.all()
|
self.count = AccessControlList.objects.filter_by_access(
|
||||||
|
permission=permission_document_view, user=request.user,
|
||||||
|
queryset=DeletedDocument.objects.all()
|
||||||
|
).count()
|
||||||
|
return super(DashboardWidgetDocumentsInTrash, self).render(request)
|
||||||
|
|
||||||
|
|
||||||
widget_pages_per_month = DashboardWidget(
|
class DashboardWidgetDocumentsTypesTotal(DashboardWidgetNumeric):
|
||||||
func=new_document_pages_this_month,
|
icon_class = icon_dashboard_document_types
|
||||||
icon_class=icon_dashboard_pages_per_month,
|
label = _('Document types')
|
||||||
label=_('New pages this month'),
|
link = reverse_lazy('documents:document_type_list')
|
||||||
link=reverse_lazy(
|
|
||||||
'statistics:statistic_detail',
|
def render(self, request):
|
||||||
args=('new-document-pages-per-month',)
|
AccessControlList = apps.get_model(
|
||||||
|
app_label='acls', model_name='AccessControlList'
|
||||||
)
|
)
|
||||||
|
DocumentType = apps.get_model(
|
||||||
|
app_label='documents', model_name='DocumentType'
|
||||||
)
|
)
|
||||||
|
self.count = AccessControlList.objects.filter_by_access(
|
||||||
|
permission=permission_document_type_view, user=request.user,
|
||||||
|
queryset=DocumentType.objects.all()
|
||||||
|
).count()
|
||||||
|
return super(DashboardWidgetDocumentsTypesTotal, self).render(request)
|
||||||
|
|
||||||
widget_new_documents_this_month = DashboardWidget(
|
|
||||||
func=new_documents_this_month,
|
class DashboardWidgetDocumentsNewThisMonth(DashboardWidgetNumeric):
|
||||||
icon_class=icon_dashboard_new_documents_this_month,
|
icon_class = icon_dashboard_new_documents_this_month
|
||||||
label=_('New documents this month'),
|
label = _('New documents this month')
|
||||||
link=reverse_lazy(
|
link=reverse_lazy(
|
||||||
'statistics:statistic_detail',
|
'statistics:statistic_detail',
|
||||||
args=('new-documents-per-month',)
|
args=('new-documents-per-month',)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def render(self, request):
|
||||||
|
self.count = new_documents_this_month(user=request.user)
|
||||||
|
return super(DashboardWidgetDocumentsNewThisMonth, self).render(request)
|
||||||
|
|
||||||
|
|
||||||
|
class DashboardWidgetDocumentsPagesNewThisMonth(DashboardWidgetNumeric):
|
||||||
|
icon_class = icon_dashboard_pages_per_month
|
||||||
|
label = _('New pages this month')
|
||||||
|
link=reverse_lazy(
|
||||||
|
'statistics:statistic_detail',
|
||||||
|
args=('new-document-pages-per-month',)
|
||||||
)
|
)
|
||||||
|
|
||||||
widget_total_documents = DashboardWidget(
|
def render(self, request):
|
||||||
icon_class=icon_dashboard_total_document,
|
self.count = new_document_pages_this_month(user=request.user)
|
||||||
queryset=get_total_documents_queryset,
|
return super(DashboardWidgetDocumentsPagesNewThisMonth, self).render(request)
|
||||||
label=_('Total documents'),
|
|
||||||
link=reverse_lazy('documents:document_list')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
widget_document_types = DashboardWidget(
|
|
||||||
icon_class=icon_dashboard_document_types,
|
|
||||||
queryset=get_document_types_queryset,
|
|
||||||
label=_('Document types'),
|
|
||||||
link=reverse_lazy('documents:document_type_list')
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
widget_documents_in_trash = DashboardWidget(
|
|
||||||
icon_class=icon_dashboard_documents_in_trash,
|
|
||||||
queryset=get_deleted_documents_queryset,
|
|
||||||
label=_('Documents in trash'),
|
|
||||||
link=reverse_lazy('documents:document_list_deleted')
|
|
||||||
)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user