Rafał Pitoń 10 years ago
parent
commit
c52dea93f2

+ 7 - 2
misago/notifications/api.py

@@ -2,6 +2,7 @@ from django.db.models import F
 from django.db import transaction
 from django.db import transaction
 from django.utils.html import escape
 from django.utils.html import escape
 
 
+from misago.notifications.checksums import update_checksum
 from misago.notifications.utils import hash_trigger
 from misago.notifications.utils import hash_trigger
 
 
 
 
@@ -13,7 +14,7 @@ __all__ = [
 ]
 ]
 
 
 
 
-def notify_user(user, message, url, trigger, formats=None, sender=None,
+def notify_user(user, message, url, trigger=None, formats=None, sender=None,
                 update_user=True):
                 update_user=True):
     from misago.notifications.models import Notification
     from misago.notifications.models import Notification
 
 
@@ -25,7 +26,7 @@ def notify_user(user, message, url, trigger, formats=None, sender=None,
         message_escaped = message_escaped % final_formats
         message_escaped = message_escaped % final_formats
 
 
     new_notification = Notification(user=user,
     new_notification = Notification(user=user,
-                                    trigger=hash_trigger(trigger),
+                                    trigger=hash_trigger(trigger or message),
                                     url=url,
                                     url=url,
                                     message=message_escaped)
                                     message=message_escaped)
 
 
@@ -35,6 +36,10 @@ def notify_user(user, message, url, trigger, formats=None, sender=None,
         new_notification.sender_slug = sender.slug
         new_notification.sender_slug = sender.slug
 
 
     new_notification.save()
     new_notification.save()
+
+    update_checksum(new_notification)
+    new_notification.save(update_fields=['checksum'])
+
     user.new_notifications = F('new_notifications') + 1
     user.new_notifications = F('new_notifications') + 1
     if update_user:
     if update_user:
         user.save(update_fields=['new_notifications'])
         user.save(update_fields=['new_notifications'])

+ 16 - 0
misago/notifications/checksums.py

@@ -0,0 +1,16 @@
+from misago.markup import checksums
+
+
+def is_valid(notification):
+    valid_checksum = make_checksum(notification)
+    return notification.checksum == valid_checksum
+
+
+def make_checksum(notification):
+    checksum_seeds = [unicode(notification.id), unicode(notification.user_id)]
+    return checksums.make_checksum(notification.message, checksum_seeds)
+
+
+def update_checksum(notification):
+    notification.checksum = make_checksum(notification)
+    return notification.checksum

+ 1 - 0
misago/notifications/migrations/0001_initial.py

@@ -22,6 +22,7 @@ class Migration(migrations.Migration):
                 ('date', models.DateTimeField(default=django.utils.timezone.now, db_index=True)),
                 ('date', models.DateTimeField(default=django.utils.timezone.now, db_index=True)),
                 ('trigger', models.CharField(max_length=8)),
                 ('trigger', models.CharField(max_length=8)),
                 ('message', models.TextField()),
                 ('message', models.TextField()),
+                ('checksum', models.CharField(max_length=64, default='-')),
                 ('url', models.TextField()),
                 ('url', models.TextField()),
                 ('sender_username', models.CharField(max_length=255, null=True, blank=True)),
                 ('sender_username', models.CharField(max_length=255, null=True, blank=True)),
                 ('sender_slug', models.CharField(max_length=255, null=True, blank=True)),
                 ('sender_slug', models.CharField(max_length=255, null=True, blank=True)),

+ 17 - 0
misago/notifications/models.py

@@ -1,7 +1,10 @@
 from django.conf import settings
 from django.conf import settings
+from django.core.urlresolvers import reverse
 from django.db import models
 from django.db import models
 from django.utils import timezone
 from django.utils import timezone
 
 
+from misago.notifications.checksums import is_valid
+
 
 
 class Notification(models.Model):
 class Notification(models.Model):
     user = models.ForeignKey(settings.AUTH_USER_MODEL,
     user = models.ForeignKey(settings.AUTH_USER_MODEL,
@@ -10,6 +13,7 @@ class Notification(models.Model):
     date = models.DateTimeField(default=timezone.now, db_index=True)
     date = models.DateTimeField(default=timezone.now, db_index=True)
     trigger = models.CharField(max_length=8)
     trigger = models.CharField(max_length=8)
     message = models.TextField()
     message = models.TextField()
+    checksum = models.CharField(max_length=64, default='-')
     url = models.TextField()
     url = models.TextField()
     sender = models.ForeignKey(settings.AUTH_USER_MODEL,
     sender = models.ForeignKey(settings.AUTH_USER_MODEL,
                                on_delete=models.SET_NULL,
                                on_delete=models.SET_NULL,
@@ -17,3 +21,16 @@ class Notification(models.Model):
                                blank=True, null=True)
                                blank=True, null=True)
     sender_username = models.CharField(max_length=255, blank=True, null=True)
     sender_username = models.CharField(max_length=255, blank=True, null=True)
     sender_slug = models.CharField(max_length=255, blank=True, null=True)
     sender_slug = models.CharField(max_length=255, blank=True, null=True)
+
+    @property
+    def is_valid(self):
+        return is_valid(self)
+
+    def get_absolute_url(self):
+        if self.is_new:
+            return reverse('misago:go_to_notification', kwargs={
+                'notification_id': self.id,
+                'trigger': self.trigger
+            })
+        else:
+            return self.url

+ 2 - 1
misago/notifications/tests/test_api.py

@@ -15,12 +15,13 @@ class NotificationsAPITests(TestCase):
 
 
     def test_notify_user(self):
     def test_notify_user(self):
         """notify_user sets new notification on user"""
         """notify_user sets new notification on user"""
-        api.notify_user(self.test_user,
+        notification = api.notify_user(self.test_user,
                         "Test notify %(token)s",
                         "Test notify %(token)s",
                         "/users/",
                         "/users/",
                         "test",
                         "test",
                         {'token': 'Bob'},
                         {'token': 'Bob'},
                         self.test_user)
                         self.test_user)
+        self.assertTrue(notification.is_valid)
 
 
         self.reload_test_user()
         self.reload_test_user()
         self.assertEqual(self.test_user.new_notifications, 1)
         self.assertEqual(self.test_user.new_notifications, 1)

+ 0 - 15
misago/notifications/tests/test_views.py

@@ -101,21 +101,6 @@ class NotificationViewsTests(AuthenticatedUserTestCase):
         self.reload_user()
         self.reload_user()
         self.assertEqual(self.user.new_notifications, 0)
         self.assertEqual(self.user.new_notifications, 0)
 
 
-    def test_read_all_ajax(self):
-        """real_all POST ajax to list sets all notifications as read"""
-        self.notify_user()
-
-        response = self.client.post(self.view_link, data={
-            'read-all': True,
-        },**self.ajax_header)
-        self.assertEqual(response.status_code, 200)
-
-        response = self.client.get(self.view_link)
-        self.assertEqual(response.status_code, 200)
-
-        self.reload_user()
-        self.assertEqual(self.user.new_notifications, 0)
-
 
 
 class AnonymousNotificationsViewsTests(UserTestCase):
 class AnonymousNotificationsViewsTests(UserTestCase):
     def setUp(self):
     def setUp(self):

+ 1 - 0
misago/notifications/urls.py

@@ -3,4 +3,5 @@ from django.conf.urls import include, patterns, url
 
 
 urlpatterns = patterns('misago.notifications.views',
 urlpatterns = patterns('misago.notifications.views',
     url(r'^notifications/$', 'notifications', name='notifications'),
     url(r'^notifications/$', 'notifications', name='notifications'),
+    url(r'^notifications/go-to/(?P<notification_id>\d+)/(?P<trigger>[a-zA-Z0-9]+)/$', 'go_to_notification', name='go_to_notification'),
 )
 )

+ 3 - 1
misago/notifications/utils.py

@@ -1,8 +1,10 @@
 from hashlib import md5
 from hashlib import md5
 
 
+from django.conf import settings
+
 
 
 def hash_trigger(message):
 def hash_trigger(message):
-    return md5(message).hexdigest()[:8]
+    return md5('%s:%s' % (message, settings.SECRET_KEY)).hexdigest()[:8]
 
 
 
 
 def variables_dict(plain=None, links=None, users=None, threads=None):
 def variables_dict(plain=None, links=None, users=None, threads=None):

+ 26 - 6
misago/notifications/views.py

@@ -1,7 +1,7 @@
 from django.contrib import messages
 from django.contrib import messages
 from django.db.transaction import atomic
 from django.db.transaction import atomic
 from django.http import JsonResponse
 from django.http import JsonResponse
-from django.shortcuts import redirect, render
+from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.translation import ugettext as _, ungettext
 from django.utils.translation import ugettext as _, ungettext
 
 
 from misago.core.uiviews import uiview
 from misago.core.uiviews import uiview
@@ -16,9 +16,7 @@ from misago.notifications.models import Notification
 def notifications(request):
 def notifications(request):
     if request.method == 'POST':
     if request.method == 'POST':
         if 'read-all' in request.POST:
         if 'read-all' in request.POST:
-            read_all(request)
-            if not request.is_ajax():
-                return redirect('misago:notifications')
+            return read_all(request)
         if request.POST.get('notification'):
         if request.POST.get('notification'):
             return read_notification(request)
             return read_notification(request)
     else:
     else:
@@ -51,12 +49,26 @@ def full_page(request):
 
 
 
 
 @atomic
 @atomic
+def go_to_notification(request, notification_id, trigger):
+    queryset = request.user.misago_notifications.select_for_update()
+    notification = get_object_or_404(
+        queryset, pk=notification_id, trigger=trigger)
+
+    if notification.is_new:
+        notification.is_new = False
+        notification.save(update_fields=['is_new'])
+        assert_real_new_notifications_count(request.user)
+
+    return redirect(notification.url)
+
+
+@atomic
 def read_all(request):
 def read_all(request):
-    if not request.is_ajax():
-        messages.success(request, _("All notifications were set as read."))
+    messages.success(request, _("All notifications were set as read."))
     read_all_user_alerts(request.user)
     read_all_user_alerts(request.user)
 
 
 
 
+@atomic
 def read_notification(request):
 def read_notification(request):
     try:
     try:
         queryset = request.user.misago_notifications
         queryset = request.user.misago_notifications
@@ -91,6 +103,14 @@ def read_notification(request):
             return redirect('misago:notifications')
             return redirect('misago:notifications')
 
 
 
 
+@atomic
+def read_all(request):
+    if not request.is_ajax():
+        messages.success(request, _("All notifications were set as read."))
+    read_all_user_alerts(request.user)
+    return redirect('misago:notifications')
+
+
 @uiview('misago_notifications')
 @uiview('misago_notifications')
 @deny_guests
 @deny_guests
 def event_sender(request, resolver_match):
 def event_sender(request, resolver_match):

+ 8 - 4
misago/templates/misago/notifications/dropdown.html

@@ -30,9 +30,16 @@
       <span class="fa fa-circle-o"></span>
       <span class="fa fa-circle-o"></span>
       {% endif %}
       {% endif %}
     </div>
     </div>
-    <a href="{{ item.url }}">
+    <a href="{{ item.get_absolute_url }}">
+    {% if item.is_valid %}
+    <a href="{{ item.get_absolute_url }}">
       {{ item.message|safe }}
       {{ item.message|safe }}
     </a>
     </a>
+    {% else %}
+    <a href="#">
+      <em>{% trans "Notification is not available." %}</em>
+    </a>
+    {% endif %}
     <footer class="text-muted">
     <footer class="text-muted">
       {% if item.sender_username %}
       {% if item.sender_username %}
         {% if item.sender_id %}
         {% if item.sender_id %}
@@ -62,8 +69,5 @@
     <button type="button" class="btn btn-default btn-refresh btn-sm" style="display: none;">
     <button type="button" class="btn btn-default btn-refresh btn-sm" style="display: none;">
       {% trans "Refresh" %}
       {% trans "Refresh" %}
     </button>
     </button>
-    <button type="submit" name="read-all" class="btn btn-default btn-read-all btn-sm pull-right" {% if not user.new_notifications %}style="display: none;"{% endif %}>
-      {% trans "Mark all as read" %}
-    </button>
   </form>
   </form>
 </div>
 </div>

+ 9 - 1
misago/templates/misago/notifications/full.html

@@ -54,7 +54,15 @@ You have {{ notifications }} notifications
         {% endif %}
         {% endif %}
       </div>
       </div>
 
 
-      <a href="{{ item.url }}" class="message">{{ item.message|safe }}</a>
+      {% if item.is_valid %}
+      <a href="{{ item.get_absolute_url }}" class="message">
+        {{ item.message|safe }}
+      </a>
+      {% else %}
+      <a href="#" class="message">
+        <em>{% trans "Notification is not available." %}</em>
+      </a>
+      {% endif %}
 
 
       <form method="POST" class="read-form">
       <form method="POST" class="read-form">
         {% csrf_token %}
         {% csrf_token %}