| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- from django import forms
- from django.apps import apps
- from django.core.exceptions import PermissionDenied
- from django.urls.base import reverse, NoReverseMatch
- from django.template.context_processors import csrf
- from django.db.models.base import ModelBase
- from django.forms.forms import DeclarativeFieldsMetaclass
- from django.forms.utils import flatatt
- from django.template import loader
- from django.http import Http404
- from django.test.client import RequestFactory
- from django.utils.encoding import force_text, smart_text
- from django.utils.html import escape
- from django.utils.safestring import mark_safe
- from django.utils.translation import ugettext as _
- from django.utils.http import urlencode, urlquote
- from django.views.decorators.cache import never_cache
- from xadmin import widgets as exwidgets
- from xadmin.layout import FormHelper
- from xadmin.models import UserSettings, UserWidget
- from xadmin.plugins.utils import get_context_dict
- from xadmin.sites import site
- from xadmin.views.base import CommAdminView, ModelAdminView, filter_hook, csrf_protect_m
- from xadmin.views.edit import CreateAdminView
- from xadmin.views.list import ListAdminView
- from xadmin.util import unquote
- import copy
- class WidgetTypeSelect(forms.Widget):
- def __init__(self, widgets, attrs=None):
- super(WidgetTypeSelect, self).__init__(attrs)
- self._widgets = widgets
- def render(self, name, value, attrs=None):
- if value is None:
- value = ''
- final_attrs = self.build_attrs(attrs, extra_attrs={'name': name})
- final_attrs['class'] = 'nav nav-pills nav-stacked'
- output = [u'<ul%s>' % flatatt(final_attrs)]
- options = self.render_options(force_text(value), final_attrs['id'])
- if options:
- output.append(options)
- output.append(u'</ul>')
- output.append('<input type="hidden" id="%s_input" name="%s" value="%s"/>' %
- (final_attrs['id'], name, force_text(value)))
- return mark_safe(u'\n'.join(output))
- def render_option(self, selected_choice, widget, id):
- if widget.widget_type == selected_choice:
- selected_html = u' class="active"'
- else:
- selected_html = ''
- return (u'<li%s><a onclick="' +
- 'javascript:$(this).parent().parent().find(\'>li\').removeClass(\'active\');$(this).parent().addClass(\'active\');' +
- '$(\'#%s_input\').attr(\'value\', \'%s\')' % (id, widget.widget_type) +
- '"><h4><i class="%s"></i> %s</h4><p>%s</p></a></li>') % (
- selected_html,
- widget.widget_icon,
- widget.widget_title or widget.widget_type,
- widget.description)
- def render_options(self, selected_choice, id):
- # Normalize to strings.
- output = []
- for widget in self._widgets:
- output.append(self.render_option(selected_choice, widget, id))
- return u'\n'.join(output)
- class UserWidgetAdmin(object):
- model_icon = 'fa fa-dashboard'
- list_display = ('widget_type', 'page_id', 'user')
- list_filter = ['user', 'widget_type', 'page_id']
- list_display_links = ('widget_type',)
- user_fields = ['user']
- hidden_menu = True
- wizard_form_list = (
- (_(u"Widget Type"), ('page_id', 'widget_type')),
- (_(u"Widget Params"), {'callback':
- "get_widget_params_form", 'convert': "convert_widget_params"})
- )
- def formfield_for_dbfield(self, db_field, **kwargs):
- if db_field.name == 'widget_type':
- widgets = widget_manager.get_widgets(self.request.GET.get('page_id', ''))
- form_widget = WidgetTypeSelect(widgets)
- return forms.ChoiceField(choices=[(w.widget_type, w.description) for w in widgets],
- widget=form_widget, label=_('Widget Type'))
- if 'page_id' in self.request.GET and db_field.name == 'page_id':
- kwargs['widget'] = forms.HiddenInput
- field = super(
- UserWidgetAdmin, self).formfield_for_dbfield(db_field, **kwargs)
- return field
- def get_widget_params_form(self, wizard):
- data = wizard.get_cleaned_data_for_step(wizard.steps.first)
- widget_type = data['widget_type']
- widget = widget_manager.get(widget_type)
- fields = copy.deepcopy(widget.base_fields)
- if 'id' in fields:
- del fields['id']
- return DeclarativeFieldsMetaclass("WidgetParamsForm", (forms.Form,), fields)
- def convert_widget_params(self, wizard, cleaned_data, form):
- widget = UserWidget()
- value = dict([(f.name, f.value()) for f in form])
- widget.set_value(value)
- cleaned_data['value'] = widget.value
- cleaned_data['user'] = self.user
- def get_list_display(self):
- list_display = super(UserWidgetAdmin, self).get_list_display()
- if not self.user.is_superuser:
- list_display.remove('user')
- return list_display
- def queryset(self):
- if self.user.is_superuser:
- return super(UserWidgetAdmin, self).queryset()
- return UserWidget.objects.filter(user=self.user)
- def update_dashboard(self, obj):
- try:
- portal_pos = UserSettings.objects.get(
- user=obj.user, key="dashboard:%s:pos" % obj.page_id)
- except UserSettings.DoesNotExist:
- return
- pos = [[w for w in col.split(',') if w != str(
- obj.id)] for col in portal_pos.value.split('|')]
- portal_pos.value = '|'.join([','.join(col) for col in pos])
- portal_pos.save()
- def delete_model(self):
- self.update_dashboard(self.obj)
- super(UserWidgetAdmin, self).delete_model()
- def delete_models(self, queryset):
- for obj in queryset:
- self.update_dashboard(obj)
- super(UserWidgetAdmin, self).delete_models(queryset)
- site.register(UserWidget, UserWidgetAdmin)
- class WidgetManager(object):
- _widgets = None
- def __init__(self):
- self._widgets = {}
- def register(self, widget_class):
- self._widgets[widget_class.widget_type] = widget_class
- return widget_class
- def get(self, name):
- return self._widgets[name]
- def get_widgets(self, page_id):
- return self._widgets.values()
- widget_manager = WidgetManager()
- class WidgetDataError(Exception):
- def __init__(self, widget, errors):
- super(WidgetDataError, self).__init__(str(errors))
- self.widget = widget
- self.errors = errors
- class BaseWidget(forms.Form):
- template = 'xadmin/widgets/base.html'
- description = 'Base Widget, don\'t use it.'
- widget_title = None
- widget_icon = 'fa fa-plus-square'
- widget_type = 'base'
- base_title = None
- id = forms.IntegerField(label=_('Widget ID'), widget=forms.HiddenInput)
- title = forms.CharField(label=_('Widget Title'), required=False, widget=exwidgets.AdminTextInputWidget)
- def __init__(self, dashboard, data):
- self.dashboard = dashboard
- self.admin_site = dashboard.admin_site
- self.request = dashboard.request
- self.user = dashboard.request.user
- self.convert(data)
- super(BaseWidget, self).__init__(data)
- if not self.is_valid():
- raise WidgetDataError(self, self.errors.as_text())
- self.setup()
- def setup(self):
- helper = FormHelper()
- helper.form_tag = False
- helper.include_media = False
- self.helper = helper
- self.id = self.cleaned_data['id']
- self.title = self.cleaned_data['title'] or self.base_title
- if not (self.user.is_superuser or self.has_perm()):
- raise PermissionDenied
- @property
- def widget(self):
- context = {'widget_id': self.id, 'widget_title': self.title, 'widget_icon': self.widget_icon,
- 'widget_type': self.widget_type, 'form': self, 'widget': self}
- context.update(csrf(self.request))
- self.context(context)
- return loader.render_to_string(self.template, context)
- def context(self, context):
- pass
- def convert(self, data):
- pass
- def has_perm(self):
- return False
- def save(self):
- value = dict([(f.name, f.value()) for f in self])
- user_widget = UserWidget.objects.get(id=self.id)
- user_widget.set_value(value)
- user_widget.save()
- def static(self, path):
- return self.dashboard.static(path)
- def vendor(self, *tags):
- return self.dashboard.vendor(*tags)
- def media(self):
- return forms.Media()
- @widget_manager.register
- class HtmlWidget(BaseWidget):
- widget_type = 'html'
- widget_icon = 'fa fa-file-o'
- description = _(
- u'Html Content Widget, can write any html content in widget.')
- content = forms.CharField(label=_(
- 'Html Content'), widget=exwidgets.AdminTextareaWidget, required=False)
- def has_perm(self):
- return True
- def context(self, context):
- context['content'] = self.cleaned_data['content']
- class ModelChoiceIterator(object):
- def __init__(self, field):
- self.field = field
- def __iter__(self):
- from xadmin import site as g_admin_site
- for m, ma in g_admin_site._registry.items():
- yield ('%s.%s' % (m._meta.app_label, m._meta.model_name),
- m._meta.verbose_name)
- class ModelChoiceField(forms.ChoiceField):
- def __init__(self, *, required=True, widget=None, label=None, initial=None,
- help_text=None, **kwargs):
- # Call Field instead of ChoiceField __init__() because we don't need
- # ChoiceField.__init__().
- forms.Field.__init__(self, **kwargs)
- self.widget.choices = self.choices
- def __deepcopy__(self, memo):
- result = forms.Field.__deepcopy__(self, memo)
- return result
- def _get_choices(self):
- return ModelChoiceIterator(self)
- choices = property(_get_choices, forms.ChoiceField._set_choices)
- def to_python(self, value):
- if isinstance(value, ModelBase):
- return value
- app_label, model_name = value.lower().split('.')
- return apps.get_model(app_label, model_name)
- def prepare_value(self, value):
- if isinstance(value, ModelBase):
- value = '%s.%s' % (value._meta.app_label, value._meta.model_name)
- return value
- def valid_value(self, value):
- value = self.prepare_value(value)
- for k, v in self.choices:
- if value == smart_text(k):
- return True
- return False
- class ModelBaseWidget(BaseWidget):
- app_label = None
- model_name = None
- model_perm = 'change'
- model = ModelChoiceField(label=_(u'Target Model'), widget=exwidgets.AdminSelectWidget)
- def __init__(self, dashboard, data):
- self.dashboard = dashboard
- super(ModelBaseWidget, self).__init__(dashboard, data)
- def setup(self):
- self.model = self.cleaned_data['model']
- self.app_label = self.model._meta.app_label
- self.model_name = self.model._meta.model_name
- super(ModelBaseWidget, self).setup()
- def has_perm(self):
- return self.dashboard.has_model_perm(self.model, self.model_perm)
- def filte_choices_model(self, model, modeladmin):
- return self.dashboard.has_model_perm(model, self.model_perm)
- def model_admin_url(self, name, *args, **kwargs):
- return reverse(
- "%s:%s_%s_%s" % (self.admin_site.app_name, self.app_label,
- self.model_name, name), args=args, kwargs=kwargs)
- class PartialBaseWidget(BaseWidget):
- def get_view_class(self, view_class, model=None, **opts):
- admin_class = self.admin_site._registry.get(model) if model else None
- return self.admin_site.get_view_class(view_class, admin_class, **opts)
- def get_factory(self):
- return RequestFactory()
- def setup_request(self, request):
- request.user = self.user
- request.session = self.request.session
- return request
- def make_get_request(self, path, data={}, **extra):
- req = self.get_factory().get(path, data, **extra)
- return self.setup_request(req)
- def make_post_request(self, path, data={}, **extra):
- req = self.get_factory().post(path, data, **extra)
- return self.setup_request(req)
- @widget_manager.register
- class QuickBtnWidget(BaseWidget):
- widget_type = 'qbutton'
- description = _(u'Quick button Widget, quickly open any page.')
- template = "xadmin/widgets/qbutton.html"
- base_title = _(u"Quick Buttons")
- widget_icon = 'fa fa-caret-square-o-right'
- def convert(self, data):
- self.q_btns = data.pop('btns', [])
- def get_model(self, model_or_label):
- if isinstance(model_or_label, ModelBase):
- return model_or_label
- else:
- return apps.get_model(*model_or_label.lower().split('.'))
- def context(self, context):
- btns = []
- for b in self.q_btns:
- btn = {}
- if 'model' in b:
- model = self.get_model(b['model'])
- if not self.user.has_perm("%s.view_%s" % (model._meta.app_label, model._meta.model_name)):
- continue
- btn['url'] = reverse("%s:%s_%s_%s" % (self.admin_site.app_name, model._meta.app_label,
- model._meta.model_name, b.get('view', 'changelist')))
- btn['title'] = model._meta.verbose_name
- btn['icon'] = self.dashboard.get_model_icon(model)
- else:
- try:
- btn['url'] = reverse(b['url'])
- except NoReverseMatch:
- btn['url'] = b['url']
- if 'title' in b:
- btn['title'] = b['title']
- if 'icon' in b:
- btn['icon'] = b['icon']
- btns.append(btn)
- context.update({'btns': btns})
- def has_perm(self):
- return True
- @widget_manager.register
- class ListWidget(ModelBaseWidget, PartialBaseWidget):
- widget_type = 'list'
- description = _(u'Any Objects list Widget.')
- template = "xadmin/widgets/list.html"
- model_perm = 'view'
- widget_icon = 'fa fa-align-justify'
- def convert(self, data):
- self.list_params = data.pop('params', {})
- self.list_count = data.pop('count', 10)
- def setup(self):
- super(ListWidget, self).setup()
- if not self.title:
- self.title = self.model._meta.verbose_name_plural
- req = self.make_get_request("", self.list_params)
- self.list_view = self.get_view_class(ListAdminView, self.model)(req)
- if self.list_count:
- self.list_view.list_per_page = self.list_count
- def context(self, context):
- list_view = self.list_view
- list_view.make_result_list()
- base_fields = list_view.base_list_display
- if len(base_fields) > 5:
- base_fields = base_fields[0:5]
- context['result_headers'] = [c for c in list_view.result_headers(
- ).cells if c.field_name in base_fields]
- context['results'] = [[o for i, o in
- enumerate(filter(lambda c:c.field_name in base_fields, r.cells))]
- for r in list_view.results()]
- context['result_count'] = list_view.result_count
- context['page_url'] = self.model_admin_url('changelist') + "?" + urlencode(self.list_params)
- @widget_manager.register
- class AddFormWidget(ModelBaseWidget, PartialBaseWidget):
- widget_type = 'addform'
- description = _(u'Add any model object Widget.')
- template = "xadmin/widgets/addform.html"
- model_perm = 'add'
- widget_icon = 'fa fa-plus'
- def setup(self):
- super(AddFormWidget, self).setup()
- if self.title is None:
- self.title = _('Add %s') % self.model._meta.verbose_name
- req = self.make_get_request("")
- self.add_view = self.get_view_class(
- CreateAdminView, self.model, list_per_page=10)(req)
- self.add_view.instance_forms()
- def context(self, context):
- helper = FormHelper()
- helper.form_tag = False
- helper.include_media = False
- context.update({
- 'addform': self.add_view.form_obj,
- 'addhelper': helper,
- 'addurl': self.add_view.model_admin_url('add'),
- 'model': self.model
- })
- def media(self):
- return self.add_view.media + self.add_view.form_obj.media + self.vendor('xadmin.plugin.quick-form.js')
- class Dashboard(CommAdminView):
- widget_customiz = True
- widgets = []
- title = _(u"Dashboard")
- icon = None
- def get_page_id(self):
- return self.request.path
- def get_portal_key(self):
- return "dashboard:%s:pos" % self.get_page_id()
- @filter_hook
- def get_widget(self, widget_or_id, data=None):
- try:
- if isinstance(widget_or_id, UserWidget):
- widget = widget_or_id
- else:
- widget = UserWidget.objects.get(user=self.user, page_id=self.get_page_id(), id=widget_or_id)
- wid = widget_manager.get(widget.widget_type)
- class widget_with_perm(wid):
- def context(self, context):
- super(widget_with_perm, self).context(context)
- context.update({'has_change_permission': self.request.user.has_perm('xadmin.change_userwidget')})
- wid_instance = widget_with_perm(self, data or widget.get_value())
- return wid_instance
- except UserWidget.DoesNotExist:
- return None
- @filter_hook
- def get_init_widget(self):
- portal = []
- widgets = self.widgets
- for col in widgets:
- portal_col = []
- for opts in col:
- try:
- widget = UserWidget(user=self.user, page_id=self.get_page_id(), widget_type=opts['type'])
- widget.set_value(opts)
- widget.save()
- portal_col.append(self.get_widget(widget))
- except (PermissionDenied, WidgetDataError):
- widget.delete()
- continue
- portal.append(portal_col)
- UserSettings(
- user=self.user, key="dashboard:%s:pos" % self.get_page_id(),
- value='|'.join([','.join([str(w.id) for w in col]) for col in portal])).save()
- return portal
- @filter_hook
- def get_widgets(self):
- if self.widget_customiz:
- portal_pos = UserSettings.objects.filter(
- user=self.user, key=self.get_portal_key())
- if len(portal_pos):
- portal_pos = portal_pos[0].value
- widgets = []
- if portal_pos:
- user_widgets = dict([(uw.id, uw) for uw in UserWidget.objects.filter(user=self.user, page_id=self.get_page_id())])
- for col in portal_pos.split('|'):
- ws = []
- for wid in col.split(','):
- try:
- widget = user_widgets.get(int(wid))
- if widget:
- ws.append(self.get_widget(widget))
- except Exception as e:
- import logging
- logging.error(e, exc_info=True)
- widgets.append(ws)
- return widgets
- return self.get_init_widget()
- @filter_hook
- def get_title(self):
- return self.title
- @filter_hook
- def get_context(self):
- new_context = {
- 'title': self.get_title(),
- 'icon': self.icon,
- 'portal_key': self.get_portal_key(),
- 'columns': [('col-sm-%d' % int(12 / len(self.widgets)), ws) for ws in self.widgets],
- 'has_add_widget_permission': self.has_model_perm(UserWidget, 'add') and self.widget_customiz,
- 'add_widget_url': self.get_admin_url('%s_%s_add' % (UserWidget._meta.app_label, UserWidget._meta.model_name)) +
- "?user=%s&page_id=%s&_redirect=%s" % (self.user.id, self.get_page_id(), urlquote(self.request.get_full_path()))
- }
- context = super(Dashboard, self).get_context()
- context.update(new_context)
- return context
- @never_cache
- def get(self, request, *args, **kwargs):
- self.widgets = self.get_widgets()
- return self.template_response('xadmin/views/dashboard.html', self.get_context())
- @csrf_protect_m
- def post(self, request, *args, **kwargs):
- if 'id' in request.POST:
- widget_id = request.POST['id']
- if request.POST.get('_delete', None) != 'on':
- widget = self.get_widget(widget_id, request.POST.copy())
- widget.save()
- else:
- try:
- widget = UserWidget.objects.get(
- user=self.user, page_id=self.get_page_id(), id=widget_id)
- widget.delete()
- try:
- portal_pos = UserSettings.objects.get(user=self.user, key="dashboard:%s:pos" % self.get_page_id())
- pos = [[w for w in col.split(',') if w != str(
- widget_id)] for col in portal_pos.value.split('|')]
- portal_pos.value = '|'.join([','.join(col) for col in pos])
- portal_pos.save()
- except Exception:
- pass
- except UserWidget.DoesNotExist:
- pass
- return self.get(request)
- @filter_hook
- def get_media(self):
- media = super(Dashboard, self).get_media() + \
- self.vendor('xadmin.page.dashboard.js', 'xadmin.page.dashboard.css')
- if self.widget_customiz:
- media = media + self.vendor('xadmin.plugin.portal.js')
- for ws in self.widgets:
- for widget in ws:
- media = media + widget.media()
- return media
- class ModelDashboard(Dashboard, ModelAdminView):
- title = _(u"%s Dashboard")
- def get_page_id(self):
- return 'model:%s/%s' % self.model_info
- @filter_hook
- def get_title(self):
- return self.title % force_text(self.obj)
- def init_request(self, object_id, *args, **kwargs):
- self.obj = self.get_object(unquote(object_id))
- if not self.has_view_permission(self.obj):
- raise PermissionDenied
- if self.obj is None:
- raise Http404(_('%(name)s object with primary key %(key)r does not exist.') %
- {'name': force_text(self.opts.verbose_name), 'key': escape(object_id)})
- @filter_hook
- def get_context(self):
- new_context = {
- 'has_change_permission': self.has_change_permission(self.obj),
- 'object': self.obj,
- }
- context = Dashboard.get_context(self)
- context.update(ModelAdminView.get_context(self))
- context.update(new_context)
- return context
- @never_cache
- def get(self, request, *args, **kwargs):
- self.widgets = self.get_widgets()
- return self.template_response(self.get_template_list('views/model_dashboard.html'), self.get_context())
|