├── README.md ├── persistent_messages ├── __init__.py ├── admin.py ├── api.py ├── constants.py ├── models.py ├── notify.py ├── storage.py ├── templates │ └── persistent_messages │ │ └── message │ │ ├── detail.html │ │ └── includes │ │ ├── message_li.html │ │ ├── messages.html │ │ └── messages.jquery.html ├── templatetags │ ├── __init__.py │ └── message_filters.py ├── tests.py ├── urls.py └── views.py └── setup.py /README.md: -------------------------------------------------------------------------------- 1 | Django Persistent Messages 2 | ========================== 3 | 4 | A Django app for unified and persistent user messages/notifications, built on top of Django's [messages framework](http://docs.djangoproject.com/en/dev/ref/contrib/messages/) (`django.contrib.messages`). 5 | 6 | This app provides support for messages that are supposed to be persistent, that is, they outlast a browser session and will be displayed as “sticky” notes to the user, until they are actively marked as read. Once read, messages are still listed in the message inbox for each user. In short: While `django.contrib.messages` makes sure that messages you create are displayed to the user, this app makes sure that they actually get noticed. 7 | 8 | * For authenticated users, messages are stored in the database. They can be temporary just like regular messages, or persistent as described above. 9 | * For anonymous users, the default cookie/session-based approach is used, i.e. there is no database access for storing messages and persistent messages are *not* possible. 10 | * There is a unified API for displaying messages to both types of users, that is, you can use the same code you'd be using with Django's messaging framework in order to add and display messages, but there is additional functionality available if the user is authenticated. 11 | * Messages can be displayed on-screen and/or sent to individual users as email notifications. 12 | 13 | Installation 14 | ------------ 15 | 16 | This document assumes that you are familiar with Python and Django. 17 | 18 | 1. [Download and unzip the app](http://github.com/philomat/django-persistent-messages/), or clone the source using `git`: 19 | 20 | $ git clone git://github.com/philomat/django-persistent-messages.git 21 | 22 | 2. Make sure `persistent_messages` is on your `PYTHONPATH`. 23 | 3. Add `persistent_messages` to your `INSTALLED_APPS` setting. 24 | 25 | INSTALLED_APPS = ( 26 | ... 27 | 'persistent_messages', 28 | ) 29 | 30 | 4. Make sure Django's `MessageMiddleware` is in your `MIDDLEWARE_CLASSES` setting (which is the case by default): 31 | 32 | MIDDLEWARE_CLASSES = ( 33 | ... 34 | 'django.contrib.messages.middleware.MessageMiddleware', 35 | ) 36 | 37 | 5. Add the persistent_messages URLs to your URL conf. For instance, in order to make messages available under `http://domain.com/messages/`, add the following line to `urls.py`. 38 | 39 | urlpatterns = patterns('', 40 | (r'^messages/', include('persistent_messages.urls')), 41 | ... 42 | ) 43 | 44 | 6. In your settings, set the message [storage backend](http://docs.djangoproject.com/en/dev/ref/contrib/messages/#message-storage-backends) to `persistent_messages.storage.PersistentMessageStorage`: 45 | 46 | MESSAGE_STORAGE = 'persistent_messages.storage.PersistentMessageStorage' 47 | 48 | 7. Set up the database tables using 49 | 50 | $ manage.py syncdb 51 | 52 | 8. If you want to use the bundled templates, add the `templates` directory to your `TEMPLATE_DIRS` setting: 53 | 54 | TEMPLATE_DIRS = ( 55 | ... 56 | 'path/to/persistent_messages/templates') 57 | ) 58 | 59 | 60 | Using messages in views and templates 61 | ------------------------------------- 62 | 63 | ### Message levels ### 64 | 65 | Django's messages framework provides a number of [message levels](http://docs.djangoproject.com/en/dev/ref/contrib/messages/#message-levels) for various purposes such as success messages, warnings etc. This app provides constants with the same names, the difference being that messages with these levels are going to be persistent: 66 | 67 | import persistent_messages 68 | # persistent message levels: 69 | persistent_messages.INFO 70 | persistent_messages.SUCCESS 71 | persistent_messages.WARNING 72 | persistent_messages.ERROR 73 | 74 | from django.contrib import messages 75 | # temporary message levels: 76 | messages.INFO 77 | messages.SUCCESS 78 | messages.WARNING 79 | messages.ERROR 80 | 81 | ### Adding a message ### 82 | 83 | Since the app is implemented as a [storage backend](http://docs.djangoproject.com/en/dev/ref/contrib/messages/#message-storage-backends) for Django's [messages framework](http://docs.djangoproject.com/en/dev/ref/contrib/messages/), you can still use the regular Django API to add a message: 84 | 85 | from django.contrib import messages 86 | messages.add_message(request, messages.INFO, 'Hello world.') 87 | 88 | This is compatible and equivalent to using the API provided by `persistent_messages`: 89 | 90 | import persistent_messages 91 | from django.contrib import messages 92 | persistent_messages.add_message(request, messages.INFO, 'Hello world.') 93 | 94 | In order to add a persistent message, use the message levels listed above: 95 | 96 | messages.add_message(request, persistent_messages.WARNING, 'You are going to see this message until you mark it as read.') 97 | 98 | or the equivalent: 99 | 100 | persistent_messages.add_message(request, persistent_messages.WARNING, 'You are going to see this message until you mark it as read.') 101 | 102 | Note that this is only possible for logged-in users, so you are probably going to have make sure that the current user is not anonymous using `request.user.is_authenticated()`. Adding a persistent message for anonymous users raises a `NotImplementedError`. 103 | 104 | Using `persistent_messages.add_message`, you can also add a subject line to the message. This makes sense when you are using the email notification feature. The following message will be displayed on-screen and sent to the email address associated with the current user: 105 | 106 | persistent_messages.add_message(request, persistent_messages.INFO, 'Message body', subject='Please read me', email=True) 107 | 108 | You can also pass this function a `User` object if the message is supposed to be sent to a user other than the one who is currently authenticated. User Sally will see this message the next time she logs in: 109 | 110 | from django.contrib.auth.models import User 111 | sally = User.objects.get(username='Sally') 112 | persistent_messages.add_message(request, persistent_messages.SUCCESS, 'Hi Sally, here is a message to you.', subject='Success message', user=sally) 113 | 114 | ### Displaying messages ### 115 | 116 | Messages can be displayed [as described in the Django manual](http://docs.djangoproject.com/en/dev/ref/contrib/messages/#displaying-messages). However, you are probably going to want to include links tags for closing each message (i.e. marking it as read). In your template, use something like: 117 | 118 | {% if messages %} 119 | 128 | {% endif %} 129 | 130 | You can also use the bundled templates instead. The following line replaces the code above. It allows the user to remove messages and mark them as read using Ajax requests, provided your HTML page includes JQuery: 131 | 132 | {% include "persistent_messages/message/includes/messages.jquery.html" %} 133 | -------------------------------------------------------------------------------- /persistent_messages/__init__.py: -------------------------------------------------------------------------------- 1 | from persistent_messages.api import * 2 | from persistent_messages.constants import * 3 | from django.contrib import messages 4 | 5 | messages.DEFAULT_TAGS.update(DEFAULT_TAGS) 6 | -------------------------------------------------------------------------------- /persistent_messages/admin.py: -------------------------------------------------------------------------------- 1 | from persistent_messages.models import Message 2 | from django.contrib import admin 3 | 4 | class MessageAdmin(admin.ModelAdmin): 5 | list_display = ['level', 'user', 'from_user', 'subject', 'message', 'created', 'read', 'is_persistent'] 6 | 7 | admin.site.register(Message, MessageAdmin) 8 | -------------------------------------------------------------------------------- /persistent_messages/api.py: -------------------------------------------------------------------------------- 1 | from persistent_messages import notify 2 | from persistent_messages import constants 3 | 4 | def add_message(request, level, message, extra_tags='', fail_silently=False, subject='', user=None, email=False, from_user=None, expires=None, close_timeout=None): 5 | """ 6 | """ 7 | if email: 8 | notify.email(level, message, extra_tags, subject, user, from_user) 9 | return request._messages.add(level, message, extra_tags, subject, user, from_user, expires, close_timeout) 10 | 11 | def info(request, message, extra_tags='', fail_silently=False, subject='', user=None, email=False, from_user=None, expires=None, close_timeout=None): 12 | """ 13 | """ 14 | level = constants.INFO 15 | return add_message(request, level, message, extra_tags, fail_silently, subject, user, email, from_user, expires, close_timeout) 16 | 17 | def warning(request, message, extra_tags='', fail_silently=False, subject='', user=None, email=False, from_user=None, expires=None, close_timeout=None): 18 | """ 19 | """ 20 | level = constants.WARNING 21 | return add_message(request, level, message, extra_tags, fail_silently, subject, user, email, from_user, expires, close_timeout) 22 | 23 | def debug(request, message, extra_tags='', fail_silently=False, subject='', user=None, email=False, from_user=None, expires=None, close_timeout=None): 24 | """ 25 | """ 26 | level = constants.DEBUG 27 | return add_message(request, level, message, extra_tags, fail_silently, subject, user, email, from_user, expires, close_timeout) 28 | -------------------------------------------------------------------------------- /persistent_messages/constants.py: -------------------------------------------------------------------------------- 1 | DEBUG = 110 2 | INFO = 120 3 | SUCCESS = 125 4 | WARNING = 130 5 | ERROR = 140 6 | 7 | DEFAULT_TAGS = { 8 | INFO: 'info', 9 | SUCCESS: 'success', 10 | WARNING: 'warning', 11 | ERROR: 'error', 12 | } 13 | 14 | PERSISTENT_MESSAGE_LEVELS = (INFO, SUCCESS, WARNING, ERROR) 15 | -------------------------------------------------------------------------------- /persistent_messages/models.py: -------------------------------------------------------------------------------- 1 | import persistent_messages 2 | from persistent_messages.constants import PERSISTENT_MESSAGE_LEVELS 3 | from django.db import models 4 | from django.contrib.auth.models import User 5 | from django.utils.encoding import force_unicode 6 | from django.contrib import messages 7 | from django.contrib.messages import utils 8 | from django.utils.translation import ugettext_lazy as _ 9 | from django.utils.encoding import force_unicode 10 | 11 | LEVEL_TAGS = utils.get_level_tags() 12 | 13 | class Message(models.Model): 14 | user = models.ForeignKey(User, blank=True, null=True) 15 | from_user = models.ForeignKey(User, blank=True, null=True, related_name="from_user") 16 | subject = models.CharField(max_length=255, blank=True, default='') 17 | message = models.TextField() 18 | LEVEL_CHOICES = ( 19 | (messages.DEBUG, 'DEBUG'), 20 | (messages.INFO, 'INFO'), 21 | (messages.SUCCESS, 'SUCCESS'), 22 | (messages.WARNING, 'WARNING'), 23 | (messages.ERROR, 'ERROR'), 24 | (persistent_messages.DEBUG, 'PERSISTENT DEBUG'), 25 | (persistent_messages.INFO, 'PERSISTENT INFO'), 26 | (persistent_messages.SUCCESS, 'PERSISTENT SUCCESS'), 27 | (persistent_messages.WARNING, 'PERSISTENT WARNING'), 28 | (persistent_messages.ERROR, 'PERSISTENT ERROR'), 29 | ) 30 | level = models.IntegerField(choices=LEVEL_CHOICES) 31 | extra_tags = models.CharField(max_length=128) 32 | created = models.DateTimeField(auto_now_add=True) 33 | modified = models.DateTimeField(auto_now=True) 34 | read = models.BooleanField(default=False) 35 | expires = models.DateTimeField(null=True, blank=True) 36 | close_timeout = models.IntegerField(null=True, blank=True) 37 | 38 | def is_persistent(self): 39 | return self.level in PERSISTENT_MESSAGE_LEVELS 40 | is_persistent.boolean = True 41 | 42 | def __eq__(self, other): 43 | return isinstance(other, Message) and self.level == other.level and \ 44 | self.message == other.message 45 | def __unicode__(self): 46 | if self.subject: 47 | message = _('%(subject)s: %(message)s') % {'subject': self.subject, 'message': self.message} 48 | else: 49 | message = self.message 50 | return force_unicode(message) 51 | 52 | def _prepare_message(self): 53 | """ 54 | Prepares the message for saving by forcing the ``message`` 55 | and ``extra_tags`` and ``subject`` to unicode in case they are lazy translations. 56 | 57 | Known "safe" types (None, int, etc.) are not converted (see Django's 58 | ``force_unicode`` implementation for details). 59 | """ 60 | self.subject = force_unicode(self.subject, strings_only=True) 61 | self.message = force_unicode(self.message, strings_only=True) 62 | self.extra_tags = force_unicode(self.extra_tags, strings_only=True) 63 | 64 | def save(self, *args, **kwargs): 65 | self._prepare_message() 66 | super(Message, self).save(*args, **kwargs) 67 | 68 | def _get_tags(self): 69 | label_tag = force_unicode(LEVEL_TAGS.get(self.level, ''), 70 | strings_only=True) 71 | extra_tags = force_unicode(self.extra_tags, strings_only=True) 72 | 73 | if (self.read): 74 | read_tag = "read" 75 | else: 76 | read_tag = "unread" 77 | 78 | if extra_tags and label_tag: 79 | return u' '.join([extra_tags, label_tag, read_tag]) 80 | elif extra_tags: 81 | return u' '.join([extra_tags, read_tag]) 82 | elif label_tag: 83 | return u' '.join([label_tag, read_tag]) 84 | return read_tag 85 | tags = property(_get_tags) 86 | -------------------------------------------------------------------------------- /persistent_messages/notify.py: -------------------------------------------------------------------------------- 1 | from django.core.mail import send_mail 2 | 3 | def email(level, message, extra_tags, subject, user, from_user): 4 | if not user or not user.email: 5 | raise Exception('Function needs to be passed a `User` object with valid email address.') 6 | send_mail(subject, message, from_user.email if from_user else None, [user.email], fail_silently=False) 7 | -------------------------------------------------------------------------------- /persistent_messages/storage.py: -------------------------------------------------------------------------------- 1 | from persistent_messages.models import Message 2 | from persistent_messages.constants import PERSISTENT_MESSAGE_LEVELS 3 | from django.contrib import messages 4 | from django.contrib.messages.storage.base import BaseStorage 5 | from django.contrib.auth.models import AnonymousUser 6 | from django.contrib.messages.storage.fallback import FallbackStorage 7 | from django.db.models import Q 8 | import datetime 9 | 10 | def get_user(request): 11 | if hasattr(request, 'user') and request.user.__class__ != AnonymousUser: 12 | return request.user 13 | else: 14 | return AnonymousUser() 15 | 16 | """ 17 | Messages need a primary key when being displayed so that they can be closed/marked as read by the user. 18 | Hence, they need to be stored when being added. You can disable this, but then you'll only be able to 19 | close a message when it is displayed for the second time. 20 | """ 21 | STORE_WHEN_ADDING = True 22 | 23 | #@TODO USE FALLBACK 24 | class PersistentMessageStorage(FallbackStorage): 25 | 26 | def __init__(self, *args, **kwargs): 27 | super(PersistentMessageStorage, self).__init__(*args, **kwargs) 28 | self.non_persistent_messages = [] 29 | self.is_anonymous = not get_user(self.request).is_authenticated() 30 | 31 | def _message_queryset(self, exclude_unread=True): 32 | qs = Message.objects.filter(user=get_user(self.request)).filter(Q(expires=None) | Q(expires__gt=datetime.datetime.now())) 33 | if exclude_unread: 34 | qs = qs.exclude(read=True) 35 | return qs 36 | 37 | def _get(self, *args, **kwargs): 38 | """ 39 | Retrieves a list of stored messages. Returns a tuple of the messages 40 | and a flag indicating whether or not all the messages originally 41 | intended to be stored in this storage were, in fact, stored and 42 | retrieved; e.g., ``(messages, all_retrieved)``. 43 | """ 44 | if not get_user(self.request).is_authenticated(): 45 | return super(PersistentMessageStorage, self)._get(*args, **kwargs) 46 | messages = [] 47 | for message in self._message_queryset(): 48 | if not message.is_persistent(): 49 | self.non_persistent_messages.append(message) 50 | messages.append(message) 51 | return (messages, True) 52 | 53 | def get_persistent(self): 54 | return self._message_queryset(exclude_unread=False).filter(level__in=PERSISTENT_MESSAGE_LEVELS) 55 | 56 | def get_persistent_unread(self): 57 | return self._message_queryset(exclude_unread=True).filter(level__in=PERSISTENT_MESSAGE_LEVELS) 58 | 59 | def count_unread(self): 60 | return self._message_queryset(exclude_unread=True).count() 61 | 62 | def count_persistent_unread(self): 63 | return self.get_persistent_unread().count() 64 | 65 | def _delete_non_persistent(self): 66 | for message in self.non_persistent_messages: 67 | message.delete() 68 | self.non_persistent_messages = [] 69 | 70 | def __iter__(self): 71 | if not get_user(self.request).is_authenticated(): 72 | return super(PersistentMessageStorage, self).__iter__() 73 | self.used = True 74 | messages = [] 75 | messages.extend(self._loaded_messages) 76 | if self._queued_messages: 77 | messages.extend(self._queued_messages) 78 | return iter(messages) 79 | 80 | def _prepare_messages(self, messages): 81 | if not get_user(self.request).is_authenticated(): 82 | return super(PersistentMessageStorage, self)._prepare_messages(messages) 83 | """ 84 | Obsolete method since model takes care of this. 85 | """ 86 | pass 87 | 88 | def _store(self, messages, response, *args, **kwargs): 89 | """ 90 | Stores a list of messages, returning a list of any messages which could 91 | not be stored. 92 | 93 | If STORE_WHEN_ADDING is True, messages are already stored at this time and won't be 94 | saved again. 95 | """ 96 | if not get_user(self.request).is_authenticated(): 97 | return super(PersistentMessageStorage, self)._store(messages, response, *args, **kwargs) 98 | for message in messages: 99 | if not self.used or message.is_persistent(): 100 | if not message.pk: 101 | message.save() 102 | return [] 103 | 104 | def update(self, response): 105 | if not get_user(self.request).is_authenticated(): 106 | return super(PersistentMessageStorage, self).update(response) 107 | """ 108 | Deletes all non-persistent, read messages. Saves all unstored messages. 109 | """ 110 | if self.used: 111 | self._delete_non_persistent() 112 | return super(PersistentMessageStorage, self).update(response) 113 | 114 | def add(self, level, message, extra_tags='', subject='', user=None, from_user=None, expires=None, close_timeout=None): 115 | """ 116 | Queues a message to be stored. 117 | 118 | The message is only queued if it contained something and its level is 119 | not less than the recording level (``self.level``). 120 | """ 121 | to_user = user or get_user(self.request) 122 | if not to_user.is_authenticated(): 123 | if Message(level=level).is_persistent(): 124 | raise NotImplementedError('Persistent message levels cannot be used for anonymous users.') 125 | else: 126 | return super(PersistentMessageStorage, self).add(level, message, extra_tags) 127 | if not message: 128 | return 129 | # Check that the message level is not less than the recording level. 130 | level = int(level) 131 | if level < self.level: 132 | return 133 | # Add the message. 134 | message = Message(user=to_user, level=level, message=message, extra_tags=extra_tags, subject=subject, from_user=from_user, expires=expires, close_timeout=close_timeout) 135 | # Messages need a primary key when being displayed so that they can be closed/marked as read by the user. 136 | # Hence, save it now instead of adding it to queue: 137 | if STORE_WHEN_ADDING: 138 | message.save() 139 | else: 140 | self.added_new = True 141 | self._queued_messages.append(message) 142 | -------------------------------------------------------------------------------- /persistent_messages/templates/persistent_messages/message/detail.html: -------------------------------------------------------------------------------- 1 | {# Example message detail template #} 2 |
3 | {% if message.subject %}

{{ message.subject }}

{% endif %} 4 |

{{ message.created|date }}

5 | {% if message.from_user %}

From: {{ message.from_user }}

{% endif %} 6 |

{{ message.message }}

7 | 8 | -------------------------------------------------------------------------------- /persistent_messages/templates/persistent_messages/message/includes/message_li.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 |
  • 3 | {% if message.subject %}{{ message.subject }}
    {% endif %} 4 | {% if message.is_persistent or jquery %} 5 | {% with 32 as max_words %} 6 | {{ message.message|truncatewords:max_words }}{% if message.message != message.message|truncatewords:max_words %} more{% endif %}
    7 | {% endwith %} 8 | {% else %} 9 | {{ message }} 10 | {% endif %} 11 | {% if message.is_persistent or jquery %} 12 | {% trans "close" %} 13 | {% endif %} 14 |
  • 15 | -------------------------------------------------------------------------------- /persistent_messages/templates/persistent_messages/message/includes/messages.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% if messages %} 3 | 10 | {% if close_all and messages|length > 1 %} 11 | {% trans "close all" %} 12 | {% endif %} 13 | {% endif %} 14 | -------------------------------------------------------------------------------- /persistent_messages/templates/persistent_messages/message/includes/messages.jquery.html: -------------------------------------------------------------------------------- 1 | {% if messages %} 2 | 57 | {% with 1 as jquery %} 58 | {% include "persistent_messages/message/includes/messages.html" %} 59 | {% endwith %} 60 | {% endif %} 61 | -------------------------------------------------------------------------------- /persistent_messages/templatetags/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samluescher/django-persistent-messages/045ed9b0d896919cd6813ba9fe528ebf6180fa28/persistent_messages/templatetags/__init__.py -------------------------------------------------------------------------------- /persistent_messages/templatetags/message_filters.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from django import template 3 | 4 | def latest(queryset, count): 5 | return queryset.order_by('-created')[:count] 6 | 7 | def latest_or_unread(queryset, count): 8 | count_unread = queryset.filter(read=False).count() 9 | if count_unread > count: 10 | count = count_unread 11 | return queryset.order_by('read', '-created')[:count] 12 | 13 | register = template.Library() 14 | register.filter(latest) 15 | register.filter(latest_or_unread) -------------------------------------------------------------------------------- /persistent_messages/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates two different styles of tests (one doctest and one 3 | unittest). These will both pass when you run "manage.py test". 4 | 5 | Replace these with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | class SimpleTest(TestCase): 11 | def test_basic_addition(self): 12 | """ 13 | Tests that 1 + 1 always equals 2. 14 | """ 15 | self.failUnlessEqual(1 + 1, 2) 16 | 17 | __test__ = {"doctest": """ 18 | Another way to test that 1 + 1 is equal to 2. 19 | 20 | >>> 1 + 1 == 2 21 | True 22 | """} 23 | 24 | -------------------------------------------------------------------------------- /persistent_messages/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls.defaults import * 2 | 3 | urlpatterns = patterns('', 4 | url(r'^detail/(?P\d+)/$', 'persistent_messages.views.message_detail', name='message_detail'), 5 | url(r'^mark_read/(?P\d+)/$', 'persistent_messages.views.message_mark_read', name='message_mark_read'), 6 | url(r'^mark_read/all/$', 'persistent_messages.views.message_mark_all_read', name='message_mark_all_read'), 7 | ) 8 | -------------------------------------------------------------------------------- /persistent_messages/views.py: -------------------------------------------------------------------------------- 1 | from persistent_messages.models import Message 2 | from persistent_messages.storage import get_user 3 | from django.http import HttpResponse, HttpResponseRedirect 4 | from django.shortcuts import get_object_or_404, render_to_response 5 | from django.template import RequestContext 6 | from django.core.exceptions import PermissionDenied 7 | 8 | def message_detail(request, message_id): 9 | user = get_user(request) 10 | if not user.is_authenticated(): 11 | raise PermissionDenied 12 | message = get_object_or_404(Message, user=user, pk=message_id) 13 | message.read = True 14 | message.save() 15 | return render_to_response('persistent_messages/message/detail.html', {'message': message}, 16 | context_instance=RequestContext(request)) 17 | 18 | def message_mark_read(request, message_id): 19 | user = get_user(request) 20 | if not user.is_authenticated(): 21 | raise PermissionDenied 22 | message = get_object_or_404(Message, user=user, pk=message_id) 23 | message.read = True 24 | message.save() 25 | if not request.is_ajax(): 26 | return HttpResponseRedirect(request.META.get('HTTP_REFERER') or '/') 27 | else: 28 | return HttpResponse('') 29 | 30 | def message_mark_all_read(request): 31 | user = get_user(request) 32 | if not user.is_authenticated(): 33 | raise PermissionDenied 34 | Message.objects.filter(user=user).update(read=True) 35 | if not request.is_ajax(): 36 | return HttpResponseRedirect(request.META.get('HTTP_REFERER') or '/') 37 | else: 38 | return HttpResponse('') 39 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from distutils.core import setup 3 | 4 | def read(fname): 5 | return open(os.path.join(os.path.dirname(__file__), fname)).read() 6 | 7 | setup( 8 | name='django-persistent-messages', 9 | version='0.1', 10 | description='A Django app for unified and persistent user messages/notifications, built on top of Django\'s messages framework', 11 | long_description=read('README.md'), 12 | author='philomat', 13 | license='BSD', 14 | url='http://github.com/philomat/django-persistent-messages', 15 | keywords = ['messages', 'django', 'persistent',], 16 | packages=[ 17 | 'persistent_messages', 18 | ], 19 | classifiers=[ 20 | 'Development Status :: 4 - Beta', 21 | 'Environment :: Web Environment', 22 | 'Intended Audience :: Developers', 23 | 'License :: OSI Approved :: BSD License', 24 | 'Operating System :: OS Independent', 25 | 'Programming Language :: Python', 26 | 'Framework :: Django', 27 | ], 28 | zip_safe=False, 29 | ) 30 | --------------------------------------------------------------------------------