610 lines
19 KiB
Python
610 lines
19 KiB
Python
import json
|
|
from django import forms
|
|
from django.contrib.admin.models import LogEntry
|
|
from django.db.models import Q
|
|
from django.template.loader import render_to_string
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from jet.utils import get_app_list, LazyDateTimeEncoder, context_to_dict
|
|
import datetime
|
|
|
|
|
|
class DashboardModule(object):
|
|
"""
|
|
Base dashboard module class. All dashboard modules (widgets) should inherit it.
|
|
"""
|
|
|
|
#: Path to widget's template. There is no need to extend such templates from any base templates.
|
|
template = 'jet.dashboard/module.html'
|
|
enabled = True
|
|
|
|
#: Specify if module can be draggable or has static position.
|
|
draggable = True
|
|
|
|
#: Specify if module can be collapsed.
|
|
collapsible = True
|
|
|
|
#: Specify if module can be deleted.
|
|
deletable = True
|
|
show_title = True
|
|
|
|
#: Default widget title that will be displayed for widget in the dashboard. User can change it later
|
|
#: for every widget.
|
|
title = ''
|
|
|
|
#: Specify title url. ``None`` if title shouldn't be clickable.
|
|
title_url = None
|
|
css_classes = None
|
|
|
|
#: HTML content that will be displayed before widget content.
|
|
pre_content = None
|
|
|
|
#: HTML content that will be displayed after widget content.
|
|
post_content = None
|
|
children = None
|
|
|
|
#: A ``django.forms.Form`` class which may contain custom widget settings. Not required.
|
|
settings_form = None
|
|
|
|
#: A ``django.forms.Form`` class which may contain custom widget child settings, if it has any. Not required.
|
|
child_form = None
|
|
|
|
#: Child name that will be displayed when editing module contents. Required if ``child_form`` set.
|
|
child_name = None
|
|
|
|
#: Same as child name, but plural.
|
|
child_name_plural = None
|
|
settings = None
|
|
column = None
|
|
order = None
|
|
|
|
#: A boolean field which specify if widget should be rendered on dashboard page load or fetched
|
|
#: later via AJAX.
|
|
ajax_load = False
|
|
|
|
#: A boolean field which makes widget ui color contrast.
|
|
contrast = False
|
|
|
|
#: Optional style attributes which will be applied to widget content container.
|
|
style = False
|
|
|
|
class Media:
|
|
css = ()
|
|
js = ()
|
|
|
|
def __init__(self, title=None, model=None, context=None, **kwargs):
|
|
if title is not None:
|
|
self.title = title
|
|
self.model = model
|
|
self.context = context or {}
|
|
|
|
for key in kwargs:
|
|
if hasattr(self.__class__, key):
|
|
setattr(self, key, kwargs[key])
|
|
|
|
self.children = self.children or []
|
|
|
|
if self.model:
|
|
self.load_from_model()
|
|
|
|
def fullname(self):
|
|
return self.__module__ + "." + self.__class__.__name__
|
|
|
|
def load_settings(self, settings):
|
|
"""
|
|
Should be implemented to restore saved in database settings. Required if you have custom settings.
|
|
"""
|
|
pass
|
|
|
|
def load_children(self, children):
|
|
self.children = children
|
|
|
|
def store_children(self):
|
|
"""
|
|
Specify if children field should be saved to database.
|
|
"""
|
|
return False
|
|
|
|
def settings_dict(self):
|
|
"""
|
|
Should be implemented to save settings to database. This method should return ``dict`` which will be serialized
|
|
using ``json``. Required if you have custom settings.
|
|
"""
|
|
pass
|
|
|
|
def dump_settings(self, settings=None):
|
|
settings = settings or self.settings_dict()
|
|
if settings:
|
|
return json.dumps(settings, cls=LazyDateTimeEncoder)
|
|
else:
|
|
return ''
|
|
|
|
def dump_children(self):
|
|
if self.store_children():
|
|
return json.dumps(self.children, cls=LazyDateTimeEncoder)
|
|
else:
|
|
return ''
|
|
|
|
def load_from_model(self):
|
|
self.title = self.model.title
|
|
|
|
if self.model.settings:
|
|
try:
|
|
self.settings = json.loads(self.model.settings)
|
|
self.load_settings(self.settings)
|
|
except ValueError:
|
|
pass
|
|
|
|
if self.store_children() and self.model.children:
|
|
try:
|
|
children = json.loads(self.model.children)
|
|
self.load_children(children)
|
|
except ValueError:
|
|
pass
|
|
|
|
def init_with_context(self, context):
|
|
"""
|
|
Allows you to load data and initialize module's state.
|
|
"""
|
|
pass
|
|
|
|
def get_context_data(self):
|
|
context = context_to_dict(self.context)
|
|
context.update({
|
|
'module': self
|
|
})
|
|
return context
|
|
|
|
def render(self):
|
|
self.init_with_context(self.context)
|
|
return render_to_string(self.template, self.get_context_data())
|
|
|
|
|
|
class LinkListItemForm(forms.Form):
|
|
url = forms.CharField(label=_('URL'))
|
|
title = forms.CharField(label=_('Title'))
|
|
external = forms.BooleanField(label=_('External link'), required=False)
|
|
|
|
|
|
class LinkListSettingsForm(forms.Form):
|
|
layout = forms.ChoiceField(label=_('Layout'), choices=(('stacked', _('Stacked')), ('inline', _('Inline'))))
|
|
|
|
|
|
class LinkList(DashboardModule):
|
|
"""
|
|
List of links widget.
|
|
|
|
Usage example:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from jet.dashboard import modules
|
|
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
|
|
|
|
|
class CustomIndexDashboard(Dashboard):
|
|
columns = 3
|
|
|
|
def init_with_context(self, context):
|
|
self.available_children.append(modules.LinkList)
|
|
self.children.append(modules.LinkList(
|
|
_('Support'),
|
|
children=[
|
|
{
|
|
'title': _('Django documentation'),
|
|
'url': 'http://docs.djangoproject.com/',
|
|
'external': True,
|
|
},
|
|
{
|
|
'title': _('Django "django-users" mailing list'),
|
|
'url': 'http://groups.google.com/group/django-users',
|
|
'external': True,
|
|
},
|
|
{
|
|
'title': _('Django irc channel'),
|
|
'url': 'irc://irc.freenode.net/django',
|
|
'external': True,
|
|
},
|
|
],
|
|
column=0,
|
|
order=0
|
|
))
|
|
|
|
"""
|
|
|
|
title = _('Links')
|
|
template = 'jet.dashboard/modules/link_list.html'
|
|
|
|
#: Specify widget layout.
|
|
#: Allowed values ``stacked`` and ``inline``.
|
|
layout = 'stacked'
|
|
|
|
#: Links are contained in ``children`` attribute which you can pass as constructor parameter
|
|
#: to make your own preinstalled link lists.
|
|
#:
|
|
#: ``children`` is an array of dictinaries::
|
|
#:
|
|
#: [
|
|
#: {
|
|
#: 'title': _('Django documentation'),
|
|
#: 'url': 'http://docs.djangoproject.com/',
|
|
#: 'external': True,
|
|
#: },
|
|
#: ...
|
|
#: ]
|
|
children = []
|
|
settings_form = LinkListSettingsForm
|
|
child_form = LinkListItemForm
|
|
child_name = _('Link')
|
|
child_name_plural = _('Links')
|
|
|
|
def __init__(self, title=None, children=list(), **kwargs):
|
|
children = list(map(self.parse_link, children))
|
|
kwargs.update({'children': children})
|
|
super(LinkList, self).__init__(title, **kwargs)
|
|
|
|
def settings_dict(self):
|
|
return {
|
|
'draggable': self.draggable,
|
|
'deletable': self.deletable,
|
|
'collapsible': self.collapsible,
|
|
'layout': self.layout
|
|
}
|
|
|
|
def load_settings(self, settings):
|
|
self.draggable = settings.get('draggable', self.draggable)
|
|
self.deletable = settings.get('deletable', self.deletable)
|
|
self.collapsible = settings.get('collapsible', self.collapsible)
|
|
self.layout = settings.get('layout', self.layout)
|
|
|
|
def store_children(self):
|
|
return True
|
|
|
|
def parse_link(self, link):
|
|
if isinstance(link, (tuple, list)):
|
|
link_dict = {'title': link[0], 'url': link[1]}
|
|
if len(link) >= 3:
|
|
link_dict['external'] = link[2]
|
|
return link_dict
|
|
elif isinstance(link, (dict,)):
|
|
return link
|
|
|
|
|
|
class AppList(DashboardModule):
|
|
"""
|
|
Shows applications and containing models links. For each model "created" and "change" links are displayed.
|
|
|
|
Usage example:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from jet.dashboard import modules
|
|
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
|
|
|
|
|
class CustomIndexDashboard(Dashboard):
|
|
columns = 3
|
|
|
|
def init_with_context(self, context):
|
|
self.children.append(modules.AppList(
|
|
_('Applications'),
|
|
exclude=('auth.*',),
|
|
column=0,
|
|
order=0
|
|
))
|
|
|
|
"""
|
|
|
|
title = _('Applications')
|
|
template = 'jet.dashboard/modules/app_list.html'
|
|
|
|
#: Specify models which should be displayed. ``models`` is an array of string formatted as ``app_label.model``.
|
|
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
|
|
models = None
|
|
|
|
#: Specify models which should NOT be displayed. ``exclude`` is an array of string formatted as ``app_label.model``.
|
|
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
|
|
exclude = None
|
|
hide_empty = True
|
|
|
|
def settings_dict(self):
|
|
return {
|
|
'models': self.models,
|
|
'exclude': self.exclude
|
|
}
|
|
|
|
def load_settings(self, settings):
|
|
self.models = settings.get('models')
|
|
self.exclude = settings.get('exclude')
|
|
|
|
def init_with_context(self, context):
|
|
app_list = get_app_list(context)
|
|
app_to_remove = []
|
|
|
|
for app in app_list:
|
|
app_name = app.get('app_label', app.get('name', ''))
|
|
app['models'] = filter(
|
|
lambda model: self.models is None or ('%s.%s' % (app_name, model['object_name'])) in self.models or ('%s.*' % app_name) in self.models,
|
|
app['models']
|
|
)
|
|
app['models'] = filter(
|
|
lambda model: self.exclude is None or (('%s.%s' % (app_name, model['object_name'])) not in self.exclude and ('%s.*' % app_name) not in self.exclude),
|
|
app['models']
|
|
)
|
|
app['models'] = list(app['models'])
|
|
|
|
if self.hide_empty and len(list(app['models'])) == 0:
|
|
app_to_remove.append(app)
|
|
|
|
for app in app_to_remove:
|
|
app_list.remove(app)
|
|
|
|
self.children = app_list
|
|
|
|
|
|
class ModelList(DashboardModule):
|
|
"""
|
|
Shows models links. For each model "created" and "change" links are displayed.
|
|
|
|
Usage example:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from jet.dashboard import modules
|
|
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
|
|
|
|
|
class CustomIndexDashboard(Dashboard):
|
|
columns = 3
|
|
|
|
def init_with_context(self, context):
|
|
self.children.append(modules.ModelList(
|
|
_('Models'),
|
|
exclude=('auth.*',),
|
|
column=0,
|
|
order=0
|
|
))
|
|
|
|
"""
|
|
|
|
title = _('Models')
|
|
template = 'jet.dashboard/modules/model_list.html'
|
|
|
|
#: Specify models which should be displayed. ``models`` is an array of string formatted as ``app_label.model``.
|
|
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
|
|
models = None
|
|
|
|
#: Specify models which should NOT be displayed. ``exclude`` is an array of string formatted as ``app_label.model``.
|
|
#: Also its possible to specify all application models with * sign (e.g. ``auth.*``).
|
|
exclude = None
|
|
hide_empty = True
|
|
|
|
def settings_dict(self):
|
|
return {
|
|
'models': self.models,
|
|
'exclude': self.exclude
|
|
}
|
|
|
|
def load_settings(self, settings):
|
|
self.models = settings.get('models')
|
|
self.exclude = settings.get('exclude')
|
|
|
|
def init_with_context(self, context):
|
|
app_list = get_app_list(context)
|
|
models = []
|
|
|
|
for app in app_list:
|
|
app_name = app.get('app_label', app.get('name', ''))
|
|
app['models'] = filter(
|
|
lambda model: self.models is None or ('%s.%s' % (app_name, model['object_name'])) in self.models or ('%s.*' % app_name) in self.models,
|
|
app['models']
|
|
)
|
|
app['models'] = filter(
|
|
lambda model: self.exclude is None or (('%s.%s' % (app_name, model['object_name'])) not in self.exclude and ('%s.*' % app_name) not in self.exclude),
|
|
app['models']
|
|
)
|
|
app['models'] = list(app['models'])
|
|
|
|
models.extend(app['models'])
|
|
|
|
self.children = models
|
|
|
|
|
|
class RecentActionsSettingsForm(forms.Form):
|
|
limit = forms.IntegerField(label=_('Items limit'), min_value=1)
|
|
|
|
|
|
class RecentActions(DashboardModule):
|
|
"""
|
|
Display list of most recent admin actions with following information:
|
|
entity name, type of action, author, date
|
|
|
|
Usage example:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from jet.dashboard import modules
|
|
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
|
|
|
|
|
class CustomIndexDashboard(Dashboard):
|
|
columns = 3
|
|
|
|
def init_with_context(self, context):
|
|
self.children.append(modules.RecentActions(
|
|
_('Recent Actions'),
|
|
10,
|
|
column=0,
|
|
order=0
|
|
))
|
|
|
|
"""
|
|
|
|
title = _('Recent Actions')
|
|
template = 'jet.dashboard/modules/recent_actions.html'
|
|
|
|
#: Number if entries to be shown (may be changed by each user personally).
|
|
limit = 10
|
|
|
|
#: Specify actions of which models should be displayed. ``include_list`` is an array of string
|
|
#: formatted as ``app_label.model``. Also its possible to specify all application models
|
|
#: with * sign (e.g. ``auth.*``).
|
|
include_list = None
|
|
|
|
#: Specify actions of which models should NOT be displayed. ``exclude_list`` is an array of string
|
|
#: formatted as ``app_label.model``. Also its possible to specify all application models
|
|
#: with * sign (e.g. ``auth.*``).
|
|
exclude_list = None
|
|
settings_form = RecentActionsSettingsForm
|
|
user = None
|
|
|
|
def __init__(self, title=None, limit=10, **kwargs):
|
|
kwargs.update({'limit': limit})
|
|
super(RecentActions, self).__init__(title, **kwargs)
|
|
|
|
def settings_dict(self):
|
|
return {
|
|
'limit': self.limit,
|
|
'include_list': self.include_list,
|
|
'exclude_list': self.exclude_list,
|
|
'user': self.user
|
|
}
|
|
|
|
def load_settings(self, settings):
|
|
self.limit = settings.get('limit', self.limit)
|
|
self.include_list = settings.get('include_list')
|
|
self.exclude_list = settings.get('exclude_list')
|
|
self.user = settings.get('user', None)
|
|
|
|
def init_with_context(self, context):
|
|
def get_qset(list):
|
|
qset = None
|
|
for contenttype in list:
|
|
try:
|
|
app_label, model = contenttype.split('.')
|
|
|
|
if model == '*':
|
|
current_qset = Q(
|
|
content_type__app_label=app_label
|
|
)
|
|
else:
|
|
current_qset = Q(
|
|
content_type__app_label=app_label,
|
|
content_type__model=model
|
|
)
|
|
except:
|
|
raise ValueError('Invalid contenttype: "%s"' % contenttype)
|
|
|
|
if qset is None:
|
|
qset = current_qset
|
|
else:
|
|
qset = qset | current_qset
|
|
return qset
|
|
|
|
qs = LogEntry.objects
|
|
|
|
if self.user:
|
|
qs = qs.filter(
|
|
user__pk=int(self.user)
|
|
)
|
|
|
|
if self.include_list:
|
|
qs = qs.filter(get_qset(self.include_list))
|
|
if self.exclude_list:
|
|
qs = qs.exclude(get_qset(self.exclude_list))
|
|
|
|
self.children = qs.select_related('content_type', 'user')[:int(self.limit)]
|
|
|
|
|
|
class FeedSettingsForm(forms.Form):
|
|
limit = forms.IntegerField(label=_('Items limit'), min_value=1)
|
|
feed_url = forms.URLField(label=_('Feed URL'))
|
|
|
|
|
|
class Feed(DashboardModule):
|
|
"""
|
|
Display RSS Feed entries with following information:
|
|
entry title, date and link to the full version
|
|
|
|
Usage example:
|
|
|
|
.. code-block:: python
|
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
from jet.dashboard import modules
|
|
from jet.dashboard.dashboard import Dashboard, AppIndexDashboard
|
|
|
|
|
|
class CustomIndexDashboard(Dashboard):
|
|
columns = 3
|
|
|
|
def init_with_context(self, context):
|
|
self.children.append(modules.Feed(
|
|
_('Latest Django News'),
|
|
feed_url='http://www.djangoproject.com/rss/weblog/',
|
|
limit=5,
|
|
column=0,
|
|
order=0
|
|
))
|
|
|
|
"""
|
|
|
|
title = _('RSS Feed')
|
|
template = 'jet.dashboard/modules/feed.html'
|
|
|
|
#: URL of the RSS feed (may be changed by each user personally).
|
|
feed_url = None
|
|
|
|
#: Number if entries to be shown (may be changed by each user personally).
|
|
limit = None
|
|
settings_form = FeedSettingsForm
|
|
ajax_load = True
|
|
|
|
def __init__(self, title=None, feed_url=None, limit=None, **kwargs):
|
|
kwargs.update({'feed_url': feed_url, 'limit': limit})
|
|
super(Feed, self).__init__(title, **kwargs)
|
|
|
|
def settings_dict(self):
|
|
return {
|
|
'feed_url': self.feed_url,
|
|
'limit': self.limit
|
|
}
|
|
|
|
def load_settings(self, settings):
|
|
self.feed_url = settings.get('feed_url')
|
|
self.limit = settings.get('limit')
|
|
|
|
def init_with_context(self, context):
|
|
if self.feed_url is not None:
|
|
try:
|
|
import feedparser
|
|
|
|
feed = feedparser.parse(self.feed_url)
|
|
|
|
if self.limit is not None:
|
|
entries = feed['entries'][:self.limit]
|
|
else:
|
|
entries = feed['entries']
|
|
|
|
for entry in entries:
|
|
try:
|
|
entry.date = datetime.date(*entry.published_parsed[0:3])
|
|
except:
|
|
pass
|
|
|
|
self.children.append(entry)
|
|
except ImportError:
|
|
self.children.append({
|
|
'title': _('You must install the FeedParser python module'),
|
|
'warning': True,
|
|
})
|
|
else:
|
|
self.children.append({
|
|
'title': _('You must provide a valid feed URL'),
|
|
'warning': True,
|
|
})
|
|
|