summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-09-21 01:45:45 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2012-09-21 01:45:45 -0400
commita202578af77b8ecf66cb58a7f535481818db91e5 (patch)
tree362008396d0c9f67e4d8e306e9db29aed6580376
parent5ac09a8bae3b095bd268866285e8a0a5bd35aa51 (diff)
downloadaskbot-a202578af77b8ecf66cb58a7f535481818db91e5.tar.gz
askbot-a202578af77b8ecf66cb58a7f535481818db91e5.tar.bz2
askbot-a202578af77b8ecf66cb58a7f535481818db91e5.zip
made possibility to display threads as new/seen
-rw-r--r--askbot/media/js/group_messaging.js59
-rw-r--r--askbot/media/js/utils.js35
-rw-r--r--askbot/templates/group_messaging/threads_list.html21
-rw-r--r--askbot/templates/user_inbox/messages.html2
-rw-r--r--group_messaging/migrations/0002_auto__add_lastvisittime__add_field_message_senders_info.py134
-rw-r--r--group_messaging/models.py91
-rw-r--r--group_messaging/tests.py20
-rw-r--r--group_messaging/views.py72
8 files changed, 370 insertions, 64 deletions
diff --git a/askbot/media/js/group_messaging.js b/askbot/media/js/group_messaging.js
index 08dd305a..d9d3f0df 100644
--- a/askbot/media/js/group_messaging.js
+++ b/askbot/media/js/group_messaging.js
@@ -48,11 +48,27 @@ MessageComposer.prototype.onAfterCancel = function(handler) {
}
};
-MessageComposer.prototype.onAfterSend = function(handler) {
- if (handler) {
- this._onAfterSend = handler;
- } else {
- return this._onAfterSend();
+/** override these two
+ * @param {object} data - the response data
+ * these functions will run after .send() receives
+ * the response
+ */
+MessageComposer.prototype.onSendSuccessInternal = function(data) {};
+MessageComposer.prototype.onSendErrorInternal = function(data) {};
+
+MessageComposer.prototype.onSendSuccess = function(callback) {
+ if (callback) {
+ this._onSendSuccess = callback;
+ } else if (this._onSendSuccess) {
+ this._onSendSuccess();
+ }
+};
+
+MessageComposer.prototype.onSendError = function(callback) {
+ if (callback) {
+ this._onSendError = callback;
+ } else if (this._onSendError) {
+ this._onSendError();
}
};
@@ -105,7 +121,15 @@ MessageComposer.prototype.send = function() {
url: url,
data: data,
cache: false,
- success: function() { me.onAfterSend(); }
+ success: function(data) {
+ if (data['success']) {
+ me.onSendSuccessInternal(data);
+ me.onSendSuccess();
+ } else {
+ me.onSendErrorInternal(data);
+ me.onSendError();
+ }
+ }
});
};
@@ -130,7 +154,6 @@ MessageComposer.prototype.createDom = function() {
var sendBtn = this.makeButton(
gettext('send'),
function() {
- debugger;
if (me.dataIsValid()){
me.send();
}
@@ -167,21 +190,33 @@ NewThreadComposer.prototype.onAfterShow = function() {
this._toInput.focus();
};
+NewThreadComposer.prototype.onSendErrorInternal = function(data) {
+ var missingUsers = data['missing_users']
+ if (missingUsers) {
+ var errorTpl = ngettext(
+ 'user {{str}} does not exist',
+ 'users {{str}} do not exist',
+ missingUsers.length
+ )
+ error = errorTpl.replace('{{str}}', joinAsPhrase(missingUsers));
+ this._toInputError.html(error);
+ }
+};
+
NewThreadComposer.prototype.getInputData = function() {
var data = NewThreadComposer.superClass_.getInputData.call(this);
- data['to_username'] = $.trim(this._toInput.val());
+ data['to_usernames'] = $.trim(this._toInput.val());
return data;
};
NewThreadComposer.prototype.dataIsValid = function() {
var superIsValid = NewThreadComposer.superClass_.dataIsValid.call(this);
var to = $.trim(this._toInput.val());
- var meIsValid = true;
if (to === '') {
- meIsValid = false;
this._toInputError.html(gettext('required'));
+ return false;
}
- return superIsValid && meIsValid;
+ return superIsValid;
};
NewThreadComposer.prototype.createDom = function() {
@@ -283,7 +318,7 @@ MessageCenter.prototype.decorate = function(element) {
this._secondCol.append(editor.getElement());
editor.setSendUrl(element.data('createThreadUrl'));
editor.onAfterCancel(function() { me.setState('show-list') });
- editor.onAfterSend(function() {
+ editor.onSendSuccess(function() {
me.setState('show-list');
notify.show(gettext('message sent'), true);
});
diff --git a/askbot/media/js/utils.js b/askbot/media/js/utils.js
index 42f19d5b..f34ee876 100644
--- a/askbot/media/js/utils.js
+++ b/askbot/media/js/utils.js
@@ -29,17 +29,40 @@ var animateHashes = function(){
}
};
-var getUniqueWords = function(value){
- var words = $.trim(value).split(/\s+/);
+var getUniqueValues = function(values) {
var uniques = new Object();
var out = new Array();
- $.each(words, function(idx, item){
- if (!(item in uniques)){
- uniques[item] = 1;
- out.push(item);
+ $.each(values, function(idx, value){
+ if (!(value in uniques)){
+ uniques[value] = 1;
+ out.push(value);
};
});
return out;
+}
+
+var getUniqueWords = function(value){
+ var words = $.trim(value).split(/\s+/);
+ return getUniqueValues(words);
+};
+
+/**
+ * comma-joins items and uses "and'
+ * between the last and penultimate items
+ * @param {Array<string>} values
+ * @return {string}
+ */
+var joinAsPhrase = function(values) {
+ var count = values.length;
+ if (count === 0) {
+ return '';
+ } else if (count === 1) {
+ return values[0];
+ } else {
+ var last = values.pop();
+ var prev = values.pop();
+ return values.join(', ') + prev + gettext('and') + last;
+ }
};
var showMessage = function(element, msg, where) {
diff --git a/askbot/templates/group_messaging/threads_list.html b/askbot/templates/group_messaging/threads_list.html
index 164867a1..80df18b9 100644
--- a/askbot/templates/group_messaging/threads_list.html
+++ b/askbot/templates/group_messaging/threads_list.html
@@ -1,13 +1,18 @@
-<ul class="threads-list">
+<table class="threads-list">
{% if threads %}
{% for thread in threads %}
- <li>
- <a data-thread-id="{{ thread.id }}">
- {{ thread.headline|escape }}
- </a>
- </li>
+ {% set thread_data = threads_data[thread.id] %}
+ <tr class="{{ thread_data['status'] }}"
+ data-thread-id="{{ thread.id }}"
+ >
+ <td class="senders">{{ thread_data['senders_info']|escape }}</td>
+ <td class="subject">{{ thread.headline|escape }}</td>
+ <td class="timestamp">{{ thread.last_active_at }}</td>
+ </tr>
{% endfor %}
{% else %}
- <li class="empty">{% trans %}there are no messages yet...{% endtrans %}</li>
+ <tr class="empty">
+ <td colspan="3">{% trans %}there are no messages yet...{% endtrans %}<td>
+ </tr>
{% endif %}
-</ul>
+</table>
diff --git a/askbot/templates/user_inbox/messages.html b/askbot/templates/user_inbox/messages.html
index 8bb64c4a..9886e148 100644
--- a/askbot/templates/user_inbox/messages.html
+++ b/askbot/templates/user_inbox/messages.html
@@ -13,7 +13,7 @@
list-style-type: none;
list-style-position: outside;
}
- li.empty {
+ tr.empty {
line-height: 30px;
vertical-align: middle;
background: #eee;
diff --git a/group_messaging/migrations/0002_auto__add_lastvisittime__add_field_message_senders_info.py b/group_messaging/migrations/0002_auto__add_lastvisittime__add_field_message_senders_info.py
new file mode 100644
index 00000000..b6d82bc8
--- /dev/null
+++ b/group_messaging/migrations/0002_auto__add_lastvisittime__add_field_message_senders_info.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ # Adding model 'LastVisitTime'
+ db.create_table('group_messaging_lastvisittime', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)),
+ ('at', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+ ))
+ db.send_create_signal('group_messaging', ['LastVisitTime'])
+
+ # Adding field 'Message.senders_info'
+ db.add_column('group_messaging_message', 'senders_info',
+ self.gf('django.db.models.fields.CharField')(default='', max_length=64),
+ keep_default=False)
+
+ def backwards(self, orm):
+ # Deleting model 'LastVisitTime'
+ db.delete_table('group_messaging_lastvisittime')
+
+ # Deleting field 'Message.senders_info'
+ db.delete_column('group_messaging_message', 'senders_info')
+
+ models = {
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'about': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'n'", 'max_length': '1'}),
+ 'bronze': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'consecutive_days_visit_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'country': ('django_countries.fields.CountryField', [], {'max_length': '2', 'blank': 'True'}),
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'date_of_birth': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+ 'display_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'email_isvalid': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'email_key': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
+ 'email_signature': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'email_tag_filter_strategy': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'gold': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'gravatar': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'ignored_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'interesting_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_fake': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'last_seen': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'location': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'new_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'questions_per_page': ('django.db.models.fields.SmallIntegerField', [], {'default': '10'}),
+ 'real_name': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
+ 'reputation': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
+ 'seen_response_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+ 'show_country': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'show_marked_tags': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'silver': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'status': ('django.db.models.fields.CharField', [], {'default': "'w'", 'max_length': '2'}),
+ 'subscribed_tags': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'group_messaging.lastvisittime': {
+ 'Meta': {'object_name': 'LastVisitTime'},
+ 'at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
+ },
+ 'group_messaging.message': {
+ 'Meta': {'object_name': 'Message'},
+ 'active_until': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'headline': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
+ 'html': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'last_active_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'message_type': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'null': 'True', 'to': "orm['group_messaging.Message']"}),
+ 'recipients': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False'}),
+ 'root': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'descendants'", 'null': 'True', 'to': "orm['group_messaging.Message']"}),
+ 'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sent_messages'", 'to': "orm['auth.User']"}),
+ 'senders_info': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64'}),
+ 'sent_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+ 'text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
+ },
+ 'group_messaging.messagememo': {
+ 'Meta': {'unique_together': "(('user', 'message'),)", 'object_name': 'MessageMemo'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'message': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['group_messaging.Message']"}),
+ 'status': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'group_messaging.senderlist': {
+ 'Meta': {'object_name': 'SenderList'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'recipient': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.Group']", 'unique': 'True'}),
+ 'senders': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'})
+ }
+ }
+
+ complete_apps = ['group_messaging'] \ No newline at end of file
diff --git a/group_messaging/models.py b/group_messaging/models.py
index 838134e7..978693ae 100644
--- a/group_messaging/models.py
+++ b/group_messaging/models.py
@@ -1,14 +1,26 @@
+"""models for the ``group_messaging`` app
+"""
+import datetime
from django.db import models
from django.contrib.auth.models import Group
from django.contrib.auth.models import User
MAX_TITLE_LENGTH = 80
+MAX_SENDERS_INFO_LENGTH = 64
#dummy parse message function
parse_message = lambda v: v
+GROUP_NAME_TPL = '_personal_%s'
+
def get_personal_group_by_user_id(user_id):
- return Group.objects.get(name='_personal_%s' % user_id)
+ return Group.objects.get(name=GROUP_NAME_TPL % user_id)
+
+
+def get_personal_groups_for_users(users):
+ """for a given list of users return their personal groups"""
+ group_names = [(GROUP_NAME_TPL % user.id) for user in users]
+ return Group.objects.filter(name__in=group_names)
def get_personal_group(user):
@@ -18,11 +30,23 @@ def get_personal_group(user):
def create_personal_group(user):
"""creates a personal group for the user"""
- group = Group(name='_personal_%s' % user.id)
+ group = Group(name=GROUP_NAME_TPL % user.id)
group.save()
return group
+class LastVisitTime(models.Model):
+ """just remembers when each user last
+ visited his/her messages inbox
+ updated any time when inbox is visited by the user.
+
+ there is only one value per user - it is necessary
+ for the quick determination of which threads are "new"
+ """
+ user = models.OneToOneField(User)
+ at = models.DateTimeField(auto_now_add=True)
+
+
class SenderListManager(models.Manager):
"""model manager for the :class:`SenderList`"""
@@ -61,7 +85,6 @@ class MessageMemo(models.Model):
STATUS_CHOICES = (
(SEEN, 'seen'),
(ARCHIVED, 'archived')
-
)
user = models.ForeignKey(User)
message = models.ForeignKey('Message')
@@ -99,13 +122,24 @@ class MessageManager(models.Manager):
headline = kwargs.get('headline', kwargs['text'])
kwargs['headline'] = headline[:MAX_TITLE_LENGTH]
kwargs['html'] = parse_message(kwargs['text'])
- return super(MessageManager, self).create(**kwargs)
+
+ message = super(MessageManager, self).create(**kwargs)
+ #creator of message saw it by definition
+ #crate a "seen" memo for the sender, because we
+ #don't want to inform the user about his/her own post
+ sender = kwargs['sender']
+ MessageMemo.objects.create(
+ message=message, user=sender, status=MessageMemo.SEEN
+ )
+ return message
+
def create_thread(self, sender=None, recipients=None, text=None):
"""creates a stored message and adds recipients"""
message = self.create(
message_type=Message.STORED,
sender=sender,
+ senders_info=sender.username,
text=text,
)
message.add_recipients(recipients)
@@ -123,10 +157,13 @@ class MessageManager(models.Manager):
recipients = set(parent.recipients.all())
senders_group = get_personal_group(parent.sender)
recipients.add(senders_group)
- message.add_recipients(recipients, ignore_user=sender)
+ message.add_recipients(recipients)
#add author of the parent as a recipient to parent
- #but make sure to mute the message
- parent.add_recipients([senders_group], ignore_user=parent.sender)
+ parent.add_recipients([senders_group])
+ #mark last active timestamp for the root message
+ #so that we know that this thread was most recently
+ #updated
+ message.update_root_info()
return message
@@ -150,47 +187,63 @@ class Message(models.Model):
)
sender = models.ForeignKey(User, related_name='sent_messages')
+
+ senders_info = models.CharField(
+ max_length=MAX_SENDERS_INFO_LENGTH,
+ default=''
+ )#comma-separated list of a few names
+
recipients = models.ManyToManyField(Group)
+
root = models.ForeignKey(
'self', null=True,
blank=True, related_name='descendants'
)
+
parent = models.ForeignKey(
'self', null=True,
blank=True, related_name='children'
)
+
headline = models.CharField(max_length=MAX_TITLE_LENGTH)
+
text = models.TextField(
null=True, blank=True,
help_text='source text for the message, e.g. in markdown format'
)
+
html = models.TextField(
null=True, blank=True,
help_text='rendered html of the message'
)
+
sent_at = models.DateTimeField(auto_now_add=True)
last_active_at = models.DateTimeField(auto_now_add=True)
active_until = models.DateTimeField(blank=True, null=True)
objects = MessageManager()
- def add_recipients(self, recipients, ignore_user=None):
+ def add_recipients(self, recipients):
"""adds recipients to the message
and updates the sender lists for all recipients
todo: sender lists may be updated in a lazy way - per user
-
- `ignore_user` parameter is used to mark a specific user
- as not needing to receive a message as new, even if that
- user is a member of any of the recipient groups
"""
- if ignore_user:
- #crate a "seen" memo for the sender, because we
- #don't want to inform the user about his/her own post
- MessageMemo.objects.create(
- message=self, user=self.sender, status=MessageMemo.SEEN
- )
-
self.recipients.add(*recipients)
for recipient in recipients:
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.
+ """
+ self.root.last_active_at = datetime.datetime.now()
+ senders_names = self.root.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()
diff --git a/group_messaging/tests.py b/group_messaging/tests.py
index 80f6f792..c8401dc1 100644
--- a/group_messaging/tests.py
+++ b/group_messaging/tests.py
@@ -51,11 +51,19 @@ class ModelTests(TestCase):
#sender_group = get_personal_group(self.sender) #maybe add this too
expected_recipients = set([recipient_group])
self.assertEqual(recipients, expected_recipients)
- self.assertRaises(
- MessageMemo.DoesNotExist,
- MessageMemo.objects.get,
- message=message
- )
+ #self.assertRaises(
+ # MessageMemo.DoesNotExist,
+ # MessageMemo.objects.get,
+ # message=message
+ #)
+ #make sure that the original senders memo to the root
+ #message is marke ad seen
+ memos = MessageMemo.objects.filter(
+ message=message,
+ user=self.sender
+ )
+ self.assertEquals(memos.count(), 1)
+ self.assertEqual(memos[0].status, MessageMemo.SEEN)
def test_get_senders_for_user(self):
"""this time send thread to a real group test that
@@ -77,11 +85,13 @@ class ModelTests(TestCase):
parent=root_message
)
self.assertEqual(response.message_type, Message.STORED)
+
#assert that there is only one "seen" memo for the response
memos = MessageMemo.objects.filter(message=response)
self.assertEqual(memos.count(), 1)
self.assertEqual(memos[0].user, self.recipient)
self.assertEqual(memos[0].status, MessageMemo.SEEN)
+
#assert that recipients are the two people who are part of
#this conversation
recipients = set(response.recipients.all())
diff --git a/group_messaging/views.py b/group_messaging/views.py
index 9d324d62..8c06ff99 100644
--- a/group_messaging/views.py
+++ b/group_messaging/views.py
@@ -8,7 +8,10 @@ in order to render messages within the page.
Notice that :mod:`urls` module decorates all these functions
and turns them into complete views
"""
+import copy
+import datetime
from coffin.template.loader import get_template
+from django.contrib.auth.models import User
from django.forms import IntegerField
from django.http import HttpResponse
from django.http import HttpResponseNotAllowed
@@ -16,7 +19,9 @@ from django.http import HttpResponseForbidden
from django.utils import simplejson
from group_messaging.models import Message
from group_messaging.models import SenderList
+from group_messaging.models import LastVisitTime
from group_messaging.models import get_personal_group_by_user_id
+from group_messaging.models import get_personal_groups_for_users
class InboxView(object):
"""custom class-based view
@@ -78,8 +83,7 @@ class InboxView(object):
class NewThread(InboxView):
"""view for creation of new thread"""
- template_name = 'create_thread.html'# contains new thread form
- http_method_list = ('GET', 'POST')
+ http_method_list = ('POST',)
def post(self, request):
"""creates a new thread on behalf of the user
@@ -87,15 +91,29 @@ class NewThread(InboxView):
need to go back to the thread listing view whose
content should be cached in the client'
"""
- username = IntegerField().clean(request.POST['to_username'])
- user = User.objects.get(username=username)
- recipient = get_personal_group_by_user_id(user.id)
- Message.objects.create_thread(
- sender=request.user,
- recipients=[recipient],
- text=request.POST['text']
- )
- return HttpResponse('', mimetype='application/json')
+ usernames = request.POST['to_usernames']
+ usernames = map(lambda v: v.strip(), usernames.split(','))
+ users = User.objects.filter(username__in=usernames)
+
+ missing = copy.copy(usernames)
+ for user in users:
+ if user.username in missing:
+ missing.remove(user.username)
+
+ result = dict()
+ if missing:
+ result['success'] = False
+ result['missing_users'] = missing
+ else:
+ recipients = get_personal_groups_for_users(users)
+ message = Message.objects.create_thread(
+ sender=request.user,
+ recipients=recipients,
+ text=request.POST['text']
+ )
+ result['success'] = True
+ result['message_id'] = message.id
+ return HttpResponse(simplejson.dumps(result), mimetype='application/json')
class NewResponse(InboxView):
@@ -121,9 +139,37 @@ 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)
- threads = threads.values('id', 'headline')
- return {'threads': threads}
+ try:
+ last_visit = LastVisitTime.objects.get(user=request.user)
+ except LastVisitTime.DoesNotExist:
+ timestamp = datetime.datetime(2010, 3, 24)#day of askbot
+ last_visit = LastVisitTime(user=request.user, at=timestamp)
+
+
+ #for each thread we need to know if there is something
+ #unread for the user - to mark "new" threads as bold
+ threads_data = dict()
+ for thread in threads:
+ thread_data = dict()
+ #determine status
+ status = 'seen'
+ if thread.last_active_at > last_visit.at:
+ status = 'new'
+ thread_data['status'] = status
+ #determine the senders info
+ senders_names = thread.senders_info.split(',')
+ if request.user.username in senders_names:
+ senders_names.remove(request.user.username)
+ thread_data['senders_info'] = ', '.join(senders_names)
+ threads_data[thread.id] = thread_data
+
+ #after we have all the data - update the last visit time
+ last_visit.at = datetime.datetime.now()
+ last_visit.save()
+
+ return {'threads': threads, 'threads_data': threads_data}
class SendersList(InboxView):