summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-05-23 23:33:37 -0400
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-05-23 23:33:37 -0400
commit1881f765d56d842b4a0d31e2432e577fa01a6447 (patch)
tree97d3dc0c96dfb5d5b86cba004dd77025e89d0a84
parent2eaa3c6a104fd5e598fc25084f8c2ecc6493ecee (diff)
downloadaskbot-1881f765d56d842b4a0d31e2432e577fa01a6447.tar.gz
askbot-1881f765d56d842b4a0d31e2432e577fa01a6447.tar.bz2
askbot-1881f765d56d842b4a0d31e2432e577fa01a6447.zip
closer to making instant notifications work
-rw-r--r--django_authopenid/views.py5
-rw-r--r--fbconnect/views.py4
-rw-r--r--forum/auth.py6
-rw-r--r--forum/const/__init__.py1
-rw-r--r--forum/models/__init__.py118
-rw-r--r--forum/models/question.py5
-rw-r--r--forum/models/signals.py17
-rw-r--r--forum/models/user.py8
-rw-r--r--forum/skins/default/templates/instant_notification.html38
-rw-r--r--forum/views/users.py3
-rw-r--r--forum_modules/authentication/auth.py4
11 files changed, 152 insertions, 57 deletions
diff --git a/django_authopenid/views.py b/django_authopenid/views.py
index 688a41fc..cce4beb5 100644
--- a/django_authopenid/views.py
+++ b/django_authopenid/views.py
@@ -75,7 +75,7 @@ EXTERNAL_LOGIN_APP = settings.LOAD_EXTERNAL_LOGIN_APP()
#todo: decouple from forum
def login(request,user):
from django.contrib.auth import login as _login
- from forum.models import user_logged_in #custom signal
+ from forum.models import signals
if settings.USE_EXTERNAL_LEGACY_LOGIN == True:
EXTERNAL_LOGIN_APP.api.login(request,user)
@@ -95,7 +95,8 @@ def login(request,user):
request.session['search_state'] = search_state
#5) send signal with old session key as argument
logging.debug('logged in user %s with session key %s' % (user.username, session_key))
- user_logged_in.send(user=user,session_key=session_key,sender=None)
+ #todo: move to auth app
+ signals.user_logged_in.send(user=user,session_key=session_key,sender=None)
#todo: uncouple this from forum
def logout(request):
diff --git a/fbconnect/views.py b/fbconnect/views.py
index 1781f6bf..91ea757a 100644
--- a/fbconnect/views.py
+++ b/fbconnect/views.py
@@ -92,8 +92,8 @@ def login_and_forward(request, user, newquestion = False, newanswer = False):
login(request, user)
logging.debug('user logged in!')
- from forum.models import user_logged_in
- user_logged_in.send(user=user,session_key=old_session,sender=None)
+ from forum.models import signals#todo: move to authentication app
+ signals.user_logged_in.send(user=user,session_key=old_session,sender=None)
logging.debug('user_logged_in signal sent')
if (newquestion):
diff --git a/forum/auth.py b/forum/auth.py
index fb3a5a68..7664a02e 100644
--- a/forum/auth.py
+++ b/forum/auth.py
@@ -10,7 +10,7 @@ from django.db import transaction
from models import Repute
from models import Question
from models import Answer
-from models import mark_offensive, delete_post_or_answer
+from models import signals
from const import TYPE_REPUTATION
import logging
@@ -213,7 +213,7 @@ def onFlaggedItem(item, post, user, timestamp=None):
#post.deleted_at = timestamp
#post.deleted_by = Admin
post.save()
- mark_offensive.send(
+ signals.mark_offensive.send(
sender=post.__class__,
instance=post,
mark_by=user
@@ -482,7 +482,7 @@ def onDeleted(post, user, timestamp=None):
elif isinstance(post, Answer):
Question.objects.update_answer_count(post.question)
logging.debug('updated answer count to %d' % post.question.answer_count)
- delete_post_or_answer.send(
+ signals.delete_post_or_answer.send(
sender=post.__class__,
instance=post,
delete_by=user
diff --git a/forum/const/__init__.py b/forum/const/__init__.py
index 3e4dedef..dba43161 100644
--- a/forum/const/__init__.py
+++ b/forum/const/__init__.py
@@ -142,6 +142,7 @@ RESPONSE_ACTIVITY_TYPES_FOR_EMAIL = (
TYPE_ACTIVITY_COMMENT_ANSWER,
TYPE_ACTIVITY_UPDATE_ANSWER,
TYPE_ACTIVITY_UPDATE_QUESTION,
+ TYPE_ACTIVITY_MENTION,
)
TYPE_RESPONSE = {
diff --git a/forum/models/__init__.py b/forum/models/__init__.py
index 1c20e74b..ebcbe6e2 100644
--- a/forum/models/__init__.py
+++ b/forum/models/__init__.py
@@ -5,9 +5,13 @@ from meta import Vote, Comment, FlaggedItem
from user import Activity, ValidationHash, EmailFeedSetting
from user import AuthKeyUserAssociation
from repute import Badge, Award, Repute
+import signals
from django.core.urlresolvers import reverse
+from django.core.mail import EmailMessage
from forum.search.indexer import create_fulltext_indexes
from django.db.models.signals import post_syncdb
+from django.template import loader, Context
+from django.utils.translation import ugettext as _
from forum import const
import logging
import re
@@ -16,15 +20,6 @@ from base import *
import datetime
from django.contrib.contenttypes.models import ContentType
-#todo: move to a separate file?
-# custom signals
-tags_updated = django.dispatch.Signal(providing_args=["question"])
-edit_question_or_answer = django.dispatch.Signal(providing_args=["instance", "modified_by"])
-delete_post_or_answer = django.dispatch.Signal(providing_args=["instance", "deleted_by"])
-mark_offensive = django.dispatch.Signal(providing_args=["instance", "mark_by"])
-user_updated = django.dispatch.Signal(providing_args=["instance", "updated_by"])
-user_logged_in = django.dispatch.Signal(providing_args=["session"])
-
#todo: must go after signals
from forum import auth
@@ -229,6 +224,14 @@ def flag_post(self, post, timestamp=None, cancel=False):
)
auth.onFlaggedItem(flag, post, user, timestamp=timestamp)
+def user_should_receive_instant_notification_about_post(user, post):
+ return EmailFeedSetting.objects.exists_match_to_post_and_subscriber(
+ subscriber = user,
+ post = post,
+ frequency = 'i',
+ )
+
+
User.add_to_class('upvote', upvote)
User.add_to_class('downvote', downvote)
User.add_to_class('accept_answer', accept_answer)
@@ -238,6 +241,10 @@ User.add_to_class('get_profile_link', get_profile_link)
User.add_to_class('get_messages', get_messages)
User.add_to_class('delete_messages', delete_messages)
User.add_to_class('toggle_favorite_question', toggle_favorite_question)
+User.add_to_class(
+ 'should_receive_instant_notification_about_post',
+ user_should_receive_instant_notification_about_post
+ )
def calculate_gravatar_hash(instance, **kwargs):
"""Calculates a User's gravatar hash from their email address."""
@@ -300,7 +307,10 @@ def record_comment_event(instance, created, **kwargs):
elif isinstance(instance.content_object, Answer):
activity_type = const.TYPE_ACTIVITY_COMMENT_ANSWER
else:
- logging.critical('recording comment for %s is not implemented' % type(instance.content_object))
+ logging.critical(
+ 'recording comment for %s is not implemented'\
+ % type(instance.content_object)
+ )
activity = Activity(
user = instance.user,
@@ -315,6 +325,8 @@ def record_comment_event(instance, created, **kwargs):
exclude_list = [instance.user],
)
activity.receiving_users.add(*receiving_users)
+ #todo: remove this upon migration to 1.2
+ signals.fake_m2m_changed.send(sender = Activity, instance = activity, created = True)
def record_revision_question_event(instance, created, **kwargs):
@@ -337,6 +349,7 @@ def record_revision_question_event(instance, created, **kwargs):
receiving_users = list(receiving_users)
activity.receiving_users.add(*receiving_users)
+
def record_revision_answer_event(instance, created, **kwargs):
if created and instance.revision <> 1:
activity = Activity(
@@ -358,6 +371,65 @@ def record_revision_answer_event(instance, created, **kwargs):
activity.receiving_users.add(*receiving_users)
+
+def maybe_send_instant_notifications(instance, created, **kwargs):
+ """todo: this handler must change when we switch to django 1.2
+ """
+ activity_instance = instance
+ if not created:
+ return
+ activity_type = activity_instance.activity_type
+ if activity_type not in const.RESPONSE_ACTIVITY_TYPES_FOR_EMAIL:
+ return
+
+ #todo: remove this after migrating to string type for const.TYPE_ACTIVITY...
+ update_type_map = {
+ const.TYPE_ACTIVITY_COMMENT_QUESTION: 'question_comment',
+ const.TYPE_ACTIVITY_COMMENT_ANSWER: 'answer_comment',
+ const.TYPE_ACTIVITY_UPDATE_ANSWER: 'answer_update',
+ const.TYPE_ACTIVITY_UPDATE_QUESTION: 'question_update',
+ const.TYPE_ACTIVITY_MENTION: 'mention',
+ }
+
+ post = activity_instance.get_response_type_content_object()
+ template = loader.get_template('instant_notification.html')
+ for u in activity_instance.receiving_users.all():
+ if u.should_receive_instant_notification_about_post(post):
+
+ mentions = Activity.objects.get_mentions(
+ mentioned_whom = u,
+ mentioned_in = post,
+ reported = False
+ )
+ if mentions:
+ #todo: find a more semantic way to do this
+ mentions.update(is_auditted = True)
+ has_mention = True
+ else:
+ has_mention = False
+
+ #get details about update
+ #todo: is there a way to solve this import issue?
+ from forum.conf import settings as forum_settings
+ data = {
+ 'receiving_user': u,
+ 'update_author': activity_instance.user,
+ 'updated_post': post,
+ 'update_url': forum_settings.APP_URL + post.get_absolute_url(),
+ 'update_type': update_type_map[activity_type],
+ 'revision_number': post.get_latest_revision_number(),
+ 'related_origin_post': post.get_origin_post(),
+ 'has_mention': has_mention,
+ }
+ #send update
+ subject = _('email update message subject')
+ text = template.render(Context(data))
+ msg = EmailMessage(subject, text, settings.DEFAULT_FROM_EMAIL, [u.email])
+ #print 'sending email to %s' % u.email
+ #print 'subject: %s' % subject
+ #print 'body: %s' % text
+ #msg.send()
+
def record_award_event(instance, created, **kwargs):
"""
After we awarded a badge to user, we need to record this activity and notify user.
@@ -542,7 +614,7 @@ def post_stored_anonymous_content(sender,user,session_key,signal,*args,**kwargs)
for aa in aa_list:
aa.publish(user)
-#signal for User modle save changes
+#signal for User model save changes
pre_save.connect(calculate_gravatar_hash, sender=User)
post_save.connect(record_ask_event, sender=Question)
post_save.connect(record_answer_event, sender=Answer)
@@ -554,17 +626,23 @@ post_save.connect(notify_award_message, sender=Award)
post_save.connect(record_answer_accepted, sender=Answer)
post_save.connect(update_last_seen, sender=Activity)
post_save.connect(record_vote, sender=Vote)
-post_delete.connect(record_cancel_vote, sender=Vote)
-delete_post_or_answer.connect(record_delete_question, sender=Question)
-delete_post_or_answer.connect(record_delete_question, sender=Answer)
-mark_offensive.connect(record_mark_offensive, sender=Question)
-mark_offensive.connect(record_mark_offensive, sender=Answer)
-tags_updated.connect(record_update_tags, sender=Question)
post_save.connect(record_favorite_question, sender=FavoriteQuestion)
-user_updated.connect(record_user_full_updated, sender=User)
-user_logged_in.connect(post_stored_anonymous_content)
+post_delete.connect(record_cancel_vote, sender=Vote)
+
+#change this to real m2m_changed with Django1.2
+signals.fake_m2m_changed.connect(maybe_send_instant_notifications, sender=Activity)
+signals.delete_post_or_answer.connect(record_delete_question, sender=Question)
+signals.delete_post_or_answer.connect(record_delete_question, sender=Answer)
+signals.mark_offensive.connect(record_mark_offensive, sender=Question)
+signals.mark_offensive.connect(record_mark_offensive, sender=Answer)
+signals.tags_updated.connect(record_update_tags, sender=Question)
+signals.user_updated.connect(record_user_full_updated, sender=User)
+signals.user_logged_in.connect(post_stored_anonymous_content)
#post_syncdb.connect(create_fulltext_indexes)
+#todo: wtf??? what is x=x about?
+signals = signals
+
Question = Question
QuestionRevision = QuestionRevision
QuestionView = QuestionView
@@ -591,6 +669,8 @@ ValidationHash = ValidationHash
AuthKeyUserAssociation = AuthKeyUserAssociation
__all__ = [
+ 'signals',
+
'Question',
'QuestionRevision',
'QuestionView',
diff --git a/forum/models/question.py b/forum/models/question.py
index c2fb6203..0793e601 100644
--- a/forum/models/question.py
+++ b/forum/models/question.py
@@ -1,4 +1,5 @@
from base import * #todo maybe remove *
+from forum.models import signals
from tag import Tag
#todo: make uniform import for consts
from forum.const import CONST
@@ -321,7 +322,7 @@ class Question(Content, DeletableContent):
self.last_activity_by = retagged_by
# Update the Question's tag associations
- tags_updated = self.objects.update_tags(
+ signals.tags_updated = self.objects.update_tags(
self,
form.cleaned_data['tags'],
request.user
@@ -339,7 +340,7 @@ class Question(Content, DeletableContent):
text = latest_revision.text
)
# send tags updated singal
- tags_updated.send(sender=question.__class__, question=self)
+ signals.tags_updated.send(sender=question.__class__, question=self)
def get_origin_post(self):
return self
diff --git a/forum/models/signals.py b/forum/models/signals.py
new file mode 100644
index 00000000..b32ffe4a
--- /dev/null
+++ b/forum/models/signals.py
@@ -0,0 +1,17 @@
+import django.dispatch
+
+tags_updated = django.dispatch.Signal(providing_args=['question'])
+
+#todo: this one seems to be unused
+edit_question_or_answer = django.dispatch.Signal(
+ providing_args=['instance', 'modified_by']
+ )
+delete_post_or_answer = django.dispatch.Signal(
+ providing_args=['instance', 'deleted_by']
+ )
+mark_offensive = django.dispatch.Signal(providing_args=['instance', 'mark_by'])
+user_updated = django.dispatch.Signal(providing_args=['instance', 'updated_by'])
+#todo: move this to authentication app
+user_logged_in = django.dispatch.Signal(providing_args=['session'])
+#todo: remove this upon migration to 1.2
+fake_m2m_changed = django.dispatch.Signal(providing_args=['instance','created'])
diff --git a/forum/models/user.py b/forum/models/user.py
index 2f4db6aa..ef709c70 100644
--- a/forum/models/user.py
+++ b/forum/models/user.py
@@ -1,5 +1,7 @@
from base import *
+#todo: remove this with Django 1.2
from django.contrib.contenttypes.models import ContentType
+from forum.models import signals
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User
from forum.models.question import Question, QuestionRevision
@@ -74,6 +76,12 @@ class ActivityManager(models.Manager):
else:
mention_activity.receiving_users.add(mentioned_whom)
+ signals.fake_m2m_changed.send(
+ sender = Activity,
+ instance = mention_activity,
+ created = True
+ )
+
return mention_activity
diff --git a/forum/skins/default/templates/instant_notification.html b/forum/skins/default/templates/instant_notification.html
index a8218c12..1bab99cf 100644
--- a/forum/skins/default/templates/instant_notification.html
+++ b/forum/skins/default/templates/instant_notification.html
@@ -16,31 +16,17 @@ has_mention - Boolean
{% blocktrans with receiving_user.get_best_name as user_name %}Dear {{user_name}},{% endblocktrans %}
{% if has_mention %}
-{% if update_type == 'question_comment' or update_type == 'answer_comment' %}
-{% blocktrans with post_author.get_profile_link as author_link and related_origin_post.get_absolute_url as origin_post_url and related_origin_post.title as origin_post_title %}
-{{author_link}} has left you a <a href="{{update_url|safe}}">comment</a>
-related to question <a href="{{origin_post_url|safe}}">{{origin_post_title}}</a>
-{% endblocktrans %}
-{% endif %}
+ {% if update_type == 'question_comment' or update_type == 'answer_comment' %}
+ {% blocktrans with post_author.get_profile_link as author_link and related_origin_post.get_absolute_url as origin_post_url and related_origin_post.title as origin_post_title %}
+ {{author_link}} has left you a <a href="{{update_url}}">comment</a>
+ related to question <a href="{{origin_post_url}}">{{origin_post_title}}</a>
+ {% endblocktrans %}
+ {% endif %}
{% else %}{# updated post has no mention of user #}
-{% if update_type == 'question_comment' or update_type == 'answer_comment' %}
-{% blocktrans with post_author.get_profile_link as author_link and related_origin_post.get_absolute_url as origin_post_url and related_origin_post.title as origin_post_title %}
-{{author_link}} has left a new <a href="{{update_url|safe}}">comment</a>
-related to question <a href="{{origin_post_url|safe}}">{{origin_post_title}}</a>
-{% endblocktrans %}
-{% if update
-{% endif %}
-{% if update_type == 'question_comment' or update_type == 'answer_comment' %}
-{% if has_mention %}
-{% blocktrans %}
-
-{% endblocktrans %}
+ {% if update_type == 'question_comment' or update_type == 'answer_comment' %}
+ {% blocktrans with post_author.get_profile_link as author_link and related_origin_post.get_absolute_url as origin_post_url and related_origin_post.title as origin_post_title %}
+ {{author_link}} has left a new <a href="{{update_url}}">comment</a>
+ related to question <a href="{{origin_post_url}}">{{origin_post_title}}</a>
+ {% endblocktrans %}
+ {% endif %}
{% endif %}
-{% endwith %}
-{% endif %}
-{% if update_type == 'answer_update' %}
-{% endif %}
-{% if update_type == 'question_update' %}
-{% endif %}
-
-{% include "email_footer.txt" %}
diff --git a/forum/views/users.py b/forum/views/users.py
index afd78ac5..dcc247bf 100644
--- a/forum/views/users.py
+++ b/forum/views/users.py
@@ -19,6 +19,7 @@ from forum import const
from django.conf import settings
from forum.conf import settings as forum_settings
from forum import models
+from forum.models import signals
question_type = ContentType.objects.get_for_model(models.Question)
answer_type = ContentType.objects.get_for_model(models.Answer)
@@ -149,7 +150,7 @@ def edit_user(request, id):
# send user updated singal if full fields have been updated
if user.email and user.real_name and user.website and user.location and \
user.date_of_birth and user.about:
- models.user_updated.send(sender=user.__class__, instance=user, updated_by=user)
+ signals.user_updated.send(sender=user.__class__, instance=user, updated_by=user)
return HttpResponseRedirect(user.get_profile_url())
else:
form = forms.EditUserForm(user)
diff --git a/forum_modules/authentication/auth.py b/forum_modules/authentication/auth.py
index 96025dc1..b46e3df3 100644
--- a/forum_modules/authentication/auth.py
+++ b/forum_modules/authentication/auth.py
@@ -110,8 +110,8 @@ def login_and_forward(request, user, forward=None, message=None):
user.backend = "django.contrib.auth.backends.ModelBackend"
login(request, user)
- from forum.models import user_logged_in
- user_logged_in.send(user=user,session_key=old_session,sender=None)
+ from forum.models import signals#todo: move to auth app
+ signals.user_logged_in.send(user=user,session_key=old_session,sender=None)
if not forward:
signin_action = request.session.get('on_signin_action', None)