summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/media/js/group_messaging.js53
-rw-r--r--askbot/templates/group_messaging/email_alert.html13
-rw-r--r--askbot/templates/group_messaging/senders_list.html7
-rw-r--r--askbot/templates/group_messaging/threads_list.html5
-rw-r--r--askbot/templates/user_inbox/base.html4
-rw-r--r--askbot/templates/user_inbox/messages.html11
-rw-r--r--group_messaging/models.py124
-rw-r--r--group_messaging/tests.py167
-rw-r--r--group_messaging/urls.py5
-rw-r--r--group_messaging/views.py66
10 files changed, 402 insertions, 53 deletions
diff --git a/askbot/media/js/group_messaging.js b/askbot/media/js/group_messaging.js
index 2522f72e..08d9056e 100644
--- a/askbot/media/js/group_messaging.js
+++ b/askbot/media/js/group_messaging.js
@@ -331,6 +331,14 @@ var ThreadHeading = function() {
};
inherits(ThreadHeading, SimpleControl);
+ThreadHeading.prototype.setParent = function(elem) {
+ this._threadsList = elem;
+};
+
+ThreadHeading.prototype.getParent = function() {
+ return this._threadsList;
+};
+
ThreadHeading.prototype.getId = function() {
return this._id;
};
@@ -338,6 +346,12 @@ ThreadHeading.prototype.getId = function() {
ThreadHeading.prototype.decorate = function(element) {
this._element = element;
this._id = element.data('threadId');
+ var deleter = element.find('.delete-or-restore');
+ var me = this;
+ setupButtonEventHandlers($(deleter), function() {
+ me.getParent().deleteOrRestoreThread(me.getId());
+ return false;
+ });
};
/**
@@ -359,12 +373,9 @@ ThreadsList.prototype.getOpenThreadHandler = function(threadId) {
};
};
-ThreadsList.prototype.setHTML = function(html) {
- $.each(this._threads, function(idx, thread) {
- thread.dispose();
- });
- this._element.html(html);
- this.decorate(this._element);
+ThreadsList.prototype.deleteOrRestoreThread = function(threadId) {
+ var ctr = this._messageCenter;
+ ctr.deleteOrRestoreThread(threadId, this._senderId);
};
ThreadsList.prototype.decorate = function(element) {
@@ -374,12 +385,14 @@ ThreadsList.prototype.decorate = function(element) {
var threads = [];
$.each(headingElements, function(idx, headingElement) {
var heading = new ThreadHeading();
+ heading.setParent(me);
heading.decorate($(headingElement));
var threadId = heading.getId();
heading.setHandler(me.getOpenThreadHandler(threadId));
threads.push(heading);
});
this._threads = threads;
+ this._senderId = element.data('senderId');
}
@@ -604,25 +617,43 @@ MessageCenter.prototype.openThread = function(threadId) {
});
};
-MessageCenter.prototype.loadThreadsForSender = function(senderId) {
+MessageCenter.prototype.setThreadsList = function(list) {
+ this._threadsList = list;
+ this._secondCol.prepend(list.getElement());
+};
+
+MessageCenter.prototype.hitThreadsList = function(url, senderId, requestMethod) {
var threadsList = this._threadsList;
- var url = this._urls['getThreads'];
- me = this;
+ var me = this;
$.ajax({
- type: 'GET',
+ type: requestMethod,
dataType: 'json',
url: url,
cache: false,
data: {sender_id: senderId},
success: function(data) {
if (data['success']) {
- threadsList.setHTML(data['html']);
+ threadsList.dispose();
+ var threads = new ThreadsList();
+ threads.setMessageCenter(me);
+ threads.decorate($(data['html']));
+ me.setThreadsList(threads);
me.setState('show-list');
}
}
});
};
+MessageCenter.prototype.deleteOrRestoreThread = function(threadId, senderId) {
+ var url = this._urls['getThreads'] + threadId + '/delete-or-restore/';
+ this.hitThreadsList(url, senderId, 'POST');
+};
+
+MessageCenter.prototype.loadThreadsForSender = function(senderId) {
+ var url = this._urls['getThreads'];
+ this.hitThreadsList(url, senderId, 'GET');
+};
+
MessageCenter.prototype.decorate = function(element) {
this._element = element;
this._firstCol = element.find('.first-col');
diff --git a/askbot/templates/group_messaging/email_alert.html b/askbot/templates/group_messaging/email_alert.html
new file mode 100644
index 00000000..90fea52b
--- /dev/null
+++ b/askbot/templates/group_messaging/email_alert.html
@@ -0,0 +1,13 @@
+{% extends "email/base_mail.html"%}
+{% from "email/macros.html" import start_quote, end_quote %}
+{% set level = 0 %}
+{% for message in messages %}
+ {{ start_quote(level) }}
+ <p>{% trans author=message.sender %}{{ author }} wrote:{% endtrans %}</p>
+ {{ message.html|escape }}
+ {{ end_quote(level) }}
+ {% set level = level + 1 %}
+{% endfor %}
+{% block footer %}
+{% include "email/footer.html" %}
+{% endblock %}
diff --git a/askbot/templates/group_messaging/senders_list.html b/askbot/templates/group_messaging/senders_list.html
index 687cacd6..4bee2626 100644
--- a/askbot/templates/group_messaging/senders_list.html
+++ b/askbot/templates/group_messaging/senders_list.html
@@ -1,8 +1,3 @@
-{#<ul class="mailboxes">
- <li><a class="inbox selected">{% trans %}Inbox{% endtrans %}</a></li>
- <li><a class="sent">{% trans %}Sent{% endtrans %}</a></li>
- <li><a class="trash">{% trans %}Trash{% endtrans %}</a></li>
-</ul>#}
{% if senders %}
<ul class="senders-list">
<li>{% trans %}Messages by sender:{% endtrans %}</li>
@@ -10,5 +5,7 @@
{% for sender in senders %}
<li><a data-sender-id="{{ sender.id }}">{{ sender.username|escape }}</a></li>
{% endfor %}
+ {# -2 is deleted messages #}
+ <li><a class="trash" data-sender-id="-2">{% trans %}trash{% endtrans %}</a></li>
</ul>
{% endif %}
diff --git a/askbot/templates/group_messaging/threads_list.html b/askbot/templates/group_messaging/threads_list.html
index bc0af802..43492402 100644
--- a/askbot/templates/group_messaging/threads_list.html
+++ b/askbot/templates/group_messaging/threads_list.html
@@ -1,10 +1,13 @@
-<table class="threads-list">
+<table class="threads-list {% if sender_id == -2 %}trash{% endif %}"
+ data-sender-id="{{ sender_id }}"
+>
{% if threads %}
{% for thread in threads %}
{% set thread_data = threads_data[thread.id] %}
<tr class="thread-heading {{ thread_data['status'] }}"
data-thread-id="{{ thread.id }}"
>
+ <td class="delete-or-restore"></td>
<td class="senders">{{ thread_data['senders_info']|escape }}</td>
<td class="subject">{{ thread.headline|escape }}</td>
<td class="timestamp">{{ thread.last_active_at|timesince }}</td>
diff --git a/askbot/templates/user_inbox/base.html b/askbot/templates/user_inbox/base.html
index 8beababc..890cb0f7 100644
--- a/askbot/templates/user_inbox/base.html
+++ b/askbot/templates/user_inbox/base.html
@@ -13,10 +13,10 @@
<div id="re_sections">
{% trans %}Sections:{% endtrans %}
{% set sep = joiner('|') %}
- {#{ sep() }}
+ {{ sep() }}
<a href="{{request.user.get_absolute_url()}}?sort=inbox&section=messages"
{% if inbox_section == 'messages' %}class="on"{% endif %}
- >{% trans %}messages{% endtrans %}</a>#}
+ >{% trans %}messages{% endtrans %}</a>
{% if re_count > 0 %}{{ sep() }}
<a href="{{request.user.get_absolute_url()}}?sort=inbox&section=forum"
{% if inbox_section == 'forum' %}class="on"{% endif %}
diff --git a/askbot/templates/user_inbox/messages.html b/askbot/templates/user_inbox/messages.html
index 5108d15e..8c731401 100644
--- a/askbot/templates/user_inbox/messages.html
+++ b/askbot/templates/user_inbox/messages.html
@@ -22,6 +22,7 @@
}
table.threads-list {
width: 100%;
+ border-spacing: 0px;
}
.threads-list tr {
height: 2em;
@@ -36,6 +37,16 @@
.threads-list tr:hover {
background-color: #eff5f6;
}
+ .threads-list td.delete-or-restore {
+ width: 15px;
+ background-image: none;
+ }
+ .threads-list tr:hover td.delete-or-restore {
+ background: url({{"/images/delete.png"|media}}) no-repeat center center;
+ }
+ .threads-list.trash tr:hover td.delete-or-restore {
+ background-image: url({{"/images/delete.png"|media}});
+ }
td.empty {
line-height: 30px;
vertical-align: middle;
diff --git a/group_messaging/models.py b/group_messaging/models.py
index 62f720cf..12fe3620 100644
--- a/group_messaging/models.py
+++ b/group_messaging/models.py
@@ -1,12 +1,16 @@
"""models for the ``group_messaging`` app
"""
import datetime
+from coffin.template.loader import get_template
from django.db import models
+from django.db.models import signals
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
+from django.utils.translation import ugettext as _
-MAX_TITLE_LENGTH = 80
+MAX_HEADLINE_LENGTH = 80
MAX_SENDERS_INFO_LENGTH = 64
+MAX_SUBJECT_LINE_LENGTH = 30
#dummy parse message function
parse_message = lambda v: v
@@ -87,7 +91,7 @@ class MessageMemo(models.Model):
(ARCHIVED, 'archived')
)
user = models.ForeignKey(User)
- message = models.ForeignKey('Message')
+ message = models.ForeignKey('Message', related_name='memos')
status = models.SmallIntegerField(
choices=STATUS_CHOICES, default=SEEN
)
@@ -99,13 +103,37 @@ class MessageMemo(models.Model):
class MessageManager(models.Manager):
"""model manager for the :class:`Message`"""
- def get_threads_for_user(self, user):
- user_groups = user.groups.all()
- return self.filter(
- root=None,
- message_type=Message.STORED,
- recipients__in=user_groups
- )
+ def get_threads(self, recipient=None, sender=None, deleted=False):
+ user_groups = recipient.groups.all()
+ user_thread_filter = models.Q(
+ root=None,
+ message_type=Message.STORED,
+ recipients__in=user_groups
+ )
+
+ filter = user_thread_filter
+ if sender:
+ filter = filter & models.Q(sender=sender)
+
+ if deleted:
+ deleted_filter = models.Q(
+ memos__status=MessageMemo.ARCHIVED,
+ memos__user=recipient
+ )
+ return self.filter(filter & deleted_filter)
+ else:
+ #rather a tricky query (may need to change the idea to get rid of this)
+ #select threads that have a memo for the user, but the memo is not ARCHIVED
+ #in addition, select threads that have zero memos for the user
+ marked_as_non_deleted_filter = models.Q(
+ memos__status=MessageMemo.SEEN,
+ memos__user=recipient
+ )
+ #part1 - marked as non-archived
+ part1 = self.filter(filter & marked_as_non_deleted_filter)
+ #part2 - messages for the user without an attached memo
+ part2 = self.filter(filter & ~models.Q(memos__user=recipient))
+ return (part1 | part2).distinct()
def create(self, **kwargs):
"""creates a message"""
@@ -120,7 +148,7 @@ class MessageManager(models.Manager):
kwargs['root'] = root
headline = kwargs.get('headline', kwargs['text'])
- kwargs['headline'] = headline[:MAX_TITLE_LENGTH]
+ kwargs['headline'] = headline[:MAX_HEADLINE_LENGTH]
kwargs['html'] = parse_message(kwargs['text'])
message = super(MessageManager, self).create(**kwargs)
@@ -143,6 +171,7 @@ class MessageManager(models.Manager):
text=text,
)
message.add_recipients(recipients)
+ message.send_email_alert()
return message
def create_response(self, sender=None, text=None, parent=None):
@@ -160,10 +189,15 @@ class MessageManager(models.Manager):
message.add_recipients(recipients)
#add author of the parent as a recipient to parent
parent.add_recipients([senders_group])
+ #update headline
+ message.root.headline = text[:MAX_HEADLINE_LENGTH]
#mark last active timestamp for the root message
- #so that we know that this thread was most recently
- #updated
- message.update_root_info()
+ message.root.last_active_at = datetime.datetime.now()
+ #update senders info - stuff that is shown in the thread heading
+ message.root.update_senders_info()
+ #unarchive the thread for all recipients
+ message.root.unarchive()
+ message.send_email_alert()
return message
@@ -205,7 +239,7 @@ class Message(models.Model):
blank=True, related_name='children'
)
- headline = models.CharField(max_length=MAX_TITLE_LENGTH)
+ headline = models.CharField(max_length=MAX_HEADLINE_LENGTH)
text = models.TextField(
null=True, blank=True,
@@ -233,17 +267,63 @@ class Message(models.Model):
sender_list, created = SenderList.objects.get_or_create(recipient=recipient)
sender_list.senders.add(self.sender)
- def update_root_info(self):
- """Update the last active at timestamp and
- the contributors info, if relevant.
- Root object will be saved to the database.
+ def get_email_subject_line(self):
+ """forms subject line based on the root message
+ and prepends 'Re': if message is non-root
+ """
+ subject = self.get_root_message().text[:MAX_SUBJECT_LINE_LENGTH]
+ if self.root:
+ subject = _('Re: ') + subject
+ return subject
+
+ def get_root_message(self):
+ """returns root message or self
+ if current message is root
+ """
+ return self.root or self
+
+ def get_recipients_users(self):
+ """returns query set of users"""
+ groups = self.recipients.all()
+ return User.objects.filter(
+ groups__in=groups
+ ).exclude(
+ id=self.sender.id
+ ).distinct()
+
+ def get_timeline(self):
+ """returns ordered query set of messages in the thread
+ with the newest first"""
+ root = self.get_root_message()
+ root_qs = Message.objects.filter(id=root.id)
+ return (root.descendants.all() | root_qs).order_by('-sent_at')
+
+
+ def send_email_alert(self):
+ """signal handler for the message post-save"""
+ subject = self.get_email_subject_line()
+ template = get_template('group_messaging/email_alert.html')
+ data = {'messages': self.get_timeline()}
+ body_text = template.render(data)
+ recipients = map(lambda v: v.email, self.get_recipients_users())
+ from askbot.mail import send_mail
+ send_mail(recipient_list=recipients, subject_line=subject, body_text=body_text)
+
+
+ def update_senders_info(self):
+ """update the contributors info,
+ meant to be used on a root message only
"""
- self.root.last_active_at = datetime.datetime.now()
- senders_names = self.root.senders_info.split(',')
+ senders_names = self.senders_info.split(',')
if self.sender.username in senders_names:
senders_names.remove(self.sender.username)
senders_names.insert(0, self.sender.username)
- self.root.senders_info = (','.join(senders_names))[:64]
- self.root.save()
+ self.senders_info = (','.join(senders_names))[:64]
+ self.save()
+
+ def unarchive(self):
+ """unarchive message for all recipients"""
+ memos = self.memos.filter(status=MessageMemo.ARCHIVED)
+ memos.update(status=MessageMemo.SEEN)
diff --git a/group_messaging/tests.py b/group_messaging/tests.py
index c8401dc1..9cc69fb8 100644
--- a/group_messaging/tests.py
+++ b/group_messaging/tests.py
@@ -3,8 +3,12 @@ from django.contrib.auth.models import User, Group
from group_messaging.models import Message
from group_messaging.models import MessageMemo
from group_messaging.models import SenderList
+from group_messaging.models import LastVisitTime
from group_messaging.models import get_personal_group
from group_messaging.models import create_personal_group
+from group_messaging.views import ThreadsList
+from mock import Mock
+import time
MESSAGE_TEXT = 'test message text'
@@ -39,6 +43,32 @@ class ModelTests(TestCase):
group = get_personal_group(user)
return self.create_thread([group])
+
+ def get_view_context(self, view_class, data=None, user=None, method='GET'):
+ spec = ['REQUEST', 'user']
+ assert(method in ('GET', 'POST'))
+ spec.append(method)
+ request = Mock(spec=spec)
+ request.REQUEST = data
+ setattr(request, method, data)
+ request.user = user
+ return view_class().get_context(request)
+
+ def setup_three_message_thread(self):
+ """talk in this order: sender, recipient, sender"""
+ root_message = self.create_thread_for_user(self.recipient)
+ response = Message.objects.create_response(
+ sender=self.recipient,
+ text='some response',
+ parent=root_message
+ )
+ response2 = Message.objects.create_response(
+ sender=self.sender,
+ text='some response2',
+ parent=response
+ )
+ return root_message, response, response2
+
def test_create_thread_for_user(self):
"""the basic create thread with one recipient
tests that the recipient is there"""
@@ -100,11 +130,11 @@ class ModelTests(TestCase):
expected_recipients = set([sender_group, recipient_group])
self.assertEqual(recipients, expected_recipients)
- def test_get_threads_for_user(self):
+ def test_get_threads(self):
root_message = self.create_thread_for_user(self.recipient)
- threads = set(Message.objects.get_threads_for_user(self.sender))
+ threads = set(Message.objects.get_threads(recipient=self.sender))
self.assertEqual(threads, set([]))
- threads = set(Message.objects.get_threads_for_user(self.recipient))
+ threads = set(Message.objects.get_threads(recipient=self.recipient))
self.assertEqual(threads, set([root_message]))
response = Message.objects.create_response(
@@ -112,7 +142,134 @@ class ModelTests(TestCase):
text='some response',
parent=root_message
)
- threads = set(Message.objects.get_threads_for_user(self.sender))
+ threads = set(Message.objects.get_threads(recipient=self.sender))
self.assertEqual(threads, set([root_message]))
- threads = set(Message.objects.get_threads_for_user(self.recipient))
+ threads = set(Message.objects.get_threads(recipient=self.recipient))
self.assertEqual(threads, set([root_message]))
+
+ def test_answer_to_deleted_thread_undeletes_thread(self):
+ #setup: message, reply, responder deletes thread
+ root_message = self.create_thread_for_user(self.recipient)
+ response = Message.objects.create_response(
+ sender=self.recipient,
+ text='some response',
+ parent=root_message
+ )
+ memo1, created = MessageMemo.objects.get_or_create(
+ message=root_message,
+ user=self.recipient,
+ status=MessageMemo.ARCHIVED
+ )
+ #OP sends reply to reply
+ response2 = Message.objects.create_response(
+ sender=self.sender,
+ text='some response2',
+ parent=response
+ )
+
+ context = self.get_view_context(
+ ThreadsList,
+ data={'sender_id': '-1'},
+ user=self.recipient
+ )
+
+ self.assertEqual(len(context['threads']), 1)
+ thread_id = context['threads'][0].id
+ thread_data = context['threads_data'][thread_id]
+ self.assertEqual(thread_data['status'], 'new')
+
+ def test_deleting_thread_is_user_specific(self):
+ """when one user deletes thread, that same thread
+ should not end up deleted by another user
+ """
+ root, response, response2 = self.setup_three_message_thread()
+
+ threads = Message.objects.get_threads(recipient=self.sender)
+ self.assertEquals(threads.count(), 1)
+ threads = Message.objects.get_threads(recipient=self.recipient)
+ self.assertEquals(threads.count(), 1)
+
+ memo1, created = MessageMemo.objects.get_or_create(
+ message=root,
+ user=self.recipient,
+ status=MessageMemo.ARCHIVED
+ )
+
+ threads = Message.objects.get_threads(recipient=self.sender)
+ self.assertEquals(threads.count(), 1)
+ threads = Message.objects.get_threads(recipient=self.recipient)
+ self.assertEquals(threads.count(), 0)
+ threads = Message.objects.get_threads(
+ recipient=self.recipient, deleted=True
+ )
+ self.assertEquals(threads.count(), 1)
+
+ def test_user_specific_inboxes(self):
+ self.create_thread_for_user(self.recipient)
+
+ threads = Message.objects.get_threads(
+ recipient=self.recipient, sender=self.sender
+ )
+ self.assertEqual(threads.count(), 1)
+ threads = Message.objects.get_threads(
+ recipient=self.sender, sender=self.recipient
+ )
+ self.assertEqual(threads.count(), 0)
+
+ def test_new_response_marks_thread_heading_as_new(self):
+ root = self.create_thread_for_user(self.recipient)
+ response = Message.objects.create_response(
+ sender=self.recipient,
+ text='some response',
+ parent=root
+ )
+ #response must show as "new" to the self.sender
+ context = self.get_view_context(
+ ThreadsList,
+ data={'sender_id': '-1'},
+ user=self.sender
+ )
+ self.assertEqual(context['threads_data'][root.id]['status'], 'new')
+ #"visit" the thread
+ last_visit_time = LastVisitTime.objects.create(
+ user=self.sender,
+ message=root
+ )
+ time.sleep(1.5)
+
+ #response must show as "seen"
+ context = self.get_view_context(
+ ThreadsList,
+ data={'sender_id': '-1'},
+ user=self.sender
+ )
+ self.assertEqual(context['threads_data'][root.id]['status'], 'seen')
+ #self.recipient makes another response
+ response = Message.objects.create_response(
+ sender=self.recipient,
+ text='some response',
+ parent=response
+ )
+ #thread must be "new" again
+ context = self.get_view_context(
+ ThreadsList,
+ data={'sender_id': '-1'},
+ user=self.sender
+ )
+ self.assertEqual(context['threads_data'][root.id]['status'], 'new')
+
+ def test_response_updates_thread_headline(self):
+ root = self.create_thread_for_user(self.recipient)
+ response = Message.objects.create_response(
+ sender=self.recipient,
+ text='some response',
+ parent=root
+ )
+ self.assertEqual(root.headline, 'some response')
+
+ def test_email_alert_sent(self):
+ root = self.create_thread_for_user(self.recipient)
+ from django.core.mail import outbox
+ self.assertEqual(len(outbox), 1)
+ self.assertEqual(len(outbox[0].recipients()), 1)
+ self.assertEqual(outbox[0].recipients()[0], self.recipient.email)
diff --git a/group_messaging/urls.py b/group_messaging/urls.py
index 30002bf3..19ee35bb 100644
--- a/group_messaging/urls.py
+++ b/group_messaging/urls.py
@@ -15,6 +15,11 @@ urlpatterns = patterns('',
name='thread_details'
),
url(
+ '^threads/(?P<thread_id>\d+)/delete-or-restore/$',
+ views.DeleteOrRestoreThread().as_view(),
+ name='delete_or_restore_thread'
+ ),
+ url(
'^threads/create/$',
views.NewThread().as_view(),
name='create_thread'
diff --git a/group_messaging/views.py b/group_messaging/views.py
index 289961ff..3d973dbe 100644
--- a/group_messaging/views.py
+++ b/group_messaging/views.py
@@ -18,6 +18,7 @@ from django.http import HttpResponseNotAllowed
from django.http import HttpResponseForbidden
from django.utils import simplejson
from group_messaging.models import Message
+from group_messaging.models import MessageMemo
from group_messaging.models import SenderList
from group_messaging.models import LastVisitTime
from group_messaging.models import get_personal_group_by_user_id
@@ -139,6 +140,7 @@ class PostReply(InboxView):
template_name='group_messaging/stored_message.html'
)
+
class ThreadsList(InboxView):
"""shows list of threads for a given user"""
template_name = 'group_messaging/threads_list.html'
@@ -147,11 +149,22 @@ class ThreadsList(InboxView):
def get_context(self, request):
"""returns thread list data"""
#get threads and the last visit time
- threads = Message.objects.get_threads_for_user(request.user)
+ sender_id = IntegerField().clean(request.REQUEST.get('sender_id', '-1'))
+ if sender_id == -2:
+ threads = Message.objects.get_threads(
+ recipient=request.user,
+ deleted=True
+ )
+ elif sender_id == -1:
+ threads = Message.objects.get_threads(recipient=request.user)
+ else:
+ sender = User.objects.get(id=sender_id)
+ threads = Message.objects.get_threads(
+ recipient=request.user,
+ sender=sender
+ )
- sender_id = IntegerField().clean(request.GET.get('sender_id', '-1'))
- if sender_id != -1:
- threads = threads.filter(sender__id=sender_id)
+ threads = threads.order_by('-last_active_at')
#for each thread we need to know if there is something
#unread for the user - to mark "new" threads as bold
@@ -177,9 +190,48 @@ class ThreadsList(InboxView):
if thread_data['thread'].last_active_at <= last_visit.at:
thread_data['status'] = 'seen'
- #after we have all the data - update the last visit time
- last_visit_times.update(at=datetime.datetime.now())
- return {'threads': threads, 'threads_data': threads_data}
+ return {
+ 'threads': threads,
+ 'threads_data': threads_data,
+ 'sender_id': sender_id
+ }
+
+
+class DeleteOrRestoreThread(ThreadsList):
+ """subclassing :class:`ThreadsList`, because deletion
+ or restoring of thread needs subsequent refreshing
+ of the threads list"""
+
+ http_method_list = ('POST',)
+
+ def post(self, request, thread_id=None):
+ """process the post request:
+ * delete or restore thread
+ * recalculate the threads list and return it for display
+ by reusing the threads list "get" function
+ """
+ #part of the threads list context
+ sender_id = IntegerField().clean(request.POST['sender_id'])
+
+ #a little cryptic, but works - sender_id==-2 means deleted post
+ if sender_id == -2:
+ action = 'restore'
+ else:
+ action = 'delete'
+
+ thread = Message.objects.get(id=thread_id)
+ memo, created = MessageMemo.objects.get_or_create(
+ user=request.user,
+ message=thread
+ )
+ if action == 'delete':
+ memo.status = MessageMemo.ARCHIVED
+ else:
+ memo.status = MessageMemo.SEEN
+ memo.save()
+
+ context = self.get_context(request)
+ return self.render_to_response(context)
class SendersList(InboxView):