Backport support for global and object event notification. GitLab issue #262.
Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import logging
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common import MayanAppConfig, menu_user
|
||||
from navigation.classes import Separator, Text
|
||||
|
||||
from .links import link_logout, link_password_change
|
||||
|
||||
@@ -21,6 +22,6 @@ class AuthenticationApp(MayanAppConfig):
|
||||
|
||||
menu_user.bind_links(
|
||||
links=(
|
||||
link_password_change, link_logout
|
||||
Separator(), link_password_change, link_logout
|
||||
), position=99
|
||||
)
|
||||
|
||||
@@ -2,13 +2,13 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_cabinets_add_document = Event(
|
||||
name='cabinets_add_document',
|
||||
label=_('Document added to cabinet')
|
||||
namespace = EventTypeNamespace(name='cabinets', label=_('Cabinets'))
|
||||
|
||||
event_cabinets_add_document = namespace.add_event_type(
|
||||
label=_('Document added to cabinet'), name='add_document'
|
||||
)
|
||||
event_cabinets_remove_document = Event(
|
||||
name='cabinets_remove_document',
|
||||
label=_('Document removed from cabinet')
|
||||
event_cabinets_remove_document = namespace.add_event_type(
|
||||
label=_('Document removed from cabinet'), name='remove_document'
|
||||
)
|
||||
|
||||
@@ -10,11 +10,17 @@ 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 events import ModelEventType
|
||||
from mayan.celery import app
|
||||
from rest_api.classes import APIEndPoint
|
||||
|
||||
from .dashboard_widgets import widget_checkouts
|
||||
from .events import (
|
||||
event_document_auto_check_in, event_document_check_in,
|
||||
event_document_check_out, event_document_forceful_check_in
|
||||
)
|
||||
from .handlers import check_new_version_creation
|
||||
from .links import (
|
||||
link_checkin_document, link_checkout_document, link_checkout_info,
|
||||
@@ -72,6 +78,13 @@ class CheckoutsApp(MayanAppConfig):
|
||||
)
|
||||
)
|
||||
|
||||
ModelEventType.register(
|
||||
model=Document, event_types=(
|
||||
event_document_auto_check_in, event_document_check_in,
|
||||
event_document_check_out, event_document_forceful_check_in
|
||||
)
|
||||
)
|
||||
|
||||
ModelPermission.register(
|
||||
model=Document, permissions=(
|
||||
permission_document_checkout,
|
||||
|
||||
@@ -2,19 +2,21 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_document_auto_check_in = Event(
|
||||
name='checkouts_document_auto_check_in',
|
||||
namespace = EventTypeNamespace(name='checkouts', label=_('Checkouts'))
|
||||
|
||||
event_document_auto_check_in = namespace.add_event_type(
|
||||
name='document_auto_check_in',
|
||||
label=_('Document automatically checked in')
|
||||
)
|
||||
event_document_check_in = Event(
|
||||
name='checkouts_document_check_in', label=_('Document checked in')
|
||||
event_document_check_in = namespace.add_event_type(
|
||||
name='document_check_in', label=_('Document checked in')
|
||||
)
|
||||
event_document_check_out = Event(
|
||||
name='checkouts_document_check_out', label=_('Document checked out')
|
||||
event_document_check_out = namespace.add_event_type(
|
||||
name='document_check_out', label=_('Document checked out')
|
||||
)
|
||||
event_document_forceful_check_in = Event(
|
||||
name='checkouts_document_forceful_check_in',
|
||||
event_document_forceful_check_in = namespace.add_event_type(
|
||||
name='document_forceful_check_in',
|
||||
label=_('Document forcefully checked in')
|
||||
)
|
||||
|
||||
@@ -67,8 +67,8 @@ class MayanAppConfig(apps.AppConfig):
|
||||
except ImportError as exception:
|
||||
if force_text(exception) not in ('No module named urls', 'No module named \'{}.urls\''.format(self.name)):
|
||||
logger.error(
|
||||
'Import time error when running AppConfig.ready(). Check '
|
||||
'apps.py, urls.py, views.py, etc.'
|
||||
'Import time error when running AppConfig.ready() of app '
|
||||
'"%s".', self.name
|
||||
)
|
||||
raise exception
|
||||
|
||||
@@ -127,7 +127,6 @@ class CommonApp(MayanAppConfig):
|
||||
Text(text=CommonApp.get_user_label_text), Separator(),
|
||||
link_current_user_details, link_current_user_edit,
|
||||
link_current_user_locale_profile_edit,
|
||||
Separator()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from __future__ import unicode_literals
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
@@ -6,9 +6,13 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from acls import ModelPermission
|
||||
from common import MayanAppConfig, menu_facet, menu_object, menu_sidebar
|
||||
from documents.search import document_page_search, document_search
|
||||
from events import ModelEventType
|
||||
from navigation import SourceColumn
|
||||
from rest_api.classes import APIEndPoint
|
||||
|
||||
from .events import (
|
||||
event_document_comment_create, event_document_comment_delete
|
||||
)
|
||||
from .links import (
|
||||
link_comment_add, link_comment_delete, link_comments_for_document
|
||||
)
|
||||
@@ -36,6 +40,12 @@ class DocumentCommentsApp(MayanAppConfig):
|
||||
|
||||
Comment = self.get_model('Comment')
|
||||
|
||||
ModelEventType.register(
|
||||
model=Document, event_types=(
|
||||
event_document_comment_create, event_document_comment_delete
|
||||
)
|
||||
)
|
||||
|
||||
ModelPermission.register(
|
||||
model=Document, permissions=(
|
||||
permission_comment_create, permission_comment_delete,
|
||||
|
||||
@@ -2,13 +2,15 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_document_comment_create = Event(
|
||||
name='document_comment_create',
|
||||
label=_('Document comment created')
|
||||
namespace = EventTypeNamespace(
|
||||
name='document_comments', label=_('Document comments')
|
||||
)
|
||||
event_document_comment_delete = Event(
|
||||
name='document_comment_delete',
|
||||
label=_('Document comment deleted')
|
||||
|
||||
event_document_comment_create = namespace.add_event_type(
|
||||
name='create', label=_('Document comment created')
|
||||
)
|
||||
event_document_comment_delete = namespace.add_event_type(
|
||||
name='delete', label=_('Document comment deleted')
|
||||
)
|
||||
|
||||
@@ -2,13 +2,15 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_parsing_document_version_submit = Event(
|
||||
name='parsing_document_version_submit',
|
||||
label=_('Document version submitted for parsing')
|
||||
namespace = EventTypeNamespace(
|
||||
name='document_parsing', label=_('Document parsing')
|
||||
)
|
||||
event_parsing_document_version_finish = Event(
|
||||
name='parsing_document_version_finish',
|
||||
label=_('Document version parsing finished')
|
||||
|
||||
event_parsing_document_version_submit = namespace.add_event_type(
|
||||
label=_('Document version submitted for parsing'), name='version_submit'
|
||||
)
|
||||
event_parsing_document_version_finish = namespace.add_event_type(
|
||||
label=_('Document version parsing finished'), name='version_finish'
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from django.apps import apps
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from document_indexing.tasks import task_index_document
|
||||
from events.classes import Event
|
||||
from events.classes import EventType
|
||||
|
||||
|
||||
def handler_index_document(sender, **kwargs):
|
||||
@@ -42,7 +42,7 @@ def handler_trigger_transition(sender, **kwargs):
|
||||
transition = list(set(trigger_transitions) & set(workflow_instance.get_transition_choices()))[0]
|
||||
|
||||
workflow_instance.do_transition(
|
||||
comment=_('Event trigger: %s') % Event.get(name=action.verb).label,
|
||||
comment=_('Event trigger: %s') % EventType.get(name=action.verb).label,
|
||||
transition=transition
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import django.db.models.deletion
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0001_initial'),
|
||||
('events', '0005_auto_20170731_0452'),
|
||||
('document_states', '0004_workflow_internal_name'),
|
||||
]
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
from acls.models import AccessControlList
|
||||
from common.validators import validate_internal_name
|
||||
from documents.models import Document, DocumentType
|
||||
from events.models import EventType
|
||||
from events.models import StoredEventType
|
||||
from permissions import Permission
|
||||
|
||||
from .error_logs import error_log_state_actions
|
||||
@@ -306,7 +306,8 @@ class WorkflowTransitionTriggerEvent(models.Model):
|
||||
related_name='trigger_events', verbose_name=_('Transition')
|
||||
)
|
||||
event_type = models.ForeignKey(
|
||||
EventType, on_delete=models.CASCADE, verbose_name=_('Event type')
|
||||
StoredEventType, on_delete=models.CASCADE,
|
||||
verbose_name=_('Event type')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
@@ -17,8 +17,8 @@ from common.views import (
|
||||
)
|
||||
from documents.models import Document
|
||||
from documents.views import DocumentListView
|
||||
from events.classes import Event
|
||||
from events.models import EventType
|
||||
from events.classes import EventType
|
||||
from events.models import StoredEventType
|
||||
|
||||
from .classes import WorkflowAction
|
||||
from .forms import (
|
||||
@@ -675,7 +675,7 @@ class WorkflowStateListView(SingleObjectListView):
|
||||
|
||||
class SetupWorkflowTransitionTriggerEventListView(FormView):
|
||||
form_class = WorkflowTransitionTriggerEventRelationshipFormSet
|
||||
submodel = EventType
|
||||
submodel = StoredEventType
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
messages.warning(
|
||||
@@ -689,7 +689,7 @@ class SetupWorkflowTransitionTriggerEventListView(FormView):
|
||||
user=self.request.user, obj=self.get_object().workflow
|
||||
)
|
||||
|
||||
Event.refresh()
|
||||
EventType.refresh()
|
||||
return super(
|
||||
SetupWorkflowTransitionTriggerEventListView, self
|
||||
).dispatch(*args, **kwargs)
|
||||
@@ -735,8 +735,10 @@ class SetupWorkflowTransitionTriggerEventListView(FormView):
|
||||
initial = []
|
||||
|
||||
# Return the queryset by name from the sorted list of the class
|
||||
event_type_ids = [event_type.name for event_type in Event.all()]
|
||||
event_type_queryset = EventType.objects.filter(name__in=event_type_ids)
|
||||
event_type_ids = [event_type.id for event_type in Event.all()]
|
||||
event_type_queryset = StoredEventType.objects.filter(
|
||||
name__in=event_type_ids
|
||||
)
|
||||
|
||||
for event_type in event_type_queryset:
|
||||
initial.append({
|
||||
|
||||
@@ -23,7 +23,11 @@ from converter.permissions import (
|
||||
permission_transformation_delete, permission_transformation_edit,
|
||||
permission_transformation_view,
|
||||
)
|
||||
from events.links import link_events_for_object
|
||||
from events import ModelEventType
|
||||
from events.links import (
|
||||
link_events_for_object, link_object_event_types_user_subcriptions_list,
|
||||
link_object_event_types_user_subcriptions_list_with_icon
|
||||
)
|
||||
from events.permissions import permission_events_view
|
||||
from mayan.celery import app
|
||||
from mayan_statistics.classes import StatisticNamespace, CharJSLine
|
||||
@@ -36,6 +40,12 @@ from .dashboard_widgets import (
|
||||
widget_new_documents_this_month, widget_pages_per_month,
|
||||
widget_total_documents
|
||||
)
|
||||
from .events import (
|
||||
event_document_create, event_document_download,
|
||||
event_document_properties_edit, event_document_type_change,
|
||||
event_document_new_version, event_document_version_revert,
|
||||
event_document_view
|
||||
)
|
||||
from .handlers import (
|
||||
create_default_document_type, handler_scan_duplicates_for
|
||||
)
|
||||
@@ -142,6 +152,19 @@ class DocumentsApp(MayanAppConfig):
|
||||
label=_('MIME type'), name='versions__mimetype', type_name='field'
|
||||
)
|
||||
|
||||
ModelEventType.register(
|
||||
model=DocumentType, event_types=(
|
||||
event_document_create,
|
||||
)
|
||||
)
|
||||
ModelEventType.register(
|
||||
model=Document, event_types=(
|
||||
event_document_download, event_document_properties_edit,
|
||||
event_document_type_change, event_document_new_version,
|
||||
event_document_version_revert, event_document_view
|
||||
)
|
||||
)
|
||||
|
||||
ModelPermission.register(
|
||||
model=Document, permissions=(
|
||||
permission_acl_edit, permission_acl_view,
|
||||
@@ -389,7 +412,8 @@ class DocumentsApp(MayanAppConfig):
|
||||
menu_object.bind_links(
|
||||
links=(
|
||||
link_document_type_edit, link_document_type_filename_list,
|
||||
link_acl_list, link_document_type_delete
|
||||
link_acl_list, link_object_event_types_user_subcriptions_list,
|
||||
link_document_type_delete
|
||||
), sources=(DocumentType,)
|
||||
)
|
||||
menu_object.bind_links(
|
||||
@@ -446,8 +470,11 @@ class DocumentsApp(MayanAppConfig):
|
||||
links=(link_document_properties,), sources=(Document,), position=2
|
||||
)
|
||||
menu_facet.bind_links(
|
||||
links=(link_events_for_object, link_document_version_list,),
|
||||
sources=(Document,), position=2
|
||||
links=(
|
||||
link_events_for_object,
|
||||
link_object_event_types_user_subcriptions_list_with_icon,
|
||||
link_document_version_list,
|
||||
), sources=(Document,), position=2
|
||||
)
|
||||
menu_facet.bind_links(links=(link_document_pages,), sources=(Document,))
|
||||
|
||||
|
||||
@@ -2,29 +2,28 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_document_create = Event(
|
||||
name='documents_document_create', label=_('Document created')
|
||||
namespace = EventTypeNamespace(name='documents', label=_('Documents'))
|
||||
|
||||
event_document_create = namespace.add_event_type(
|
||||
name='document_create', label=_('Document created')
|
||||
)
|
||||
event_document_download = Event(
|
||||
name='documents_document_download',
|
||||
label=_('Document downloaded')
|
||||
event_document_download = namespace.add_event_type(
|
||||
name='document_download', label=_('Document downloaded')
|
||||
)
|
||||
event_document_properties_edit = Event(
|
||||
name='documents_document_edit', label=_('Document properties edited')
|
||||
event_document_properties_edit = namespace.add_event_type(
|
||||
name='document_edit', label=_('Document properties edited')
|
||||
)
|
||||
event_document_type_change = Event(
|
||||
name='documents_document_type_change', label=_('Document type changed')
|
||||
event_document_type_change = namespace.add_event_type(
|
||||
name='document_type_change', label=_('Document type changed')
|
||||
)
|
||||
event_document_new_version = Event(
|
||||
name='documents_document_new_version', label=_('New version uploaded')
|
||||
event_document_new_version = namespace.add_event_type(
|
||||
name='document_new_version', label=_('New version uploaded')
|
||||
)
|
||||
event_document_version_revert = Event(
|
||||
name='documents_document_version_revert',
|
||||
label=_('Document version reverted')
|
||||
event_document_version_revert = namespace.add_event_type(
|
||||
name='document_version_revert', label=_('Document version reverted')
|
||||
)
|
||||
event_document_view = Event(
|
||||
name='documents_document_view',
|
||||
label=_('Document viewed')
|
||||
event_document_view = namespace.add_event_type(
|
||||
name='document_view', label=_('Document viewed')
|
||||
)
|
||||
|
||||
@@ -222,9 +222,13 @@ class Document(models.Model):
|
||||
if new_document:
|
||||
if user:
|
||||
self.add_as_recent_document_for_user(user)
|
||||
event_document_create.commit(actor=user, target=self)
|
||||
event_document_create.commit(
|
||||
actor=user, target=self, action_object=self.document_type
|
||||
)
|
||||
else:
|
||||
event_document_create.commit(target=self)
|
||||
event_document_create.commit(
|
||||
target=self, action_object=self.document_type
|
||||
)
|
||||
else:
|
||||
if _commit_events:
|
||||
event_document_properties_edit.commit(actor=user, target=self)
|
||||
|
||||
@@ -64,7 +64,7 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase):
|
||||
|
||||
event = Action.objects.any(obj=self.document).first()
|
||||
|
||||
self.assertEqual(event.verb, event_document_download.name)
|
||||
self.assertEqual(event.verb, event_document_download.id)
|
||||
self.assertEqual(event.target, self.document)
|
||||
self.assertEqual(event.actor, self.user)
|
||||
|
||||
@@ -98,6 +98,6 @@ class DocumentEventsTestCase(GenericDocumentViewTestCase):
|
||||
|
||||
event = Action.objects.any(obj=self.document).first()
|
||||
|
||||
self.assertEqual(event.verb, event_document_view.name)
|
||||
self.assertEqual(event.verb, event_document_view.id)
|
||||
self.assertEqual(event.target, self.document)
|
||||
self.assertEqual(event.actor, self.user)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .classes import Event # NOQA
|
||||
from .classes import EventTypeNamespace, ModelEventType # NOQA
|
||||
|
||||
default_app_config = 'events.apps.EventsApp'
|
||||
|
||||
@@ -2,9 +2,19 @@ from __future__ import unicode_literals
|
||||
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import EventType
|
||||
from .models import EventSubscription, Notification, StoredEventType
|
||||
|
||||
|
||||
@admin.register(EventType)
|
||||
class EventTypeAdmin(admin.ModelAdmin):
|
||||
@admin.register(EventSubscription)
|
||||
class EventSubscriptionAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'stored_event_type')
|
||||
|
||||
|
||||
@admin.register(StoredEventType)
|
||||
class StoredEventTypeAdmin(admin.ModelAdmin):
|
||||
readonly_fields = ('name', '__str__')
|
||||
|
||||
|
||||
@admin.register(Notification)
|
||||
class NotificationAdmin(admin.ModelAdmin):
|
||||
list_display = ('user', 'action', 'read')
|
||||
|
||||
@@ -10,9 +10,13 @@ from rest_framework import generics
|
||||
from acls.models import AccessControlList
|
||||
from rest_api.permissions import MayanPermission
|
||||
|
||||
from .classes import Event
|
||||
from .classes import EventType, EventTypeNamespace
|
||||
from .models import Notification
|
||||
from .permissions import permission_events_view
|
||||
from .serializers import EventSerializer, EventTypeSerializer
|
||||
from .serializers import (
|
||||
EventSerializer, EventTypeSerializer, EventTypeNamespaceSerializer,
|
||||
NotificationSerializer
|
||||
)
|
||||
|
||||
|
||||
class APIObjectEventListView(generics.ListAPIView):
|
||||
@@ -46,13 +50,72 @@ class APIObjectEventListView(generics.ListAPIView):
|
||||
return any_stream(obj)
|
||||
|
||||
|
||||
class APIEventTypeNamespaceDetailView(generics.RetrieveAPIView):
|
||||
"""
|
||||
Returns the details of an event type namespace.
|
||||
"""
|
||||
serializer_class = EventTypeNamespaceSerializer
|
||||
|
||||
def get_object(self):
|
||||
try:
|
||||
return EventTypeNamespace.get(name=self.kwargs['name'])
|
||||
except KeyError:
|
||||
raise Http404
|
||||
|
||||
|
||||
class APIEventTypeNamespaceListView(generics.ListAPIView):
|
||||
"""
|
||||
Returns a list of all the available event type namespaces.
|
||||
"""
|
||||
|
||||
serializer_class = EventTypeNamespaceSerializer
|
||||
queryset = EventTypeNamespace.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {
|
||||
'format': self.format_kwarg,
|
||||
'request': self.request,
|
||||
'view': self
|
||||
}
|
||||
|
||||
|
||||
class APIEventTypeNamespaceEventTypeListView(generics.ListAPIView):
|
||||
"""
|
||||
Returns a list of all the available event types from a namespaces.
|
||||
"""
|
||||
|
||||
serializer_class = EventTypeSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
try:
|
||||
return EventTypeNamespace.get(
|
||||
name=self.kwargs['name']
|
||||
).get_event_types()
|
||||
except KeyError:
|
||||
raise Http404
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {
|
||||
'format': self.format_kwarg,
|
||||
'request': self.request,
|
||||
'view': self
|
||||
}
|
||||
|
||||
|
||||
class APIEventTypeListView(generics.ListAPIView):
|
||||
"""
|
||||
Returns a list of all the available event types.
|
||||
"""
|
||||
|
||||
serializer_class = EventTypeSerializer
|
||||
queryset = sorted(Event.all(), key=lambda event: event.name)
|
||||
queryset = EventType.all()
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {
|
||||
'format': self.format_kwarg,
|
||||
'request': self.request,
|
||||
'view': self
|
||||
}
|
||||
|
||||
|
||||
class APIEventListView(generics.ListAPIView):
|
||||
@@ -64,3 +127,20 @@ class APIEventListView(generics.ListAPIView):
|
||||
permission_classes = (MayanPermission,)
|
||||
queryset = Action.objects.all()
|
||||
serializer_class = EventSerializer
|
||||
|
||||
def get_serializer_context(self):
|
||||
return {
|
||||
'format': self.format_kwarg,
|
||||
'request': self.request,
|
||||
'view': self
|
||||
}
|
||||
|
||||
|
||||
class APINotificationListView(generics.ListAPIView):
|
||||
"""
|
||||
Return a list of notifications for the current user.
|
||||
"""
|
||||
serializer_class = NotificationSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
return Notification.objects.filter(user=self.request.user)
|
||||
|
||||
@@ -3,11 +3,19 @@ from __future__ import unicode_literals
|
||||
from django.apps import apps
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common import MayanAppConfig, menu_tools
|
||||
from common import (
|
||||
MayanAppConfig, menu_main, menu_object, menu_secondary, menu_tools,
|
||||
menu_user
|
||||
)
|
||||
from common.widgets import two_state_template
|
||||
from navigation import SourceColumn
|
||||
from rest_api.classes import APIEndPoint
|
||||
|
||||
from .links import link_events_list
|
||||
from .links import (
|
||||
link_events_list, link_event_types_subscriptions_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
|
||||
|
||||
@@ -28,6 +36,8 @@ class EventsApp(MayanAppConfig):
|
||||
def ready(self):
|
||||
super(EventsApp, self).ready()
|
||||
Action = apps.get_model(app_label='actstream', model_name='Action')
|
||||
Notification = self.get_model(model_name='Notification')
|
||||
StoredEventType = self.get_model(model_name='StoredEventType')
|
||||
|
||||
APIEndPoint(app=self, version_string='1')
|
||||
|
||||
@@ -39,7 +49,7 @@ class EventsApp(MayanAppConfig):
|
||||
func=lambda context: event_actor(context['object'])
|
||||
)
|
||||
SourceColumn(
|
||||
source=Action, label=_('Verb'),
|
||||
source=Action, label=_('Event'),
|
||||
func=lambda context: event_type_link(context['object'])
|
||||
)
|
||||
SourceColumn(
|
||||
@@ -49,4 +59,44 @@ class EventsApp(MayanAppConfig):
|
||||
)
|
||||
)
|
||||
|
||||
SourceColumn(
|
||||
source=StoredEventType, label=_('Namespace'), attribute='namespace'
|
||||
)
|
||||
SourceColumn(
|
||||
source=StoredEventType, label=_('Label'), attribute='label'
|
||||
)
|
||||
|
||||
SourceColumn(
|
||||
source=Notification, label=_('Timestamp'),
|
||||
attribute='action.timestamp'
|
||||
)
|
||||
SourceColumn(
|
||||
source=Notification, label=_('Actor'), attribute='action.actor'
|
||||
)
|
||||
SourceColumn(
|
||||
source=Notification, label=_('Event'),
|
||||
func=lambda context: event_type_link(context['object'].action)
|
||||
)
|
||||
SourceColumn(
|
||||
source=Notification, label=_('Target'),
|
||||
func=lambda context: event_object_link(context['object'].action)
|
||||
)
|
||||
SourceColumn(
|
||||
source=Notification, label=_('Seen'),
|
||||
func=lambda context: two_state_template(
|
||||
state=context['object'].read
|
||||
)
|
||||
)
|
||||
|
||||
menu_main.bind_links(
|
||||
links=(link_user_notifications_list,), position=99
|
||||
)
|
||||
menu_object.bind_links(
|
||||
links=(link_notification_mark_read,), sources=(Notification,)
|
||||
)
|
||||
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,))
|
||||
|
||||
@@ -1,70 +1,231 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.apps import apps
|
||||
from django.utils.encoding import force_text
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from actstream import action
|
||||
|
||||
from .permissions import permission_events_view
|
||||
|
||||
class Event(object):
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventTypeNamespace(object):
|
||||
_registry = {}
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return Event.sort(event_type_list=cls._registry.values())
|
||||
return sorted(cls._registry.values())
|
||||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
return cls._registry[name]
|
||||
|
||||
def __init__(self, name, label):
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.event_types = []
|
||||
self.__class__._registry[name] = self
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.label)
|
||||
|
||||
def add_event_type(self, name, label):
|
||||
event_type = EventType(namespace=self, name=name, label=label)
|
||||
self.event_types.append(event_type)
|
||||
return event_type
|
||||
|
||||
def get_event_types(self):
|
||||
return EventType.sort(event_type_list=self.event_types)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventType(object):
|
||||
_registry = {}
|
||||
|
||||
@staticmethod
|
||||
def sort(event_type_list):
|
||||
return sorted(
|
||||
event_type_list, key=lambda x: (x.namespace.label, x.label)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
# Return sorted permisions by namespace.name
|
||||
return EventType.sort(event_type_list=cls._registry.values())
|
||||
|
||||
@classmethod
|
||||
def get(cls, name):
|
||||
try:
|
||||
return cls._registry[name]
|
||||
except KeyError:
|
||||
raise KeyError(
|
||||
_('Unknown or obsolete event type: {0}'.format(name))
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_label(cls, name):
|
||||
try:
|
||||
return cls.get(name=name).label
|
||||
except KeyError as exception:
|
||||
return force_text(exception)
|
||||
return _('Unknown or obsolete event type: %s') % name
|
||||
|
||||
@classmethod
|
||||
def refresh(cls):
|
||||
for event_type in cls.all():
|
||||
event_type.get_type()
|
||||
event_type.get_stored_event_type()
|
||||
|
||||
@staticmethod
|
||||
def sort(event_type_list):
|
||||
return sorted(
|
||||
event_type_list, key=lambda x: x.label
|
||||
)
|
||||
|
||||
def __init__(self, name, label):
|
||||
def __init__(self, namespace, name, label):
|
||||
self.namespace = namespace
|
||||
self.name = name
|
||||
self.label = label
|
||||
self.event_type = None
|
||||
self.__class__._registry[name] = self
|
||||
self.stored_event_type = None
|
||||
self.__class__._registry[self.id] = self
|
||||
|
||||
def get_type(self):
|
||||
if not self.event_type:
|
||||
EventType = apps.get_model('events', 'EventType')
|
||||
|
||||
self.event_type, created = EventType.objects.get_or_create(
|
||||
name=self.name
|
||||
)
|
||||
|
||||
return self.event_type
|
||||
def __str__(self):
|
||||
return force_text('{}: {}'.format(self.namespace.label, self.label))
|
||||
|
||||
def commit(self, actor=None, action_object=None, target=None):
|
||||
if not self.event_type:
|
||||
EventType = apps.get_model('events', 'EventType')
|
||||
self.event_type, created = EventType.objects.get_or_create(
|
||||
name=self.name
|
||||
)
|
||||
AccessControlList = apps.get_model(
|
||||
app_label='acls', model_name='AccessControlList'
|
||||
)
|
||||
Action = apps.get_model(
|
||||
app_label='actstream', model_name='Action'
|
||||
)
|
||||
ContentType = apps.get_model(
|
||||
app_label='contenttypes', model_name='ContentType'
|
||||
)
|
||||
Notification = apps.get_model(
|
||||
app_label='events', model_name='Notification'
|
||||
)
|
||||
|
||||
action.send(
|
||||
actor or target, actor=actor, verb=self.name,
|
||||
results = action.send(
|
||||
actor or target, actor=actor, verb=self.id,
|
||||
action_object=action_object, target=target
|
||||
)
|
||||
|
||||
for handler, result in results:
|
||||
if isinstance(result, Action):
|
||||
for user in get_user_model().objects.all():
|
||||
notification = None
|
||||
|
||||
if user.event_subscriptions.filter(stored_event_type__name=result.verb).exists():
|
||||
if result.target:
|
||||
try:
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_events_view,
|
||||
user=user, obj=result.target
|
||||
)
|
||||
except PermissionDenied:
|
||||
pass
|
||||
else:
|
||||
notification = Notification.objects.create(action=result, user=user)
|
||||
else:
|
||||
notification = Notification.objects.create(action=result, user=user)
|
||||
|
||||
if result.target:
|
||||
content_type = ContentType.objects.get_for_model(model=result.target)
|
||||
|
||||
relationship = user.object_subscriptions.filter(
|
||||
content_type=content_type,
|
||||
object_id=result.target.pk,
|
||||
stored_event_type__name=result.verb
|
||||
)
|
||||
|
||||
if relationship.exists():
|
||||
try:
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_events_view,
|
||||
user=user, obj=result.target
|
||||
)
|
||||
except PermissionDenied:
|
||||
pass
|
||||
else:
|
||||
notification = Notification.objects.create(action=result, user=user)
|
||||
if not notification and result.action_object:
|
||||
content_type = ContentType.objects.get_for_model(model=result.action_object)
|
||||
|
||||
relationship = user.object_subscriptions.filter(
|
||||
content_type=content_type,
|
||||
object_id=result.action_object.pk,
|
||||
stored_event_type__name=result.verb
|
||||
)
|
||||
|
||||
if relationship.exists():
|
||||
try:
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_events_view,
|
||||
user=user, obj=result.action_object
|
||||
)
|
||||
except PermissionDenied:
|
||||
pass
|
||||
else:
|
||||
notification = Notification.objects.create(action=result, user=user)
|
||||
|
||||
def get_stored_event_type(self):
|
||||
if not self.stored_event_type:
|
||||
StoredEventType = apps.get_model('events', 'StoredEventType')
|
||||
|
||||
self.stored_event_type, created = StoredEventType.objects.get_or_create(
|
||||
name=self.id
|
||||
)
|
||||
|
||||
return self.stored_event_type
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return '%s.%s' % (self.namespace.name, self.name)
|
||||
|
||||
|
||||
class ModelEventType(object):
|
||||
"""
|
||||
Class to allow matching a model to a specific set of events.
|
||||
"""
|
||||
_inheritances = {}
|
||||
_proxies = {}
|
||||
_registry = {}
|
||||
|
||||
@classmethod
|
||||
def get_for_class(cls, klass):
|
||||
return cls._registry.get(klass, ())
|
||||
|
||||
@classmethod
|
||||
def get_for_instance(cls, instance):
|
||||
StoredEventType = apps.get_model(
|
||||
app_label='events', model_name='StoredEventType'
|
||||
)
|
||||
|
||||
events = []
|
||||
|
||||
class_events = cls._registry.get(type(instance))
|
||||
|
||||
if class_events:
|
||||
events.extend(class_events)
|
||||
|
||||
proxy = cls._proxies.get(type(instance))
|
||||
|
||||
if proxy:
|
||||
events.extend(cls._registry.get(proxy))
|
||||
|
||||
pks = [
|
||||
event.id for event in set(events)
|
||||
]
|
||||
|
||||
return EventType.sort(
|
||||
event_type_list=StoredEventType.objects.filter(name__in=pks)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get_inheritance(cls, model):
|
||||
return cls._inheritances[model]
|
||||
|
||||
@classmethod
|
||||
def register(cls, model, event_types):
|
||||
cls._registry.setdefault(model, [])
|
||||
for event_type in event_types:
|
||||
cls._registry[model].append(event_type)
|
||||
|
||||
@classmethod
|
||||
def register_inheritance(cls, model, related):
|
||||
cls._inheritances[model] = related
|
||||
|
||||
@classmethod
|
||||
def register_proxy(cls, source, model):
|
||||
cls._proxies[model] = source
|
||||
|
||||
122
mayan/apps/events/forms.py
Normal file
122
mayan/apps/events/forms.py
Normal file
@@ -0,0 +1,122 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django import forms
|
||||
from django.forms.formsets import formset_factory
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .models import EventSubscription, ObjectEventSubscription
|
||||
|
||||
|
||||
class EventTypeUserRelationshipForm(forms.Form):
|
||||
namespace = forms.CharField(
|
||||
label=_('Namespace'), required=False,
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'})
|
||||
)
|
||||
label = forms.CharField(
|
||||
label=_('Label'), required=False,
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'})
|
||||
)
|
||||
subscription = forms.ChoiceField(
|
||||
label=_('Subscription'),
|
||||
widget=forms.RadioSelect(), choices=(
|
||||
('none', _('No')),
|
||||
('subscribed', _('Subscribed')),
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EventTypeUserRelationshipForm, self).__init__(
|
||||
*args, **kwargs
|
||||
)
|
||||
|
||||
self.fields['namespace'].initial = self.initial['stored_event_type'].namespace
|
||||
self.fields['label'].initial = self.initial['stored_event_type'].label
|
||||
|
||||
subscription = EventSubscription.objects.get_for(
|
||||
stored_event_type=self.initial['stored_event_type'],
|
||||
user=self.initial['user'],
|
||||
)
|
||||
|
||||
if subscription.exists():
|
||||
self.fields['subscription'].initial = 'subscribed'
|
||||
else:
|
||||
self.fields['subscription'].initial = 'none'
|
||||
|
||||
def save(self):
|
||||
subscription = EventSubscription.objects.get_for(
|
||||
stored_event_type=self.initial['stored_event_type'],
|
||||
user=self.initial['user'],
|
||||
)
|
||||
|
||||
if self.cleaned_data['subscription'] == 'none':
|
||||
subscription.delete()
|
||||
elif self.cleaned_data['subscription'] == 'subscribed':
|
||||
if not subscription.exists():
|
||||
EventSubscription.objects.create_for(
|
||||
stored_event_type=self.initial['stored_event_type'],
|
||||
user=self.initial['user']
|
||||
)
|
||||
|
||||
|
||||
EventTypeUserRelationshipFormSet = formset_factory(
|
||||
EventTypeUserRelationshipForm, extra=0
|
||||
)
|
||||
|
||||
|
||||
class ObjectEventTypeUserRelationshipForm(forms.Form):
|
||||
namespace = forms.CharField(
|
||||
label=_('Namespace'), required=False,
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'})
|
||||
)
|
||||
label = forms.CharField(
|
||||
label=_('Label'), required=False,
|
||||
widget=forms.TextInput(attrs={'readonly': 'readonly'})
|
||||
)
|
||||
subscription = forms.ChoiceField(
|
||||
label=_('Subscription'),
|
||||
widget=forms.RadioSelect(), choices=(
|
||||
('none', _('No')),
|
||||
('subscribed', _('Subscribed')),
|
||||
)
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ObjectEventTypeUserRelationshipForm, self).__init__(
|
||||
*args, **kwargs
|
||||
)
|
||||
|
||||
self.fields['namespace'].initial = self.initial['stored_event_type'].namespace
|
||||
self.fields['label'].initial = self.initial['stored_event_type'].label
|
||||
|
||||
subscription = ObjectEventSubscription.objects.get_for(
|
||||
obj=self.initial['object'],
|
||||
stored_event_type=self.initial['stored_event_type'],
|
||||
user=self.initial['user'],
|
||||
)
|
||||
|
||||
if subscription.exists():
|
||||
self.fields['subscription'].initial = 'subscribed'
|
||||
else:
|
||||
self.fields['subscription'].initial = 'none'
|
||||
|
||||
def save(self):
|
||||
subscription = ObjectEventSubscription.objects.get_for(
|
||||
obj=self.initial['object'],
|
||||
stored_event_type=self.initial['stored_event_type'],
|
||||
user=self.initial['user'],
|
||||
)
|
||||
|
||||
if self.cleaned_data['subscription'] == 'none':
|
||||
subscription.delete()
|
||||
elif self.cleaned_data['subscription'] == 'subscribed':
|
||||
if not subscription.exists():
|
||||
ObjectEventSubscription.objects.create_for(
|
||||
obj=self.initial['object'],
|
||||
stored_event_type=self.initial['stored_event_type'],
|
||||
user=self.initial['user']
|
||||
)
|
||||
|
||||
|
||||
ObjectEventTypeUserRelationshipFormSet = formset_factory(
|
||||
ObjectEventTypeUserRelationshipForm, extra=0
|
||||
)
|
||||
@@ -26,12 +26,44 @@ def get_kwargs_factory(variable_name):
|
||||
return get_kwargs
|
||||
|
||||
|
||||
def get_notification_count(context):
|
||||
return context['request'].user.notifications.filter(read=False).count()
|
||||
|
||||
|
||||
link_events_list = Link(
|
||||
icon='fa fa-list-ol', permissions=(permission_events_view,),
|
||||
text=_('Events'), view='events:events_list'
|
||||
)
|
||||
link_events_details = Link(
|
||||
text=_('Events'), view='events:events_list'
|
||||
)
|
||||
link_events_for_object = Link(
|
||||
icon='fa fa-list-ol', permissions=(permission_events_view,),
|
||||
text=_('Events'), view='events:events_for_object',
|
||||
kwargs=get_kwargs_factory('resolved_object')
|
||||
)
|
||||
link_event_types_subscriptions_list = Link(
|
||||
icon='fa fa-list-ol', text=_('Event subscriptions'),
|
||||
view='events:event_types_user_subcriptions_list'
|
||||
)
|
||||
link_notification_mark_read = Link(
|
||||
args='object.pk', text=_('Mark as seen'),
|
||||
view='events:notification_mark_read'
|
||||
)
|
||||
link_notification_mark_read_all = Link(
|
||||
text=_('Mark all as seen'), view='events:notification_mark_read_all'
|
||||
)
|
||||
link_object_event_types_user_subcriptions_list = Link(
|
||||
kwargs=get_kwargs_factory('resolved_object'),
|
||||
permissions=(permission_events_view,), text=_('Subscriptions'),
|
||||
view='events:object_event_types_user_subcriptions_list',
|
||||
)
|
||||
link_object_event_types_user_subcriptions_list_with_icon = Link(
|
||||
kwargs=get_kwargs_factory('resolved_object'), icon='fa fa-rss',
|
||||
permissions=(permission_events_view,), text=_('Subscriptions'),
|
||||
view='events:object_event_types_user_subcriptions_list',
|
||||
)
|
||||
link_user_notifications_list = Link(
|
||||
icon='fa fa-bell', text=get_notification_count,
|
||||
view='events:user_notifications_list'
|
||||
)
|
||||
|
||||
34
mayan/apps/events/managers.py
Normal file
34
mayan/apps/events/managers.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
|
||||
|
||||
class EventSubscriptionManager(models.Manager):
|
||||
def create_for(self, stored_event_type, user):
|
||||
return self.create(
|
||||
stored_event_type=stored_event_type, user=user
|
||||
)
|
||||
|
||||
def get_for(self, stored_event_type, user):
|
||||
return self.filter(
|
||||
stored_event_type=stored_event_type, user=user
|
||||
)
|
||||
|
||||
|
||||
class ObjectEventSubscriptionManager(models.Manager):
|
||||
def create_for(self, obj, stored_event_type, user):
|
||||
content_type = ContentType.objects.get_for_model(model=obj)
|
||||
|
||||
return self.create(
|
||||
content_type=content_type, object_id=obj.pk,
|
||||
stored_event_type=stored_event_type, user=user
|
||||
)
|
||||
|
||||
def get_for(self, obj, stored_event_type, user):
|
||||
content_type = ContentType.objects.get_for_model(model=obj)
|
||||
|
||||
return self.filter(
|
||||
content_type=content_type, object_id=obj.pk,
|
||||
stored_event_type=stored_event_type, user=user
|
||||
)
|
||||
30
mayan/apps/events/migrations/0002_eventsubscription.py
Normal file
30
mayan/apps/events/migrations/0002_eventsubscription.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-07-29 07:04
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('events', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='EventSubscription',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('event_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='events.EventType', verbose_name='Event type')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Event subscription',
|
||||
'verbose_name_plural': 'Event subscriptions',
|
||||
},
|
||||
),
|
||||
]
|
||||
32
mayan/apps/events/migrations/0003_notification.py
Normal file
32
mayan/apps/events/migrations/0003_notification.py
Normal file
@@ -0,0 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-07-29 07:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('actstream', '0002_remove_action_data'),
|
||||
('events', '0002_eventsubscription'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Notification',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('read', models.BooleanField(default=False, verbose_name='Read')),
|
||||
('action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to='actstream.Action', verbose_name='Action')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Notification',
|
||||
'verbose_name_plural': 'Notifications',
|
||||
},
|
||||
),
|
||||
]
|
||||
40
mayan/apps/events/migrations/0004_auto_20170731_0423.py
Normal file
40
mayan/apps/events/migrations/0004_auto_20170731_0423.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-07-31 04:23
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0003_notification'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='EventType',
|
||||
new_name='StoredEventType',
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='storedeventtype',
|
||||
options={'verbose_name': 'Stored event type', 'verbose_name_plural': 'Stored event types'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='eventsubscription',
|
||||
name='event_type',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='eventsubscription',
|
||||
name='stored_event_type',
|
||||
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='event_subscriptions', to='events.StoredEventType', verbose_name='Event type'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='eventsubscription',
|
||||
name='user',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='event_subscriptions', to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
||||
86
mayan/apps/events/migrations/0005_auto_20170731_0452.py
Normal file
86
mayan/apps/events/migrations/0005_auto_20170731_0452.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-07-31 04:52
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def update_event_types_names(apps, schema_editor):
|
||||
Action = apps.get_model('actstream', 'Action')
|
||||
StoredEventType = apps.get_model('events', 'StoredEventType')
|
||||
|
||||
known_namespaces = {
|
||||
'documents_': 'documents.',
|
||||
'checkouts_': 'checkouts.',
|
||||
'document_comment_': 'document_comments.',
|
||||
'parsing_document_': 'document_parsing.',
|
||||
'ocr_': 'ocr.',
|
||||
'tag_': 'tags.',
|
||||
}
|
||||
|
||||
pattern = re.compile('|'.join(known_namespaces.keys()))
|
||||
|
||||
for event_type in StoredEventType.objects.all():
|
||||
event_type.name = pattern.sub(
|
||||
lambda x: known_namespaces[x.group()], event_type.name
|
||||
)
|
||||
event_type.save()
|
||||
|
||||
for action in Action.objects.all():
|
||||
action.verb = pattern.sub(
|
||||
lambda x: known_namespaces[x.group()], action.verb
|
||||
)
|
||||
action.save()
|
||||
|
||||
|
||||
def revert_event_types_names(apps, schema_editor):
|
||||
Action = apps.get_model('actstream', 'Action')
|
||||
StoredEventType = apps.get_model('events', 'StoredEventType')
|
||||
|
||||
known_namespaces = {
|
||||
'documents\.': 'documents_',
|
||||
'checkouts\.': 'checkouts_',
|
||||
'document_comments\.': 'document_comment_',
|
||||
'document_parsing\.': 'parsing_document_',
|
||||
'ocr\.': 'ocr_',
|
||||
'tags\.': 'tag_',
|
||||
}
|
||||
|
||||
pattern = re.compile('|'.join(known_namespaces.keys()))
|
||||
|
||||
for event_type in StoredEventType.objects.all():
|
||||
old_name = event_type.name
|
||||
new_name = pattern.sub(
|
||||
lambda x: known_namespaces[x.group().replace('.', '\\.')],
|
||||
event_type.name
|
||||
)
|
||||
event_type.name = new_name
|
||||
if old_name == new_name:
|
||||
event_type.delete()
|
||||
else:
|
||||
event_type.save()
|
||||
|
||||
for action in Action.objects.all():
|
||||
new_name = pattern.sub(
|
||||
lambda x: known_namespaces[x.group().replace('.', '\\.')],
|
||||
action.verb
|
||||
)
|
||||
action.verb = new_name
|
||||
action.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0004_auto_20170731_0423'),
|
||||
('actstream', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
code=update_event_types_names,
|
||||
reverse_code=revert_event_types_names
|
||||
),
|
||||
]
|
||||
33
mayan/apps/events/migrations/0006_objecteventsubscription.py
Normal file
33
mayan/apps/events/migrations/0006_objecteventsubscription.py
Normal file
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.10.7 on 2017-07-31 06:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('events', '0005_auto_20170731_0452'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ObjectEventSubscription',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('object_id', models.PositiveIntegerField()),
|
||||
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
|
||||
('stored_event_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='object_subscriptions', to='events.StoredEventType', verbose_name='Event type')),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='object_subscriptions', to=settings.AUTH_USER_MODEL, verbose_name='User')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Object event subscription',
|
||||
'verbose_name_plural': 'Object event subscriptions',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,28 +1,111 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.encoding import force_text, python_2_unicode_compatible
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from .classes import Event
|
||||
from actstream.models import Action
|
||||
|
||||
from .classes import EventType
|
||||
from .managers import (
|
||||
EventSubscriptionManager, ObjectEventSubscriptionManager
|
||||
)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventType(models.Model):
|
||||
class StoredEventType(models.Model):
|
||||
name = models.CharField(
|
||||
max_length=64, unique=True, verbose_name=_('Name')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Event type')
|
||||
verbose_name_plural = _('Event types')
|
||||
verbose_name = _('Stored event type')
|
||||
verbose_name_plural = _('Stored event types')
|
||||
|
||||
def __str__(self):
|
||||
return self.get_class().label
|
||||
return force_text(self.get_class())
|
||||
|
||||
def get_class(self):
|
||||
return Event.get(name=self.name)
|
||||
return EventType.get(name=self.name)
|
||||
|
||||
@property
|
||||
def label(self):
|
||||
return self.get_class().label
|
||||
|
||||
@property
|
||||
def namespace(self):
|
||||
return self.get_class().namespace
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class EventSubscription(models.Model):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE,
|
||||
related_name='event_subscriptions', verbose_name=_('User')
|
||||
)
|
||||
stored_event_type = models.ForeignKey(
|
||||
StoredEventType, on_delete=models.CASCADE,
|
||||
related_name='event_subscriptions', verbose_name=_('Event type')
|
||||
)
|
||||
|
||||
objects = EventSubscriptionManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Event subscription')
|
||||
verbose_name_plural = _('Event subscriptions')
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.stored_event_type)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Notification(models.Model):
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE,
|
||||
related_name='notifications', verbose_name=_('User')
|
||||
)
|
||||
action = models.ForeignKey(
|
||||
Action, on_delete=models.CASCADE, related_name='notifications',
|
||||
verbose_name=_('Action')
|
||||
)
|
||||
read = models.BooleanField(default=False, verbose_name=_('Read'))
|
||||
|
||||
class Meta:
|
||||
ordering = ('-action__timestamp',)
|
||||
verbose_name = _('Notification')
|
||||
verbose_name_plural = _('Notifications')
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.action)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class ObjectEventSubscription(models.Model):
|
||||
content_type = models.ForeignKey(
|
||||
ContentType, on_delete=models.CASCADE,
|
||||
)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey(
|
||||
ct_field='content_type',
|
||||
fk_field='object_id',
|
||||
)
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE,
|
||||
related_name='object_subscriptions', verbose_name=_('User')
|
||||
)
|
||||
stored_event_type = models.ForeignKey(
|
||||
StoredEventType, on_delete=models.CASCADE,
|
||||
related_name='object_subscriptions', verbose_name=_('Event type')
|
||||
)
|
||||
|
||||
objects = ObjectEventSubscriptionManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Object event subscription')
|
||||
verbose_name_plural = _('Object event subscriptions')
|
||||
|
||||
def __str__(self):
|
||||
return force_text(self.stored_event_type)
|
||||
|
||||
@@ -4,30 +4,59 @@ from django.utils.six import string_types
|
||||
|
||||
from actstream.models import Action
|
||||
from rest_framework import serializers
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from common.serializers import ContentTypeSerializer
|
||||
from rest_api.fields import DynamicSerializerField
|
||||
from user_management.serializers import UserSerializer
|
||||
|
||||
from .classes import Event
|
||||
from .models import EventType
|
||||
from .classes import EventType
|
||||
from .models import Notification, StoredEventType
|
||||
|
||||
|
||||
class EventTypeNamespaceSerializer(serializers.Serializer):
|
||||
label = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
url = serializers.SerializerMethodField()
|
||||
|
||||
event_types_url = serializers.HyperlinkedIdentityField(
|
||||
lookup_field='name',
|
||||
view_name='rest_api:event-type-namespace-event-type-list',
|
||||
)
|
||||
|
||||
def get_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:event-type-namespace-detail', args=(
|
||||
instance.name,
|
||||
), request=self.context['request'], format=self.context['format']
|
||||
)
|
||||
|
||||
|
||||
class EventTypeSerializer(serializers.Serializer):
|
||||
label = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
id = serializers.CharField()
|
||||
event_type_namespace_url = serializers.SerializerMethodField()
|
||||
|
||||
def get_event_type_namespace_url(self, instance):
|
||||
return reverse(
|
||||
'rest_api:event-type-namespace-detail', args=(
|
||||
instance.namespace.name,
|
||||
), request=self.context['request'], format=self.context['format']
|
||||
)
|
||||
|
||||
def to_representation(self, instance):
|
||||
if isinstance(instance, Event):
|
||||
if isinstance(instance, EventType):
|
||||
return super(EventTypeSerializer, self).to_representation(
|
||||
instance
|
||||
)
|
||||
elif isinstance(instance, EventType):
|
||||
elif isinstance(instance, StoredEventType):
|
||||
return super(EventTypeSerializer, self).to_representation(
|
||||
instance.get_class()
|
||||
)
|
||||
elif isinstance(instance, string_types):
|
||||
return super(EventTypeSerializer, self).to_representation(
|
||||
Event.get(name=instance)
|
||||
EventType.get(name=instance)
|
||||
)
|
||||
|
||||
|
||||
@@ -43,3 +72,12 @@ class EventSerializer(serializers.ModelSerializer):
|
||||
'action_object_content_type', 'action_object_object_id'
|
||||
)
|
||||
model = Action
|
||||
|
||||
|
||||
class NotificationSerializer(serializers.ModelSerializer):
|
||||
user = UserSerializer(read_only=True)
|
||||
action = EventSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
fields = ('action', 'read', 'user')
|
||||
model = Notification
|
||||
|
||||
@@ -3,9 +3,15 @@ from __future__ import unicode_literals
|
||||
from django.conf.urls import url
|
||||
|
||||
from .api_views import (
|
||||
APIEventListView, APIEventTypeListView, APIObjectEventListView
|
||||
APIEventListView, APIEventTypeListView, APIEventTypeNamespaceDetailView,
|
||||
APIEventTypeNamespaceEventTypeListView, APIEventTypeNamespaceListView,
|
||||
APINotificationListView, APIObjectEventListView
|
||||
)
|
||||
from .views import (
|
||||
EventListView, EventTypeSubscriptionListView, NotificationListView,
|
||||
NotificationMarkRead, NotificationMarkReadAll, ObjectEventListView,
|
||||
ObjectEventTypeSubscriptionListView, VerbEventListView
|
||||
)
|
||||
from .views import EventListView, ObjectEventListView, VerbEventListView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^all/$', EventListView.as_view(), name='events_list'),
|
||||
@@ -14,16 +20,60 @@ urlpatterns = [
|
||||
ObjectEventListView.as_view(), name='events_for_object'
|
||||
),
|
||||
url(
|
||||
r'^by_verb/(?P<verb>[\w\-]+)/$', VerbEventListView.as_view(),
|
||||
r'^by_verb/(?P<verb>[\w\-\.]+)/$', VerbEventListView.as_view(),
|
||||
name='events_by_verb'
|
||||
),
|
||||
url(
|
||||
r'^notifications/(?P<pk>\d+)/mark_read/$',
|
||||
NotificationMarkRead.as_view(), name='notification_mark_read'
|
||||
),
|
||||
url(
|
||||
r'^notifications/all/mark_read/$',
|
||||
NotificationMarkReadAll.as_view(), name='notification_mark_read_all'
|
||||
),
|
||||
url(
|
||||
r'^user/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/subscriptions/$',
|
||||
ObjectEventTypeSubscriptionListView.as_view(),
|
||||
name='object_event_types_user_subcriptions_list'
|
||||
),
|
||||
url(
|
||||
r'^user/event_types/subscriptions/$',
|
||||
EventTypeSubscriptionListView.as_view(),
|
||||
name='event_types_user_subcriptions_list'
|
||||
),
|
||||
url(
|
||||
r'^user/notifications/$',
|
||||
NotificationListView.as_view(),
|
||||
name='user_notifications_list'
|
||||
),
|
||||
]
|
||||
|
||||
api_urls = [
|
||||
url(r'^types/$', APIEventTypeListView.as_view(), name='event-type-list'),
|
||||
url(
|
||||
r'^event_type_namespaces/(?P<name>[-\w]+)/$',
|
||||
APIEventTypeNamespaceDetailView.as_view(),
|
||||
name='event-type-namespace-detail'
|
||||
),
|
||||
url(
|
||||
r'^event_type_namespaces/(?P<name>[-\w]+)/event_types/$',
|
||||
APIEventTypeNamespaceEventTypeListView.as_view(),
|
||||
name='event-type-namespace-event-type-list'
|
||||
),
|
||||
url(
|
||||
r'^event_type_namespaces/$', APIEventTypeNamespaceListView.as_view(),
|
||||
name='event-type-namespace-list'
|
||||
),
|
||||
url(
|
||||
r'^event_types/$', APIEventTypeListView.as_view(),
|
||||
name='event-type-list'
|
||||
),
|
||||
url(r'^events/$', APIEventListView.as_view(), name='event-list'),
|
||||
url(
|
||||
r'^object/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/events/$',
|
||||
r'^notifications/$', APINotificationListView.as_view(),
|
||||
name='notification-list'
|
||||
),
|
||||
url(
|
||||
r'^objects/(?P<app_label>[-\w]+)/(?P<model>[-\w]+)/(?P<object_id>\d+)/events/$',
|
||||
APIObjectEventListView.as_view(), name='object-event-list'
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.http import Http404
|
||||
from django.http import Http404, HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from actstream.models import Action, any_stream
|
||||
|
||||
from acls.models import AccessControlList
|
||||
from common.generics import FormView, SimpleView
|
||||
from common.utils import encapsulate
|
||||
from common.views import SingleObjectListView
|
||||
|
||||
from .classes import Event
|
||||
from .classes import EventType, ModelEventType
|
||||
from .forms import (
|
||||
EventTypeUserRelationshipFormSet, ObjectEventTypeUserRelationshipFormSet
|
||||
)
|
||||
from .models import StoredEventType
|
||||
from .permissions import permission_events_view
|
||||
from .widgets import event_object_link
|
||||
|
||||
@@ -37,6 +44,96 @@ class EventListView(SingleObjectListView):
|
||||
return Action.objects.all()
|
||||
|
||||
|
||||
class EventTypeSubscriptionListView(FormView):
|
||||
form_class = EventTypeUserRelationshipFormSet
|
||||
main_model = 'user'
|
||||
submodel = StoredEventType
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
EventType.refresh()
|
||||
return super(EventTypeSubscriptionListView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
try:
|
||||
for instance in form:
|
||||
instance.save()
|
||||
except Exception as exception:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('Error updating event subscription; %s') % exception
|
||||
)
|
||||
else:
|
||||
messages.success(
|
||||
self.request, _('Event subscriptions updated successfully')
|
||||
)
|
||||
|
||||
return super(
|
||||
EventTypeSubscriptionListView, self
|
||||
).form_valid(form=form)
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'form_display_mode_table': True,
|
||||
'object': self.get_object(),
|
||||
'title': _(
|
||||
'Event subscriptions'
|
||||
) % self.get_object()
|
||||
}
|
||||
|
||||
def get_initial(self):
|
||||
obj = self.get_object()
|
||||
initial = []
|
||||
|
||||
for element in self.get_queryset():
|
||||
initial.append({
|
||||
'user': obj,
|
||||
'main_model': self.main_model,
|
||||
'stored_event_type': element,
|
||||
})
|
||||
return initial
|
||||
|
||||
def get_queryset(self):
|
||||
# Return the queryset by name from the sorted list of the class
|
||||
event_type_ids = [event_type.id for event_type in EventType.all()]
|
||||
return self.submodel.objects.filter(name__in=event_type_ids)
|
||||
|
||||
def get_post_action_redirect(self):
|
||||
return reverse('common:current_user_details')
|
||||
|
||||
|
||||
class NotificationListView(SingleObjectListView):
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'hide_object': True,
|
||||
'object': self.request.user,
|
||||
'title': _('Notifications'),
|
||||
}
|
||||
|
||||
def get_object_list(self):
|
||||
return self.request.user.notifications.all()
|
||||
|
||||
|
||||
class NotificationMarkRead(SimpleView):
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.get_queryset().filter(pk=self.kwargs['pk']).update(read=True)
|
||||
return HttpResponseRedirect(reverse('events:user_notifications_list'))
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.user.notifications.all()
|
||||
|
||||
|
||||
class NotificationMarkReadAll(SimpleView):
|
||||
def dispatch(self, *args, **kwargs):
|
||||
self.get_queryset().update(read=True)
|
||||
return HttpResponseRedirect(reverse('events:user_notifications_list'))
|
||||
|
||||
def get_queryset(self):
|
||||
return self.request.user.notifications.all()
|
||||
|
||||
|
||||
class ObjectEventListView(EventListView):
|
||||
view_permissions = None
|
||||
|
||||
@@ -73,6 +170,76 @@ class ObjectEventListView(EventListView):
|
||||
return any_stream(self.content_object)
|
||||
|
||||
|
||||
class ObjectEventTypeSubscriptionListView(FormView):
|
||||
form_class = ObjectEventTypeUserRelationshipFormSet
|
||||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
EventType.refresh()
|
||||
return super(ObjectEventTypeSubscriptionListView, self).dispatch(*args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
try:
|
||||
for instance in form:
|
||||
instance.save()
|
||||
except Exception as exception:
|
||||
messages.error(
|
||||
self.request,
|
||||
_('Error updating object event subscription; %s') % exception
|
||||
)
|
||||
else:
|
||||
messages.success(
|
||||
self.request, _('Object event subscriptions updated successfully')
|
||||
)
|
||||
|
||||
return super(
|
||||
ObjectEventTypeSubscriptionListView, self
|
||||
).form_valid(form=form)
|
||||
|
||||
def get_object(self):
|
||||
object_content_type = get_object_or_404(
|
||||
ContentType, app_label=self.kwargs['app_label'],
|
||||
model=self.kwargs['model']
|
||||
)
|
||||
|
||||
try:
|
||||
content_object = object_content_type.get_object_for_this_type(
|
||||
pk=self.kwargs['object_id']
|
||||
)
|
||||
except object_content_type.model_class().DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
AccessControlList.objects.check_access(
|
||||
permissions=permission_events_view, user=self.request.user,
|
||||
obj=content_object
|
||||
)
|
||||
|
||||
return content_object
|
||||
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
'form_display_mode_table': True,
|
||||
'object': self.get_object(),
|
||||
'title': _(
|
||||
'Event subscriptions for: %s'
|
||||
) % self.get_object()
|
||||
}
|
||||
|
||||
def get_initial(self):
|
||||
obj = self.get_object()
|
||||
initial = []
|
||||
|
||||
for element in self.get_queryset():
|
||||
initial.append({
|
||||
'user': self.request.user,
|
||||
'object': obj,
|
||||
'stored_event_type': element,
|
||||
})
|
||||
return initial
|
||||
|
||||
def get_queryset(self):
|
||||
return ModelEventType.get_for_instance(instance=self.get_object())
|
||||
|
||||
|
||||
class VerbEventListView(SingleObjectListView):
|
||||
def get_extra_context(self):
|
||||
return {
|
||||
@@ -87,7 +254,7 @@ class VerbEventListView(SingleObjectListView):
|
||||
'hide_object': True,
|
||||
'title': _(
|
||||
'Events of type: %s'
|
||||
) % Event.get_label(self.kwargs['verb']),
|
||||
) % EventType.get(name=self.kwargs['verb']),
|
||||
}
|
||||
|
||||
def get_object_list(self):
|
||||
|
||||
@@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
||||
from django.urls import reverse
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .classes import Event
|
||||
from .classes import EventType
|
||||
|
||||
|
||||
def event_object_link(entry, attribute='target'):
|
||||
@@ -26,6 +26,6 @@ def event_type_link(entry):
|
||||
return mark_safe(
|
||||
'<a href="%(url)s">%(label)s</a>' % {
|
||||
'url': reverse('events:events_by_verb', kwargs={'verb': entry.verb}),
|
||||
'label': Event.get_label(entry.verb)
|
||||
'label': EventType.get(name=entry.verb)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,13 +2,15 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_ocr_document_version_submit = Event(
|
||||
name='ocr_document_version_submit',
|
||||
label=_('Document version submitted for OCR')
|
||||
namespace = EventTypeNamespace(name='ocr', label=_('OCR'))
|
||||
|
||||
event_ocr_document_version_submit = namespace.add_event_type(
|
||||
label=_('Document version submitted for OCR'),
|
||||
name='document_version_submit'
|
||||
)
|
||||
event_ocr_document_version_finish = Event(
|
||||
name='ocr_document_version_finish',
|
||||
label=_('Document version OCR finished')
|
||||
event_ocr_document_version_finish = namespace.add_event_type(
|
||||
label=_('Document version OCR finished'),
|
||||
name='document_version_finish'
|
||||
)
|
||||
|
||||
@@ -24,7 +24,10 @@ class DynamicSerializerField(serializers.ReadOnlyField):
|
||||
for klass, serializer_class in self.serializers.items():
|
||||
if isinstance(value, klass):
|
||||
return serializer_class(
|
||||
context={'request': self.context['request']}
|
||||
context={
|
||||
'format': self.context['format'],
|
||||
'request': self.context['request']
|
||||
}
|
||||
).to_representation(instance=value)
|
||||
|
||||
return _('Unable to find serializer class for: %s') % value
|
||||
|
||||
@@ -124,7 +124,7 @@ class Source(models.Model):
|
||||
logger.critical(
|
||||
'Unexpected exception while trying to create version for '
|
||||
'new document "%s" from source "%s"; %s',
|
||||
label or file_object.name, self, exception
|
||||
label or file_object.name, self, exception, exc_info=True
|
||||
)
|
||||
document.delete(to_trash=False)
|
||||
raise
|
||||
|
||||
@@ -2,13 +2,13 @@ from __future__ import absolute_import, unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from events.classes import Event
|
||||
from events import EventTypeNamespace
|
||||
|
||||
event_tag_attach = Event(
|
||||
name='tag_attach',
|
||||
label=_('Tag attached to document')
|
||||
namespace = EventTypeNamespace(name='tags', label=_('Tags'))
|
||||
|
||||
event_tag_attach = namespace.add_event_type(
|
||||
label=_('Tag attached to document'), name='attach'
|
||||
)
|
||||
event_tag_remove = Event(
|
||||
name='tag_remove',
|
||||
label=_('Tag removed from document')
|
||||
event_tag_remove = namespace.add_event_type(
|
||||
label=_('Tag removed from document'), name='remove'
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user