mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Notification fix (#3939)
* Fix docstring for NotificationMessage class * Fix for 'refresh' button in notification table * Simplify API for marking notifications as 'read' - Simply update the detail serializer - No requirement for extra API endpoints - Same updates for news feed entry - Hide 'read' news on the home page - Add ability to mark news items as read via table * Bug fix for build.js * Fix for part category template
This commit is contained in:
parent
54e7dd28e5
commit
95645c7b14
@ -19,8 +19,8 @@ import common.models
|
||||
import common.serializers
|
||||
from InvenTree.api import BulkDeleteMixin
|
||||
from InvenTree.helpers import inheritors
|
||||
from InvenTree.mixins import (CreateAPI, ListAPI, RetrieveAPI,
|
||||
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||
from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI,
|
||||
RetrieveUpdateDestroyAPI)
|
||||
from plugin.models import NotificationUserSetting
|
||||
from plugin.serializers import NotificationUserSettingSerializer
|
||||
|
||||
@ -319,36 +319,6 @@ class NotificationDetail(NotificationMessageMixin, RetrieveUpdateDestroyAPI):
|
||||
"""
|
||||
|
||||
|
||||
class NotificationReadEdit(NotificationMessageMixin, CreateAPI):
|
||||
"""General API endpoint to manipulate read state of a notification."""
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""Add instance to context so it can be accessed in the serializer."""
|
||||
context = super().get_serializer_context()
|
||||
if self.request:
|
||||
context['instance'] = self.get_object()
|
||||
return context
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Set the `read` status to the target value."""
|
||||
message = self.get_object()
|
||||
try:
|
||||
message.read = self.target
|
||||
message.save()
|
||||
except Exception as exc:
|
||||
raise serializers.ValidationError(detail=serializers.as_serializer_error(exc))
|
||||
|
||||
|
||||
class NotificationRead(NotificationReadEdit):
|
||||
"""API endpoint to mark a notification as read."""
|
||||
target = True
|
||||
|
||||
|
||||
class NotificationUnread(NotificationReadEdit):
|
||||
"""API endpoint to mark a notification as unread."""
|
||||
target = False
|
||||
|
||||
|
||||
class NotificationReadAll(NotificationMessageMixin, RetrieveAPI):
|
||||
"""API endpoint to mark all notifications as read."""
|
||||
|
||||
@ -390,11 +360,6 @@ class NewsFeedEntryDetail(NewsFeedMixin, RetrieveUpdateDestroyAPI):
|
||||
"""Detail view for an individual news feed object."""
|
||||
|
||||
|
||||
class NewsFeedEntryRead(NewsFeedMixin, NotificationReadEdit):
|
||||
"""API endpoint to mark a news item as read."""
|
||||
target = True
|
||||
|
||||
|
||||
settings_api_urls = [
|
||||
# User settings
|
||||
re_path(r'^user/', include([
|
||||
@ -432,8 +397,6 @@ common_api_urls = [
|
||||
re_path(r'^notifications/', include([
|
||||
# Individual purchase order detail URLs
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path(r'^read/', NotificationRead.as_view(), name='api-notifications-read'),
|
||||
re_path(r'^unread/', NotificationUnread.as_view(), name='api-notifications-unread'),
|
||||
re_path(r'.*$', NotificationDetail.as_view(), name='api-notifications-detail'),
|
||||
])),
|
||||
# Read all
|
||||
@ -446,7 +409,6 @@ common_api_urls = [
|
||||
# News
|
||||
re_path(r'^news/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
re_path(r'^read/', NewsFeedEntryRead.as_view(), name='api-news-read'),
|
||||
re_path(r'.*$', NewsFeedEntryDetail.as_view(), name='api-news-detail'),
|
||||
])),
|
||||
re_path(r'^.*$', NewsFeedEntryList.as_view(), name='api-news-list'),
|
||||
|
@ -2199,14 +2199,13 @@ class NotificationEntry(models.Model):
|
||||
|
||||
|
||||
class NotificationMessage(models.Model):
|
||||
"""A NotificationEntry records the last time a particular notifaction was sent out.
|
||||
"""A NotificationMessage is a message sent to a particular user, notifying them of some *important information*
|
||||
|
||||
It is recorded to ensure that notifications are not sent out "too often" to users.
|
||||
Notification messages can be generated by a variety of sources.
|
||||
|
||||
Attributes:
|
||||
- key: A text entry describing the notification e.g. 'part.notify_low_stock'
|
||||
- uid: An (optional) numerical ID for a particular instance
|
||||
- date: The last time this notification was sent
|
||||
target_object: The 'target' of the notification message
|
||||
source_object: The 'source' of the notification message
|
||||
"""
|
||||
|
||||
# generic link to target
|
||||
|
@ -158,7 +158,7 @@ class NotificationMessageSerializer(InvenTreeModelSerializer):
|
||||
|
||||
age_human = serializers.CharField(read_only=True)
|
||||
|
||||
read = serializers.BooleanField(read_only=True)
|
||||
read = serializers.BooleanField()
|
||||
|
||||
def get_target(self, obj):
|
||||
"""Function to resolve generic object reference to target."""
|
||||
@ -203,20 +203,10 @@ class NotificationMessageSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class NotificationReadSerializer(NotificationMessageSerializer):
|
||||
"""Serializer for reading a notification."""
|
||||
|
||||
def is_valid(self, raise_exception=False):
|
||||
"""Ensure instance data is available for view and let validation pass."""
|
||||
self.instance = self.context['instance'] # set instance that should be returned
|
||||
self._validated_data = True
|
||||
return True
|
||||
|
||||
|
||||
class NewsFeedEntrySerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the NewsFeedEntry model."""
|
||||
|
||||
read = serializers.BooleanField(read_only=True)
|
||||
read = serializers.BooleanField()
|
||||
|
||||
class Meta:
|
||||
"""Meta options for NewsFeedEntrySerializer."""
|
||||
|
@ -25,7 +25,7 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
{% if user.is_staff and roles.part_category.change %}
|
||||
{% if category and user.is_staff and roles.part_category.change %}
|
||||
{% url 'admin:part_partcategory_change' category.pk as url %}
|
||||
{% include "admin_button.html" with url=url %}
|
||||
{% endif %}
|
||||
|
@ -2679,7 +2679,7 @@ function loadBuildTable(table, options) {
|
||||
treeColumn: 1,
|
||||
});
|
||||
|
||||
table.treegrid('expandAll');
|
||||
$(table).treegrid('expandAll');
|
||||
} else if (display_mode == 'calendar') {
|
||||
|
||||
if (!loaded_calendar) {
|
||||
|
@ -17,6 +17,7 @@ function loadNewsFeedTable(table, options={}, enableDelete=false) {
|
||||
groupBy: false,
|
||||
queryParams: {
|
||||
ordering: 'published',
|
||||
read: false,
|
||||
},
|
||||
paginationVAlign: 'bottom',
|
||||
formatNoMatches: function() {
|
||||
@ -49,10 +50,31 @@ function loadNewsFeedTable(table, options={}, enableDelete=false) {
|
||||
field: 'published',
|
||||
title: '{% trans "Published" %}',
|
||||
sortable: 'true',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
formatter: function(value, row) {
|
||||
var html = renderDate(value);
|
||||
var buttons = getReadEditButton(row.pk, row.read);
|
||||
html += `<div class='btn-group float-right' role='group'>${buttons}</div>`;
|
||||
return html;
|
||||
}
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
$(table).on('click', '.notification-read', function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
var url = `/api/news/${pk}/`;
|
||||
|
||||
inventreePut(url,
|
||||
{
|
||||
read: true,
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ function loadNotificationTable(table, options={}, enableDelete=false) {
|
||||
var params = options.params || {};
|
||||
var read = typeof(params.read) === 'undefined' ? true : params.read;
|
||||
|
||||
setupFilterList(`notifications-${options.name}`, table);
|
||||
setupFilterList(`notifications-${options.name}`, $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: options.url,
|
||||
@ -157,38 +157,50 @@ function notificationCheck(force = false) {
|
||||
* - panel_caller: this button was clicked in the notification panel
|
||||
**/
|
||||
function updateNotificationReadState(btn, panel_caller=false) {
|
||||
var url = `/api/notifications/${btn.attr('pk')}/${btn.attr('target')}/`;
|
||||
|
||||
inventreePut(url, {}, {
|
||||
method: 'POST',
|
||||
success: function() {
|
||||
// update the notification tables if they were declared
|
||||
if (window.updateNotifications) {
|
||||
window.updateNotifications();
|
||||
}
|
||||
// Determine 'read' status of the notification
|
||||
var status = btn.attr('target') == 'read';
|
||||
var pk = btn.attr('pk');
|
||||
|
||||
// update current notification count
|
||||
var count = parseInt($('#notification-counter').html());
|
||||
if (btn.attr('target') == 'read') {
|
||||
count = count - 1;
|
||||
} else {
|
||||
count = count + 1;
|
||||
}
|
||||
var url = `/api/notifications/${pk}/`;
|
||||
|
||||
// Prevent negative notification count
|
||||
if (count < 0) {
|
||||
count = 0;
|
||||
}
|
||||
inventreePut(
|
||||
url,
|
||||
{
|
||||
read: status,
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function() {
|
||||
// update the notification tables if they were declared
|
||||
if (window.updateNotifications) {
|
||||
window.updateNotifications();
|
||||
}
|
||||
|
||||
// update notification indicator now
|
||||
updateNotificationIndicator(count);
|
||||
// update current notification count
|
||||
var count = parseInt($('#notification-counter').html());
|
||||
|
||||
// remove notification if called from notification panel
|
||||
if (panel_caller) {
|
||||
btn.parent().parent().remove();
|
||||
if (status) {
|
||||
count = count - 1;
|
||||
} else {
|
||||
count = count + 1;
|
||||
}
|
||||
|
||||
// Prevent negative notification count
|
||||
if (count < 0) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
// update notification indicator now
|
||||
updateNotificationIndicator(count);
|
||||
|
||||
// remove notification if called from notification panel
|
||||
if (panel_caller) {
|
||||
btn.parent().parent().remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user