diff --git a/README.md b/README.md index d38b8bd6..d23a57bf 100755 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Join us in-game in the channel allianceauth for help and feature requests. Special Thanks: - Thanking [Nikdoof](https://github.com/nikdoof), without his old auth + Thanking Nikdoof, without his old auth implementation this project wouldn't be as far as it is now. Thanks to Raynaldo for his original work on this system and getting it as far as it is today. @@ -74,6 +74,7 @@ Special Permissions In Admin: auth | user | sigtracker_view ( Allows for an individual view signitures) auth | user | optimer_management ( Allows for an individual to create and remove fleet operations) auth | user | optimer_view ( Allows for an individual view fleet operations) + auth | user | logging_notifications ( Generate notifications from logging) Active Developers diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index 56ea232b..830ed9ad 100755 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -61,6 +61,7 @@ INSTALLED_APPS = ( 'sigtracker', 'optimer', 'corputils', + 'notifications', ) MIDDLEWARE_CLASSES = ( @@ -126,6 +127,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'util.context_processors.domain_url', 'util.context_processors.member_api_mask', 'util.context_processors.blue_api_mask', + 'notifications.context_processors.user_notification_count', ) TEMPLATE_DIRS = ( @@ -408,67 +410,72 @@ LOGGING = { 'level': 'DEBUG', # edit this line to change logging level to console 'class': 'logging.StreamHandler', 'formatter': 'verbose', - } + }, + 'notifications': { # creates notifications for users with logging_notifications permission + 'level': 'ERROR', # edit this line to change logging level to notifications + 'class': 'notifications.handlers.NotificationHandler', + 'formatter': 'verbose', + }, }, 'loggers': { 'authentication': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'celerytask': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'eveonline': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'groupmanagement': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'hrapplications': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'portal': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'registration': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'services': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'srp': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'timerboard': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'sigtracker': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'optimer': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'corputils': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'util': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'DEBUG', }, 'django': { - 'handlers': ['log_file', 'console'], + 'handlers': ['log_file', 'console', 'notifications'], 'level': 'ERROR', }, } diff --git a/alliance_auth/urls.py b/alliance_auth/urls.py index df632863..5325ea52 100755 --- a/alliance_auth/urls.py +++ b/alliance_auth/urls.py @@ -186,4 +186,7 @@ urlpatterns = patterns('', url(r'^remove_optimer/(\w+)', 'optimer.views.remove_optimer', name='auth_remove_optimer'), url(r'^edit_optimer/(\w+)$', 'optimer.views.edit_optimer', name='auth_edit_optimer'), + # Notifications + url(r'^notifications/$', 'notifications.views.notification_list', name='auth_notification_list'), + url(r'^notifications/(\w+)/$', 'notifications.views.notification_view', name='auth_notification_view'), ) diff --git a/notifications/__init__.py b/notifications/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/notifications/admin.py b/notifications/admin.py new file mode 100644 index 00000000..56893293 --- /dev/null +++ b/notifications/admin.py @@ -0,0 +1,4 @@ +from django.contrib import admin +from.models import Notification + +admin.site.register(Notification) diff --git a/notifications/context_processors.py b/notifications/context_processors.py new file mode 100644 index 00000000..1c9a5385 --- /dev/null +++ b/notifications/context_processors.py @@ -0,0 +1,4 @@ +from .models import Notification + +def user_notification_count(request): + return {'notifications':len(Notification.objects.filter(user__id=request.user.id).filter(viewed=False))} diff --git a/notifications/handlers.py b/notifications/handlers.py new file mode 100644 index 00000000..bdffeb33 --- /dev/null +++ b/notifications/handlers.py @@ -0,0 +1,14 @@ +import logging +from django.contrib.auth.models import User +from .models import Notification + +class NotificationHandler(logging.Handler): + def emit(self, record): + for user in User.objects.all(): + if user.has_perm('auth.logging_notifications'): + notif = Notification() + notif.user = user + notif.title = "%s [%s:%s]" % (record.levelname, record.funcName, record.lineno) + notif.level = str([item[0] for item in Notification.LEVEL_CHOICES if item[1] == record.levelname][0]) + notif.message = record.getMessage() + notif.save() diff --git a/notifications/models.py b/notifications/models.py new file mode 100644 index 00000000..cf0d688d --- /dev/null +++ b/notifications/models.py @@ -0,0 +1,34 @@ +from django.db import models +from django.contrib.auth.models import User +import logging + +logger = logging.getLogger(__name__) + +class Notification(models.Model): + + LEVEL_CHOICES = ( + ('danger', 'CRITICAL'), + ('danger', 'ERROR'), + ('warning', 'WARN'), + ('info', 'INFO'), + ('success', 'DEBUG'), + ) + + user = models.ForeignKey(User, on_delete=models.CASCADE) + level = models.CharField(choices=LEVEL_CHOICES, max_length=10) + title = models.CharField(max_length=254) + message = models.TextField() + timestamp = models.DateTimeField(auto_now_add=True) + viewed = models.BooleanField(default=False) + + def view(self): + logger.info("Marking notification as viewed: %s" % self) + self.viewed = True + self.save() + + def __unicode__(self): + output = "%s: %s" % (self.user, self.title) + return output.encode('utf-8') + + def set_level(self, level): + self.level = [item[0] for item in self.LEVEL_CHOICES if item[1] == level][0] diff --git a/notifications/tests.py b/notifications/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/notifications/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/notifications/views.py b/notifications/views.py new file mode 100644 index 00000000..7fa53a48 --- /dev/null +++ b/notifications/views.py @@ -0,0 +1,31 @@ +from django.shortcuts import render, get_object_or_404, redirect +from .models import Notification +from django.contrib.auth.decorators import login_required +import logging + +logger = logging.getLogger(__name__) + +@login_required +def notification_list(request): + logger.debug("notification_list called by user %s" % request.user) + new_notifs = Notification.objects.filter(user=request.user).filter(viewed=False) + old_notifs = Notification.objects.filter(user=request.user).filter(viewed=True) + logger.debug("User %s has %s unread and %s read notifications" % (request.user, len(new_notifs), len(old_notifs))) + context = { + 'read': old_notifs, + 'unread': new_notifs, + } + return render(request, 'registered/notification_list.html', context) + +@login_required +def notification_view(request, notif_id): + logger.debug("notification_view called by user %s for notif_id %s" % (request.user, notif_id)) + notif = get_object_or_404(Notification, pk=notif_id) + if notif.user == request.user: + logger.debug("Providing notification for user %s" % request.user) + context = {'notif': notif} + notif.view() + return render(request, 'registered/notification_view.html', context) + else: + logger.warn("User %s not authorized to view notif_id %s belonging to user %s" % (request.user, notif_id, notif.user)) + return redirect('auth_notification_list') diff --git a/stock/templates/public/base.html b/stock/templates/public/base.html index a1c89870..b9f81154 100755 --- a/stock/templates/public/base.html +++ b/stock/templates/public/base.html @@ -47,6 +47,15 @@