diff --git a/HISTORY.rst b/HISTORY.rst
index 74004b3a43..d8b045d2bd 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -104,6 +104,9 @@
* Add group created and group edited events.
* Add support for SourceColumn widgets.
* Improve styling of the template debug view.
+* Add support for showing the current user's events.
+* Add support kwargs to the SourceColumn class.
+* Improve the event widgets, views and tests.
3.1.11 (2019-04-XX)
===================
diff --git a/docs/releases/3.2.rst b/docs/releases/3.2.rst
index d9778bceba..a6402d28e0 100644
--- a/docs/releases/3.2.rst
+++ b/docs/releases/3.2.rst
@@ -136,6 +136,9 @@ Other changes
* Add group created and group edited events.
* Add support for SourceColumn widgets.
* Improve styling of the template debug view.
+* Add support for showing the current user's events.
+* Add support kwargs to the SourceColumn class.
+* Improve the event widgets, views and tests.
Removals
--------
diff --git a/mayan/apps/events/apps.py b/mayan/apps/events/apps.py
index 169227ed2e..729650a443 100644
--- a/mayan/apps/events/apps.py
+++ b/mayan/apps/events/apps.py
@@ -1,7 +1,6 @@
from __future__ import unicode_literals
from django.apps import apps
-from django.contrib.auth import get_user_model
from django.utils.translation import ugettext_lazy as _
from mayan.apps.common.apps import MayanAppConfig
@@ -11,13 +10,15 @@ from mayan.apps.common.menus import (
from mayan.apps.common.widgets import TwoStateWidget
from mayan.apps.navigation import SourceColumn
+from .html_widgets import (
+ ObjectLinkWidget, widget_event_actor_link, widget_event_type_link
+)
from .links import (
- link_events_list, link_event_types_subscriptions_list,
- link_notification_mark_read, link_notification_mark_read_all,
- link_user_events, link_user_notifications_list,
+ link_current_user_events, link_event_types_subscriptions_list,
+ link_events_list, link_notification_mark_read,
+ link_notification_mark_read_all, link_user_notifications_list,
)
from .licenses import * # NOQA
-from .widgets import event_object_link, event_type_link, event_user_link
class EventsApp(MayanAppConfig):
@@ -33,24 +34,23 @@ class EventsApp(MayanAppConfig):
Action = apps.get_model(app_label='actstream', model_name='Action')
Notification = self.get_model(model_name='Notification')
StoredEventType = self.get_model(model_name='StoredEventType')
- User = get_user_model()
SourceColumn(
source=Action, label=_('Timestamp'), attribute='timestamp'
)
SourceColumn(
- source=Action, label=_('Actor'),
- func=lambda context: event_user_link(context['object'])
+ func=widget_event_actor_link, label=_('Actor'), source=Action
)
SourceColumn(
- source=Action, label=_('Event'),
- func=lambda context: event_type_link(context['object'])
+ func=widget_event_type_link, label=_('Event'), source=Action
)
SourceColumn(
- source=Action, label=_('Action object'),
- func=lambda context: event_object_link(
- entry=context['object'], attribute='action_object'
- )
+ attribute='action_object', label=_('Action object'), source=Action,
+ widget=ObjectLinkWidget
+ )
+ SourceColumn(
+ attribute='target', label=_('Target'), source=Action,
+ widget=ObjectLinkWidget
)
SourceColumn(
@@ -64,17 +64,20 @@ class EventsApp(MayanAppConfig):
source=Notification, label=_('Timestamp'),
attribute='action.timestamp'
)
+
SourceColumn(
- source=Notification, label=_('Actor'), attribute='action.actor'
+ func=widget_event_actor_link, label=_('Actor'),
+ kwargs={'attribute': 'action'}, source=Notification
)
SourceColumn(
- source=Notification, label=_('Event'),
- func=lambda context: event_type_link(context['object'].action)
+ func=widget_event_type_link, label=_('Event'),
+ kwargs={'attribute': 'action'}, source=Notification
)
SourceColumn(
- source=Notification, label=_('Target'),
- func=lambda context: event_object_link(context['object'].action)
+ attribute='action.target', label=_('Target'), source=Notification,
+ widget=ObjectLinkWidget
)
+
SourceColumn(
source=Notification, label=_('Seen'),
func=lambda context: TwoStateWidget(
@@ -88,14 +91,13 @@ class EventsApp(MayanAppConfig):
menu_object.bind_links(
links=(link_notification_mark_read,), sources=(Notification,)
)
- menu_object.bind_links(
- links=(link_user_events,), sources=(User,)
- )
menu_secondary.bind_links(
links=(link_notification_mark_read_all,),
sources=('events:user_notifications_list',)
)
menu_tools.bind_links(links=(link_events_list,))
menu_user.bind_links(
- links=(link_event_types_subscriptions_list,), position=50
+ links=(
+ link_event_types_subscriptions_list, link_current_user_events
+ ), position=50
)
diff --git a/mayan/apps/events/html_widgets.py b/mayan/apps/events/html_widgets.py
new file mode 100644
index 0000000000..3ce2babb92
--- /dev/null
+++ b/mayan/apps/events/html_widgets.py
@@ -0,0 +1,82 @@
+from __future__ import unicode_literals
+
+from django.apps import apps
+from django.template import Context, Template
+from django.urls import reverse
+from django.utils.encoding import force_text
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext_lazy as _
+
+from .classes import EventType
+
+
+class ObjectLinkWidget(object):
+ template_string = '{{ object_type }}{{ label }}'
+
+ def __init__(self):
+ self.template = Template(template_string=self.template_string)
+
+ def render(self, name=None, value=None):
+ label = ''
+ object_type = ''
+ url = None
+
+ if value:
+ label = force_text(value)
+ object_type = '{}: '.format(value._meta.verbose_name)
+ try:
+ url = value.get_absolute_url()
+ except AttributeError:
+ url = None
+
+ return self.template.render(
+ context=Context(
+ {'label': label, 'object_type': object_type, 'url': url or '#'}
+ )
+ )
+
+
+def widget_event_actor_link(context, attribute=None):
+ entry = context['object']
+
+ ContentType = apps.get_model(
+ app_label='contenttypes', model_name='ContentType'
+ )
+
+ if attribute:
+ entry = getattr(entry, attribute)
+
+ if entry.actor == entry.target:
+ label = _('System')
+ url = None
+ else:
+ label = entry.actor
+ content_type = ContentType.objects.get_for_model(model=entry.actor)
+
+ url = reverse(
+ viewname='events:events_for_object', kwargs={
+ 'app_label': content_type.app_label, 'model': content_type.model,
+ 'object_id': entry.actor.pk
+ }
+ )
+
+ if url:
+ return Template(
+ template_string='{{ label }}'
+ ).render(context=Context({'label': entry.actor, 'url': url}))
+ else:
+ return label
+
+
+def widget_event_type_link(context, attribute=None):
+ entry = context['object']
+
+ if attribute:
+ entry = getattr(entry, attribute)
+
+ return mark_safe(
+ '%(label)s' % {
+ 'url': reverse(viewname='events:events_by_verb', kwargs={'verb': entry.verb}),
+ 'label': EventType.get(name=entry.verb)
+ }
+ )
diff --git a/mayan/apps/events/icons.py b/mayan/apps/events/icons.py
index 9a4419ec3d..f02aeb483a 100644
--- a/mayan/apps/events/icons.py
+++ b/mayan/apps/events/icons.py
@@ -5,9 +5,8 @@ from mayan.apps.appearance.classes import Icon
icon_event_types_subscriptions_list = Icon(
driver_name='fontawesome', symbol='list-ol'
)
-icon_events_list = Icon(driver_name='fontawesome', symbol='list-ol')
icon_events_for_object = Icon(driver_name='fontawesome', symbol='list-ol')
-icon_events_user_list = Icon(driver_name='fontawesome', symbol='rss')
+icon_events_list = Icon(driver_name='fontawesome', symbol='list-ol')
icon_object_event_types_user_subcriptions_list = Icon(
driver_name='fontawesome', symbol='rss'
)
diff --git a/mayan/apps/events/links.py b/mayan/apps/events/links.py
index a3b45874d4..85c20d66a3 100644
--- a/mayan/apps/events/links.py
+++ b/mayan/apps/events/links.py
@@ -41,9 +41,9 @@ def get_unread_notification_count(context):
).filter(read=False).count()
-link_events_list = Link(
- icon_class=icon_events_list, permissions=(permission_events_view,),
- text=_('Events'), view='events:events_list'
+link_current_user_events = Link(
+ icon_class=icon_events_list, text=_('My events'),
+ view='events:current_user_events'
)
link_events_details = Link(
text=_('Events'), view='events:events_list'
@@ -54,6 +54,10 @@ link_events_for_object = Link(
permissions=(permission_events_view,), text=_('Events'),
view='events:events_for_object',
)
+link_events_list = Link(
+ icon_class=icon_events_list, permissions=(permission_events_view,),
+ text=_('Events'), view='events:events_list'
+)
link_event_types_subscriptions_list = Link(
icon_class=icon_event_types_subscriptions_list,
text=_('Event subscriptions'),
@@ -72,10 +76,6 @@ link_object_event_types_user_subcriptions_list = Link(
permissions=(permission_events_view,), text=_('Subscriptions'),
view='events:object_event_types_user_subcriptions_list',
)
-link_user_events = Link(
- args='resolved_object.pk', text=_('User events'),
- view='events:user_events'
-)
link_user_notifications_list = Link(
badge_text=get_unread_notification_count,
icon_class=icon_user_notifications_list, text='',
diff --git a/mayan/apps/events/tests/literals.py b/mayan/apps/events/tests/literals.py
new file mode 100644
index 0000000000..ac8a767583
--- /dev/null
+++ b/mayan/apps/events/tests/literals.py
@@ -0,0 +1,6 @@
+from __future__ import unicode_literals
+
+TEST_EVENT_TYPE_NAMESPACE_LABEL = 'test event type namespace label'
+TEST_EVENT_TYPE_NAMESPACE_NAME = 'test_event_type_namespace_name'
+TEST_EVENT_TYPE_LABEL = 'test event type label'
+TEST_EVENT_TYPE_NAME = 'test_event_type_name'
diff --git a/mayan/apps/events/tests/mixins.py b/mayan/apps/events/tests/mixins.py
new file mode 100644
index 0000000000..e4ea3dd31b
--- /dev/null
+++ b/mayan/apps/events/tests/mixins.py
@@ -0,0 +1,20 @@
+from __future__ import unicode_literals
+
+from ..classes import EventTypeNamespace
+
+from .literals import (
+ TEST_EVENT_TYPE_LABEL, TEST_EVENT_TYPE_NAME,
+ TEST_EVENT_TYPE_NAMESPACE_LABEL, TEST_EVENT_TYPE_NAMESPACE_NAME
+)
+
+
+class EventTypeTestMixin(object):
+ def _create_test_event_type(self):
+ self.test_event_type_namespace = EventTypeNamespace(
+ label=TEST_EVENT_TYPE_NAMESPACE_LABEL,
+ name=TEST_EVENT_TYPE_NAMESPACE_NAME
+ )
+ self.test_event_type = self.test_event_type_namespace.add_event_type(
+ label=TEST_EVENT_TYPE_LABEL,
+ name=TEST_EVENT_TYPE_NAME
+ )
diff --git a/mayan/apps/events/tests/test_api.py b/mayan/apps/events/tests/test_api.py
index 449a564182..1312f8e741 100644
--- a/mayan/apps/events/tests/test_api.py
+++ b/mayan/apps/events/tests/test_api.py
@@ -1,9 +1,69 @@
from __future__ import unicode_literals
+from django.contrib.contenttypes.models import ContentType
+
+from rest_framework import status
+
+from mayan.apps.documents.tests import DocumentTestMixin
from mayan.apps.rest_api.tests import BaseAPITestCase
+from ..permissions import permission_events_view
+
+from .mixins import EventTypeTestMixin
+
+
+class EventTypeNamespaceAPITestCase(EventTypeTestMixin, BaseAPITestCase):
+ def setUp(self):
+ super(EventTypeNamespaceAPITestCase, self).setUp()
+ self._create_test_event_type()
-class EventAPITestCase(BaseAPITestCase):
def test_evet_type_list_view(self):
response = self.get(viewname='rest_api:event-type-list')
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+ def test_event_type_namespace_list_view(self):
+ response = self.get(viewname='rest_api:event-type-namespace-list')
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+ def test_event_type_namespace_event_type_list_view(self):
+ response = self.get(
+ viewname='rest_api:event-type-namespace-event-type-list',
+ kwargs={
+ 'name': self.test_event_type_namespace.name
+ }
+ )
+
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+
+
+class ObjectEventAPITestCase(DocumentTestMixin, BaseAPITestCase):
+ auto_upload_document = False
+
+ def setUp(self):
+ super(ObjectEventAPITestCase, self).setUp()
+ self.test_object = self.test_document_type
+
+ content_type = ContentType.objects.get_for_model(model=self.test_object)
+
+ self.view_arguments = {
+ 'app_label': content_type.app_label,
+ 'model': content_type.model,
+ 'object_id': self.test_object.pk
+ }
+
+ def _request_object_event_list_api_view(self):
+ return self.get(
+ viewname='rest_api:object-event-list',
+ kwargs=self.view_arguments
+ )
+
+ def test_object_event_list_view_no_permission(self):
+ response = self._request_object_event_list_api_view()
+ self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
+ def test_object_event_list_view_with_access(self):
+ self.grant_access(
+ obj=self.test_object, permission=permission_events_view
+ )
+ response = self._request_object_event_list_api_view()
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
diff --git a/mayan/apps/events/tests/test_views.py b/mayan/apps/events/tests/test_views.py
index a199a0b7b4..f7fc4cb4c9 100644
--- a/mayan/apps/events/tests/test_views.py
+++ b/mayan/apps/events/tests/test_views.py
@@ -8,15 +8,18 @@ from ..permissions import permission_events_view
class EventsViewTestCase(GenericDocumentViewTestCase):
+ auto_upload_document = False
+
def setUp(self):
super(EventsViewTestCase, self).setUp()
+ self.test_object = self.test_document_type
- content_type = ContentType.objects.get_for_model(self.test_document)
+ content_type = ContentType.objects.get_for_model(self.test_object)
self.view_arguments = {
'app_label': content_type.app_label,
'model': content_type.model,
- 'object_id': self.document.pk
+ 'object_id': self.test_object.pk
}
def _request_events_for_object_view(self):
@@ -27,19 +30,15 @@ class EventsViewTestCase(GenericDocumentViewTestCase):
def test_events_for_object_view_no_permission(self):
response = self._request_events_for_object_view()
self.assertNotContains(
- response=response, text=self.test_document.label, status_code=403
- )
- self.assertNotContains(
- response=response, text='otal:', status_code=403
+ response=response, text=self.test_object.label, status_code=404
)
def test_events_for_object_view_with_permission(self):
- self.grant_permission(permission=permission_events_view)
+ self.grant_access(
+ obj=self.test_object, permission=permission_events_view
+ )
response = self._request_events_for_object_view()
self.assertContains(
- response=response, text=self.test_document.label, status_code=200
- )
- self.assertNotContains(
- response=response, text='otal: 0', status_code=200
+ response=response, text=self.test_object.label, status_code=200
)
diff --git a/mayan/apps/events/urls.py b/mayan/apps/events/urls.py
index 0c9ac0f4d1..f46d2b7ad2 100644
--- a/mayan/apps/events/urls.py
+++ b/mayan/apps/events/urls.py
@@ -8,9 +8,10 @@ from .api_views import (
APINotificationListView, APIObjectEventListView
)
from .views import (
- EventListView, EventTypeSubscriptionListView, NotificationListView,
- NotificationMarkRead, NotificationMarkReadAll, ObjectEventListView,
- ObjectEventTypeSubscriptionListView, UserEventListView, VerbEventListView
+ CurrentUserEventListView, EventListView, EventTypeSubscriptionListView,
+ NotificationListView, NotificationMarkRead, NotificationMarkReadAll,
+ ObjectEventListView, ObjectEventTypeSubscriptionListView,
+ VerbEventListView
)
urlpatterns = [
@@ -31,15 +32,15 @@ urlpatterns = [
regex=r'^notifications/all/mark_read/$',
view=NotificationMarkReadAll.as_view(), name='notification_mark_read_all'
),
+ url(
+ regex=r'^user/events/$', name='current_user_events',
+ view=CurrentUserEventListView.as_view()
+ ),
url(
regex=r'^user/(?P[-\w]+)/(?P[-\w]+)/(?P\d+)/subscriptions/$',
view=ObjectEventTypeSubscriptionListView.as_view(),
name='object_event_types_user_subcriptions_list'
),
- url(
- regex=r'^user/(?P\d+)/events/$', view=UserEventListView.as_view(),
- name='user_events'
- ),
url(
regex=r'^user/event_types/subscriptions/$',
view=EventTypeSubscriptionListView.as_view(),
diff --git a/mayan/apps/events/views.py b/mayan/apps/events/views.py
index 47b448892c..2af4834ede 100644
--- a/mayan/apps/events/views.py
+++ b/mayan/apps/events/views.py
@@ -1,7 +1,6 @@
from __future__ import absolute_import, unicode_literals
from django.contrib import messages
-from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import get_object_or_404
@@ -13,7 +12,6 @@ from actstream.models import Action, any_stream
from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.generics import FormView, SimpleView
-from mayan.apps.common.utils import encapsulate
from mayan.apps.common.views import SingleObjectListView
from .classes import EventType, ModelEventType
@@ -21,12 +19,11 @@ from .forms import (
EventTypeUserRelationshipFormSet, ObjectEventTypeUserRelationshipFormSet
)
from .icons import (
- icon_events_list, icon_events_user_list, icon_user_notifications_list
+ icon_events_list, icon_user_notifications_list
)
from .links import link_event_types_subscriptions_list
from .models import StoredEventType
from .permissions import permission_events_view
-from .widgets import event_object_link
class EventListView(SingleObjectListView):
@@ -34,14 +31,6 @@ class EventListView(SingleObjectListView):
def get_extra_context(self):
return {
- 'extra_columns': (
- {
- 'name': _('Target'),
- 'attribute': encapsulate(
- lambda entry: event_object_link(entry)
- )
- },
- ),
'hide_object': True,
'title': _('Events'),
}
@@ -154,26 +143,10 @@ class NotificationMarkReadAll(SimpleView):
class ObjectEventListView(EventListView):
- view_permissions = None
+ view_permission = None
def dispatch(self, request, *args, **kwargs):
- self.object_content_type = get_object_or_404(
- klass=ContentType, app_label=self.kwargs['app_label'],
- model=self.kwargs['model']
- )
-
- try:
- self.content_object = self.object_content_type.get_object_for_this_type(
- pk=self.kwargs['object_id']
- )
- except self.object_content_type.model_class().DoesNotExist:
- raise Http404
-
- AccessControlList.objects.check_access(
- permissions=permission_events_view, user=request.user,
- obj=self.content_object
- )
-
+ self.object = self.get_object()
return super(
ObjectEventListView, self
).dispatch(request, *args, **kwargs)
@@ -188,13 +161,28 @@ class ObjectEventListView(EventListView):
'or using this object.'
),
'no_results_title': _('There are no events for this object'),
- 'object': self.content_object,
- 'title': _('Events for: %s') % self.content_object,
+ 'object': self.object,
+ 'title': _('Events for: %s') % self.object,
})
return context
+ def get_object(self):
+ content_type = get_object_or_404(
+ klass=ContentType, app_label=self.kwargs['app_label'],
+ model=self.kwargs['model']
+ )
+
+ queryset = AccessControlList.objects.filter_by_access(
+ permission=permission_events_view,
+ queryset=content_type.model_class().objects.all(),
+ user=self.request.user
+ )
+ return get_object_or_404(
+ klass=queryset, pk=self.kwargs['object_id']
+ )
+
def get_object_list(self):
- return any_stream(self.content_object)
+ return any_stream(self.object)
class ObjectEventTypeSubscriptionListView(FormView):
@@ -270,50 +258,14 @@ class ObjectEventTypeSubscriptionListView(FormView):
return ModelEventType.get_for_instance(instance=self.get_object())
-class UserEventListView(SingleObjectListView):
- view_permission = permission_events_view
-
- def get_extra_context(self):
- return {
- 'extra_columns': (
- {
- 'name': _('Target'),
- 'attribute': encapsulate(
- lambda entry: event_object_link(entry)
- )
- },
- ),
- 'hide_object': True,
- 'no_results_icon': icon_events_user_list,
- 'no_results_text': _(
- 'Events are actions that have been performed to this '
- 'user account or by this user account.'
- ),
- 'no_results_title': _('There are no events for this user'),
- 'object': self.get_user(),
- 'title': _(
- 'Events for user: %s'
- ) % self.get_user(),
- }
-
- def get_object_list(self):
- return Action.objects.actor(obj=self.get_user())
-
- def get_user(self):
- return get_object_or_404(klass=get_user_model(), pk=self.kwargs['pk'])
+class CurrentUserEventListView(ObjectEventListView):
+ def get_object(self):
+ return self.request.user
class VerbEventListView(SingleObjectListView):
def get_extra_context(self):
return {
- 'extra_columns': (
- {
- 'name': _('Target'),
- 'attribute': encapsulate(
- lambda entry: event_object_link(entry)
- )
- },
- ),
'hide_object': True,
'title': _(
'Events of type: %s'
diff --git a/mayan/apps/events/widgets.py b/mayan/apps/events/widgets.py
deleted file mode 100644
index 0c69c9c6cd..0000000000
--- a/mayan/apps/events/widgets.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from __future__ import unicode_literals
-
-from django.urls import reverse
-from django.utils.encoding import force_text
-from django.utils.safestring import mark_safe
-from django.utils.translation import ugettext_lazy as _
-
-from .classes import EventType
-
-
-def event_object_link(entry, attribute='target'):
- label = ''
- url = '#'
- obj_type = ''
-
- obj = getattr(entry, attribute)
-
- if obj:
- obj_type = '{}: '.format(obj._meta.verbose_name)
- if hasattr(obj, 'get_absolute_url'):
- url = obj.get_absolute_url()
- label = force_text(obj)
-
- return mark_safe(
- '%(obj_type)s%(label)s' % {
- 'url': url, 'label': label, 'obj_type': obj_type
- }
- )
-
-
-def event_type_link(entry):
- return mark_safe(
- '%(label)s' % {
- 'url': reverse('events:events_by_verb', kwargs={'verb': entry.verb}),
- 'label': EventType.get(name=entry.verb)
- }
- )
-
-
-def event_user_link(entry):
- if entry.actor == entry.target:
- return _('System')
- else:
- return mark_safe(
- '%(label)s' % {
- 'url': reverse('events:user_events', kwargs={'pk': entry.actor.pk}),
- 'label': entry.actor
- }
- )
diff --git a/mayan/apps/navigation/classes.py b/mayan/apps/navigation/classes.py
index cbf60fe7cc..a63face88f 100644
--- a/mayan/apps/navigation/classes.py
+++ b/mayan/apps/navigation/classes.py
@@ -14,7 +14,7 @@ from django.urls import resolve, reverse
from django.utils.encoding import force_str, force_text
from mayan.apps.common.settings import setting_home_view
-from mayan.apps.common.utils import return_attrib
+from mayan.apps.common.utils import resolve_attribute, return_attrib
from mayan.apps.permissions import Permission
from .utils import get_current_view_name
@@ -536,11 +536,12 @@ class SourceColumn(object):
# unhashable type: list
return ()
- def __init__(self, source, label=None, attribute=None, func=None, order=None, widget=None):
+ def __init__(self, source, label=None, attribute=None, func=None, kwargs=None, order=None, widget=None):
self.source = source
self._label = label
self.attribute = attribute
self.func = func
+ self.kwargs = kwargs or {}
self.order = order or 0
self.__class__._registry.setdefault(source, [])
self.__class__._registry[source].append(self)
@@ -561,9 +562,12 @@ class SourceColumn(object):
def resolve(self, context):
if self.attribute:
- result = return_attrib(context['object'], self.attribute)
+ result = resolve_attribute(
+ attribute=self.attribute, kwargs=self.kwargs,
+ obj=context['object']
+ )
elif self.func:
- result = self.func(context=context)
+ result = self.func(context=context, **self.kwargs)
if self.widget:
widget_instance = self.widget()