From 09a09b8ff58464a190c1fc79727086ecba6e9783 Mon Sep 17 00:00:00 2001 From: Jonathan Dan Date: Wed, 31 Dec 2014 13:00:55 +0100 Subject: [PATCH 1/5] Add `MESSAGES_DELETED_MAX_AGE` to settings * default is 30 days * New project dependency: `django-appconf` --- django_messages/conf.py | 8 ++++++++ setup.py | 3 +++ 2 files changed, 11 insertions(+) create mode 100644 django_messages/conf.py diff --git a/django_messages/conf.py b/django_messages/conf.py new file mode 100644 index 0000000..0a3d133 --- /dev/null +++ b/django_messages/conf.py @@ -0,0 +1,8 @@ +from django.conf import settings +from appconf import AppConf + +class MessagesAppConf(AppConf): + DELETED_MAX_AGE = 30 + + class Meta: + prefix = 'MESSAGES' diff --git a/setup.py b/setup.py index 5530c06..ba20cf8 100644 --- a/setup.py +++ b/setup.py @@ -19,6 +19,9 @@ 'locale/*/LC_MESSAGES/*', ] }, + install_requires=[ + "django-appconf>=0.6", + ], classifiers=( 'Development Status :: 4 - Beta', 'Environment :: Web Environment', From e74c5ccf2f4edb6259d2c94a4b68c48912202657 Mon Sep 17 00:00:00 2001 From: Jonathan Dan Date: Wed, 31 Dec 2014 13:43:22 +0100 Subject: [PATCH 2/5] if `minimum age` argument is not provided use `MESSAGES_DELETED_MAX_AGE` * Job can now be called with no arguments: setting `MESSAGES_DELETED_MAX_AGE` is used * Or using previous behaviour (by passing a minimum age as an argument) --- .../management/commands/delete_deleted_messages.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/django_messages/management/commands/delete_deleted_messages.py b/django_messages/management/commands/delete_deleted_messages.py index e79483d..9d1c758 100644 --- a/django_messages/management/commands/delete_deleted_messages.py +++ b/django_messages/management/commands/delete_deleted_messages.py @@ -2,18 +2,19 @@ from django.core.management.base import BaseCommand, CommandError from django.utils import timezone from ...models import Message +from django_messages.conf import settings class Command(BaseCommand): args = '' help = ( 'Deletes messages that have been marked as deleted by both the sender ' - 'and recipient. You must provide the minimum age in days.' + 'and recipient. Minimum age in days is pulled from settings or given as an argument.' ) def handle(self, *args, **options): if len(args) == 0: - raise CommandError('You must provide the minimum age in days.') + args = [settings.MESSAGES_DELETED_MAX_AGE] elif len(args) > 1: raise CommandError( 'This management command accepts only one argument.' From 3618c77dc6ce9610c86c956f7505a9540b186f2a Mon Sep 17 00:00:00 2001 From: Jonathan Dan Date: Wed, 31 Dec 2014 13:48:29 +0100 Subject: [PATCH 3/5] In trash view only display messages deletes since less than `MESSAGES_DELETED_MAX_AGE` --- django_messages/models.py | 9 ++++++--- django_messages/views.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/django_messages/models.py b/django_messages/models.py index 5dd07f4..e3b5566 100644 --- a/django_messages/models.py +++ b/django_messages/models.py @@ -1,9 +1,11 @@ +import datetime from django.conf import settings from django.db import models from django.db.models import signals from django.utils import timezone from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ +from django_messages.conf import settings AUTH_USER_MODEL = getattr(settings, 'AUTH_USER_MODEL', 'auth.User') @@ -33,14 +35,15 @@ def outbox_for(self, user): def trash_for(self, user): """ Returns all messages that were either received or sent by the given - user and are marked as deleted. + user and are marked as deleted since less than MESSAGES_DELETED_MAX_AGE days. """ + the_date = timezone.now() - datetime.timedelta(days=settings.MESSAGES_DELETED_MAX_AGE) return self.filter( recipient=user, - recipient_deleted_at__isnull=False, + recipient_deleted_at__gt=the_date, ) | self.filter( sender=user, - sender_deleted_at__isnull=False, + sender_deleted_at__gt=the_date, ) diff --git a/django_messages/views.py b/django_messages/views.py index 3341c32..b8f268b 100644 --- a/django_messages/views.py +++ b/django_messages/views.py @@ -46,7 +46,7 @@ def outbox(request, template_name='django_messages/outbox.html'): @login_required def trash(request, template_name='django_messages/trash.html'): """ - Displays a list of deleted messages. + Displays a list of deleted messages which are less than MESSAGES_DELETED_MAX_AGE days old. Optional arguments: ``template_name``: name of the template to use Hint: A Cron-Job could periodicly clean up old messages, which are deleted From 1e5823192c698d563773eca48f012532db77c870 Mon Sep 17 00:00:00 2001 From: Jonathan Dan Date: Tue, 13 Jan 2015 18:44:49 +0100 Subject: [PATCH 4/5] Added permanently deleted function Internally we change the deleted_at date of a permanently deleted message to more than MESSAGES_DELETED_MAX_AGE days ago. If both users permanently deleted a message or if deleted_at is more than MESSAGES_DELETED_MAX_AGE days ago, the message is deleted from the database. Even if the message could not be deleted from the database (one of the users did not delete the message), the message will not appear in the trash anymore. --- django_messages/management.py | 1 + .../templates/django_messages/trash.html | 4 +- django_messages/urls.py | 1 + django_messages/views.py | 45 ++++++++++++++++++- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/django_messages/management.py b/django_messages/management.py index 366296b..79d569a 100644 --- a/django_messages/management.py +++ b/django_messages/management.py @@ -11,6 +11,7 @@ def create_notice_types(app, created_models, verbosity, **kwargs): notification.create_notice_type("messages_replied", _("Message Replied"), _("you have replied to a message"), default=1) notification.create_notice_type("messages_reply_received", _("Reply Received"), _("you have received a reply to a message"), default=2) notification.create_notice_type("messages_deleted", _("Message Deleted"), _("you have deleted a message"), default=1) + notification.create_notice_type("messages_permanently_deleted", _("Message Permanently Deleted"), _("you have permanently deleted a message"), default=0) notification.create_notice_type("messages_recovered", _("Message Recovered"), _("you have undeleted a message"), default=1) signals.post_syncdb.connect(create_notice_types, sender=notification) diff --git a/django_messages/templates/django_messages/trash.html b/django_messages/templates/django_messages/trash.html index 02fb11e..30e162f 100644 --- a/django_messages/templates/django_messages/trash.html +++ b/django_messages/templates/django_messages/trash.html @@ -17,7 +17,7 @@

{% trans "Deleted Messages" %}

{{ message.subject }} {{ message.sent_at|date:_("DATETIME_FORMAT") }} - {% trans "undelete" %} + {% trans "undelete" %}, {% trans "permanently delete" %} {% endfor %} @@ -26,5 +26,5 @@

{% trans "Deleted Messages" %}

{% trans "No messages." %}

{% endif %}
-

{% trans "Deleted Messages are removed from the trash at unregular intervals, don't rely on this feature for long-time storage." %}

+

{% blocktrans with deleted_max_age=max_age %} Deleted Messages are removed from the trash after {{deleted_max_age}} days, don't rely on this feature for long-time storage.{% endblocktrans %}

{% endblock %} \ No newline at end of file diff --git a/django_messages/urls.py b/django_messages/urls.py index 1effc02..3ae78e4 100644 --- a/django_messages/urls.py +++ b/django_messages/urls.py @@ -12,6 +12,7 @@ url(r'^reply/(?P[\d]+)/$', reply, name='messages_reply'), url(r'^view/(?P[\d]+)/$', view, name='messages_detail'), url(r'^delete/(?P[\d]+)/$', delete, name='messages_delete'), + url(r'^permanently_delete/(?P[\d]+)/$', permanently_delete, name='messages_permanently_delete'), url(r'^undelete/(?P[\d]+)/$', undelete, name='messages_undelete'), url(r'^trash/$', trash, name='messages_trash'), ) diff --git a/django_messages/views.py b/django_messages/views.py index b8f268b..4018c44 100644 --- a/django_messages/views.py +++ b/django_messages/views.py @@ -1,3 +1,4 @@ +import datetime from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext @@ -11,6 +12,7 @@ from django_messages.models import Message from django_messages.forms import ComposeForm from django_messages.utils import format_quote, get_user_model, get_username_field +from django_messages.conf import settings User = get_user_model() @@ -54,7 +56,7 @@ def trash(request, template_name='django_messages/trash.html'): """ message_list = Message.objects.trash_for(request.user) return render_to_response(template_name, { - 'message_list': message_list, + 'message_list': message_list, 'max_age': settings.MESSAGES_DELETED_MAX_AGE }, context_instance=RequestContext(request)) @login_required @@ -189,6 +191,47 @@ def undelete(request, message_id, success_url=None): return HttpResponseRedirect(success_url) raise Http404 +def permanently_delete_message(user, message_id): + """ + Function called to permanently delete a message + """ + permanently_deleted_date = timezone.now() - datetime.timedelta(days=settings.MESSAGES_DELETED_MAX_AGE) + message = get_object_or_404(Message, id=message_id) + deleted = False + + if message.sender == user: + message.sender_deleted_at = permanently_deleted_date + deleted = True + if message.recipient == user: + message.recipient_deleted_at = permanently_deleted_date + deleted = True + if deleted: + message.save() + if message.sender_deleted_at and message.recipient_deleted_at: + if message.sender_deleted_at <= permanently_deleted_date and message.recipient_deleted_at <= permanently_deleted_date: + message.delete() + return deleted + +@login_required +def permanently_delete(request, message_id, success_url=None): + """ + Marks a message as permanently deleted by sender or recipient. + This is done by marking deleted_at to more than MESSAGES_DELETED_MAX_AGE + days ago. + The message is permanently deleted if both users permanently deleted it or + if it was deleted more than MESSAGES_DELETED_MAX_AGE days ago. + """ + if success_url is None: + success_url = reverse('messages_inbox') + if 'next' in request.GET: + success_url = request.GET['next'] + if permanently_delete_message(request.user, message_id): + messages.info(request, _(u"Message permanently deleted.")) + if notification: + notification.send([user], "messages_permanently_deleted") + return HttpResponseRedirect(success_url) + raise Http404 + @login_required def view(request, message_id, form_class=ComposeForm, quote_helper=format_quote, subject_template=_(u"Re: %(subject)s"), From 78ff3aba6f1f832a46695f61de6961a8dc2a3073 Mon Sep 17 00:00:00 2001 From: Jonathan Dan Date: Tue, 13 Jan 2015 19:06:08 +0100 Subject: [PATCH 5/5] Added empty trash function --- django_messages/management.py | 1 + .../templates/django_messages/trash.html | 1 + django_messages/urls.py | 1 + django_messages/views.py | 23 ++++++++++++++++--- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/django_messages/management.py b/django_messages/management.py index 79d569a..114be28 100644 --- a/django_messages/management.py +++ b/django_messages/management.py @@ -13,6 +13,7 @@ def create_notice_types(app, created_models, verbosity, **kwargs): notification.create_notice_type("messages_deleted", _("Message Deleted"), _("you have deleted a message"), default=1) notification.create_notice_type("messages_permanently_deleted", _("Message Permanently Deleted"), _("you have permanently deleted a message"), default=0) notification.create_notice_type("messages_recovered", _("Message Recovered"), _("you have undeleted a message"), default=1) + notification.create_notice_type("empty_trash", _("Emptied Trash"), _("you have permanently deleted all messages in your trash bin"), default=0) signals.post_syncdb.connect(create_notice_types, sender=notification) else: diff --git a/django_messages/templates/django_messages/trash.html b/django_messages/templates/django_messages/trash.html index 30e162f..b769829 100644 --- a/django_messages/templates/django_messages/trash.html +++ b/django_messages/templates/django_messages/trash.html @@ -5,6 +5,7 @@ {% block content %}

{% trans "Deleted Messages" %}

{% if message_list %} +{% trans "Empty trash" %} diff --git a/django_messages/urls.py b/django_messages/urls.py index 3ae78e4..23e6cd4 100644 --- a/django_messages/urls.py +++ b/django_messages/urls.py @@ -15,4 +15,5 @@ url(r'^permanently_delete/(?P[\d]+)/$', permanently_delete, name='messages_permanently_delete'), url(r'^undelete/(?P[\d]+)/$', undelete, name='messages_undelete'), url(r'^trash/$', trash, name='messages_trash'), + url(r'^empty_trash/$', empty_trash, name='messages_empty_trash'), ) diff --git a/django_messages/views.py b/django_messages/views.py index 4018c44..9d458f0 100644 --- a/django_messages/views.py +++ b/django_messages/views.py @@ -191,12 +191,11 @@ def undelete(request, message_id, success_url=None): return HttpResponseRedirect(success_url) raise Http404 -def permanently_delete_message(user, message_id): +def permanently_delete_message(user, message): """ Function called to permanently delete a message """ permanently_deleted_date = timezone.now() - datetime.timedelta(days=settings.MESSAGES_DELETED_MAX_AGE) - message = get_object_or_404(Message, id=message_id) deleted = False if message.sender == user: @@ -221,17 +220,35 @@ def permanently_delete(request, message_id, success_url=None): The message is permanently deleted if both users permanently deleted it or if it was deleted more than MESSAGES_DELETED_MAX_AGE days ago. """ + message = get_object_or_404(Message, id=message_id) if success_url is None: success_url = reverse('messages_inbox') if 'next' in request.GET: success_url = request.GET['next'] - if permanently_delete_message(request.user, message_id): + if permanently_delete_message(request.user, message): messages.info(request, _(u"Message permanently deleted.")) if notification: notification.send([user], "messages_permanently_deleted") return HttpResponseRedirect(success_url) raise Http404 +@login_required +def empty_trash(request, success_url=None): + """ + Empties a user's trash. + """ + if success_url is None: + success_url = reverse('messages_inbox') + if 'next' in request.GET: + success_url = request.GET['next'] + message_list = Message.objects.trash_for(request.user) + for message in message_list.all(): + permanently_delete_message(request.user, message) + messages.info(request, _(u"Trash is now empty.")) + if notification: + notification.send([user], "trash_empty") + return HttpResponseRedirect(success_url) + @login_required def view(request, message_id, form_class=ComposeForm, quote_helper=format_quote, subject_template=_(u"Re: %(subject)s"),
{% trans "Sender" %}{% trans "Subject" %}{% trans "Date" %}{% trans "Action" %}