mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-19 18:29:23 +00:00
Merge pull request #1965 from haiwen/guest-login-notify
[notification] Notify inviter on guest accept invitation.
This commit is contained in:
5
seahub/invitations/signals.py
Normal file
5
seahub/invitations/signals.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||||
|
import django.dispatch
|
||||||
|
|
||||||
|
accept_guest_invitation_successful = django.dispatch.Signal(
|
||||||
|
providing_args=["invitation_obj"])
|
@@ -10,6 +10,7 @@ from seahub.auth import get_backends
|
|||||||
from seahub.base.accounts import User
|
from seahub.base.accounts import User
|
||||||
from seahub.constants import GUEST_USER
|
from seahub.constants import GUEST_USER
|
||||||
from seahub.invitations.models import Invitation
|
from seahub.invitations.models import Invitation
|
||||||
|
from seahub.invitations.signals import accept_guest_invitation_successful
|
||||||
from seahub.settings import SITE_ROOT
|
from seahub.settings import SITE_ROOT
|
||||||
|
|
||||||
def token_view(request, token):
|
def token_view(request, token):
|
||||||
@@ -38,6 +39,11 @@ def token_view(request, token):
|
|||||||
for backend in get_backends():
|
for backend in get_backends():
|
||||||
u.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
|
u.backend = "%s.%s" % (backend.__module__, backend.__class__.__name__)
|
||||||
auth_login(request, u)
|
auth_login(request, u)
|
||||||
|
|
||||||
|
# send signal to notify inviter
|
||||||
|
accept_guest_invitation_successful.send(
|
||||||
|
sender=None, invitation_obj=i)
|
||||||
|
|
||||||
return HttpResponseRedirect(SITE_ROOT)
|
return HttpResponseRedirect(SITE_ROOT)
|
||||||
|
|
||||||
return render_to_response('invitations/token_view.html', {
|
return render_to_response('invitations/token_view.html', {
|
||||||
|
@@ -20,6 +20,7 @@ import seahub.settings as settings
|
|||||||
from seahub.avatar.templatetags.avatar_tags import avatar
|
from seahub.avatar.templatetags.avatar_tags import avatar
|
||||||
from seahub.avatar.util import get_default_avatar_url
|
from seahub.avatar.util import get_default_avatar_url
|
||||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||||
|
from seahub.invitations.models import Invitation
|
||||||
from seahub.profile.models import Profile
|
from seahub.profile.models import Profile
|
||||||
|
|
||||||
# Get an instance of a logger
|
# Get an instance of a logger
|
||||||
@@ -169,6 +170,20 @@ class Command(BaseCommand):
|
|||||||
notice.author = author
|
notice.author = author
|
||||||
return notice
|
return notice
|
||||||
|
|
||||||
|
def format_guest_invitation_accepted_msg(self, notice):
|
||||||
|
d = json.loads(notice.detail)
|
||||||
|
inv_id = d['invitation_id']
|
||||||
|
try:
|
||||||
|
inv = Invitation.objects.get(pk=inv_id)
|
||||||
|
except Invitation.DoesNotExist:
|
||||||
|
self.delete()
|
||||||
|
return None
|
||||||
|
|
||||||
|
notice.inv_accepter = inv.accepter
|
||||||
|
notice.inv_url = '#invitations/'
|
||||||
|
notice.inv_accept_at = inv.accept_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
return notice
|
||||||
|
|
||||||
def get_user_language(self, username):
|
def get_user_language(self, username):
|
||||||
return Profile.objects.get_user_language(username)
|
return Profile.objects.get_user_language(username)
|
||||||
|
|
||||||
@@ -255,6 +270,12 @@ class Command(BaseCommand):
|
|||||||
elif notice.is_file_comment_msg():
|
elif notice.is_file_comment_msg():
|
||||||
notice = self.format_file_comment_msg(notice)
|
notice = self.format_file_comment_msg(notice)
|
||||||
|
|
||||||
|
elif notice.is_guest_invitation_accepted_msg():
|
||||||
|
notice = self.format_guest_invitation_accepted_msg(notice)
|
||||||
|
|
||||||
|
if notice is None:
|
||||||
|
continue
|
||||||
|
|
||||||
notices.append(notice)
|
notices.append(notice)
|
||||||
|
|
||||||
if not notices:
|
if not notices:
|
||||||
|
@@ -6,6 +6,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.conf import settings
|
||||||
from django.forms import ModelForm, Textarea
|
from django.forms import ModelForm, Textarea
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
@@ -16,8 +17,10 @@ from seaserv import seafile_api, ccnet_api
|
|||||||
|
|
||||||
from seahub.base.fields import LowerCaseCharField
|
from seahub.base.fields import LowerCaseCharField
|
||||||
from seahub.base.templatetags.seahub_tags import email2nickname
|
from seahub.base.templatetags.seahub_tags import email2nickname
|
||||||
|
from seahub.invitations.models import Invitation
|
||||||
from seahub.utils.repo import get_repo_shared_users
|
from seahub.utils.repo import get_repo_shared_users
|
||||||
from seahub.utils import normalize_cache_key
|
from seahub.utils import normalize_cache_key
|
||||||
|
from seahub.utils.timeutils import datetime_to_isoformat_timestr
|
||||||
|
|
||||||
# Get an instance of a logger
|
# Get an instance of a logger
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -48,6 +51,7 @@ MSG_TYPE_REPO_SHARE = 'repo_share'
|
|||||||
MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group'
|
MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group'
|
||||||
MSG_TYPE_USER_MESSAGE = 'user_message'
|
MSG_TYPE_USER_MESSAGE = 'user_message'
|
||||||
MSG_TYPE_FILE_COMMENT = 'file_comment'
|
MSG_TYPE_FILE_COMMENT = 'file_comment'
|
||||||
|
MSG_TYPE_GUEST_INVITATION_ACCEPTED = 'guest_invitation_accepted'
|
||||||
|
|
||||||
USER_NOTIFICATION_COUNT_CACHE_PREFIX = 'USER_NOTIFICATION_COUNT_'
|
USER_NOTIFICATION_COUNT_CACHE_PREFIX = 'USER_NOTIFICATION_COUNT_'
|
||||||
|
|
||||||
@@ -86,6 +90,9 @@ def file_comment_msg_to_json(repo_id, file_path, author, comment):
|
|||||||
'author': author,
|
'author': author,
|
||||||
'comment': comment})
|
'comment': comment})
|
||||||
|
|
||||||
|
def guest_invitation_accepted_msg_to_json(invitation_id):
|
||||||
|
return json.dumps({'invitation_id': invitation_id})
|
||||||
|
|
||||||
def get_cache_key_of_unseen_notifications(username):
|
def get_cache_key_of_unseen_notifications(username):
|
||||||
return normalize_cache_key(username,
|
return normalize_cache_key(username,
|
||||||
USER_NOTIFICATION_COUNT_CACHE_PREFIX)
|
USER_NOTIFICATION_COUNT_CACHE_PREFIX)
|
||||||
@@ -278,6 +285,13 @@ class UserNotificationManager(models.Manager):
|
|||||||
"""
|
"""
|
||||||
return self._add_user_notification(to_user, MSG_TYPE_FILE_COMMENT, detail)
|
return self._add_user_notification(to_user, MSG_TYPE_FILE_COMMENT, detail)
|
||||||
|
|
||||||
|
def add_guest_invitation_accepted_msg(self, to_user, detail):
|
||||||
|
"""Nofity ``to_user`` that a guest has accpeted an invitation.
|
||||||
|
"""
|
||||||
|
return self._add_user_notification(
|
||||||
|
to_user, MSG_TYPE_GUEST_INVITATION_ACCEPTED, detail)
|
||||||
|
|
||||||
|
|
||||||
class UserNotification(models.Model):
|
class UserNotification(models.Model):
|
||||||
to_user = LowerCaseCharField(db_index=True, max_length=255)
|
to_user = LowerCaseCharField(db_index=True, max_length=255)
|
||||||
msg_type = models.CharField(db_index=True, max_length=30)
|
msg_type = models.CharField(db_index=True, max_length=30)
|
||||||
@@ -369,6 +383,9 @@ class UserNotification(models.Model):
|
|||||||
def is_file_comment_msg(self):
|
def is_file_comment_msg(self):
|
||||||
return self.msg_type == MSG_TYPE_FILE_COMMENT
|
return self.msg_type == MSG_TYPE_FILE_COMMENT
|
||||||
|
|
||||||
|
def is_guest_invitation_accepted_msg(self):
|
||||||
|
return self.msg_type == MSG_TYPE_GUEST_INVITATION_ACCEPTED
|
||||||
|
|
||||||
def group_message_detail_to_dict(self):
|
def group_message_detail_to_dict(self):
|
||||||
"""Parse group message detail, returns dict contains ``group_id`` and
|
"""Parse group message detail, returns dict contains ``group_id`` and
|
||||||
``msg_from``.
|
``msg_from``.
|
||||||
@@ -692,6 +709,28 @@ class UserNotification(models.Model):
|
|||||||
}
|
}
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
def format_guest_invitation_accepted_msg(self):
|
||||||
|
try:
|
||||||
|
d = json.loads(self.detail)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return _(u"Internal error")
|
||||||
|
|
||||||
|
inv_id = d['invitation_id']
|
||||||
|
try:
|
||||||
|
inv = Invitation.objects.get(pk=inv_id)
|
||||||
|
except Invitation.DoesNotExist:
|
||||||
|
self.delete()
|
||||||
|
return
|
||||||
|
|
||||||
|
msg = _('Guest %(user)s accepted your <a href="%(href)s">invitation</a> at %(time)s.') % {
|
||||||
|
'user': inv.accepter,
|
||||||
|
'href': settings.SITE_ROOT + '#invitations/',
|
||||||
|
'time': inv.accept_time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
########## handle signals
|
########## handle signals
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
@@ -700,6 +739,7 @@ from seahub.signals import upload_file_successful, comment_file_successful
|
|||||||
from seahub.group.signals import grpmsg_added, group_join_request, add_user_to_group
|
from seahub.group.signals import grpmsg_added, group_join_request, add_user_to_group
|
||||||
from seahub.share.signals import share_repo_to_user_successful, \
|
from seahub.share.signals import share_repo_to_user_successful, \
|
||||||
share_repo_to_group_successful
|
share_repo_to_group_successful
|
||||||
|
from seahub.invitations.signals import accept_guest_invitation_successful
|
||||||
|
|
||||||
@receiver(upload_file_successful)
|
@receiver(upload_file_successful)
|
||||||
def add_upload_file_msg_cb(sender, **kwargs):
|
def add_upload_file_msg_cb(sender, **kwargs):
|
||||||
@@ -803,3 +843,11 @@ def comment_file_successful_cb(sender, **kwargs):
|
|||||||
for u in notify_users:
|
for u in notify_users:
|
||||||
detail = file_comment_msg_to_json(repo.id, file_path, author, comment)
|
detail = file_comment_msg_to_json(repo.id, file_path, author, comment)
|
||||||
UserNotification.objects.add_file_comment_msg(u, detail)
|
UserNotification.objects.add_file_comment_msg(u, detail)
|
||||||
|
|
||||||
|
@receiver(accept_guest_invitation_successful)
|
||||||
|
def accept_guest_invitation_successful_cb(sender, **kwargs):
|
||||||
|
inv_obj = kwargs['invitation_obj']
|
||||||
|
|
||||||
|
detail = guest_invitation_accepted_msg_to_json(inv_obj.pk)
|
||||||
|
UserNotification.objects.add_guest_invitation_accepted_msg(
|
||||||
|
inv_obj.inviter, detail)
|
||||||
|
@@ -53,6 +53,9 @@ You've got {{num}} new notices on {{ site_name }}:
|
|||||||
{% elif notice.is_file_comment_msg %}
|
{% elif notice.is_file_comment_msg %}
|
||||||
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with file_url=notice.file_url file_name=notice.file_name author=notice.author %}File <a href="{{url_base}}{{file_url}}">{{file_name}}</a> has a new comment from user {{author}}{% endblocktrans %}</p>
|
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with file_url=notice.file_url file_name=notice.file_name author=notice.author %}File <a href="{{url_base}}{{file_url}}">{{file_name}}</a> has a new comment from user {{author}}{% endblocktrans %}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_guest_invitation_accepted_msg %}
|
||||||
|
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with user=notice.inv_accepter inv_url=notice.inv_url time=notice.inv_accept_at %}Guest {{user}} accepted your <a href="{{url_base}}{{inv_url}}">invitation</a> at {{time}}.{% endblocktrans %}</p>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding: 5px 3px; border-bottom: 1px solid #eee; font-size: 13px; color: #333; word-wrap: break-word;">{{ notice.timestamp|date:"Y-m-d G:i:s"}}</td>
|
<td style="padding: 5px 3px; border-bottom: 1px solid #eee; font-size: 13px; color: #333; word-wrap: break-word;">{{ notice.timestamp|date:"Y-m-d G:i:s"}}</td>
|
||||||
|
@@ -34,6 +34,9 @@
|
|||||||
{% elif notice.is_file_comment_msg %}
|
{% elif notice.is_file_comment_msg %}
|
||||||
<p class="brief">{{ notice.format_file_comment_msg|safe }}</p>
|
<p class="brief">{{ notice.format_file_comment_msg|safe }}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_guest_invitation_accepted_msg %}
|
||||||
|
<p class="brief">{{ notice.format_guest_invitation_accepted_msg|safe }}</p>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ notice.timestamp|translate_seahub_time }}</td>
|
<td>{{ notice.timestamp|translate_seahub_time }}</td>
|
||||||
|
@@ -139,22 +139,17 @@ def add_notice_from_info(notices):
|
|||||||
'''
|
'''
|
||||||
default_avatar_url = get_default_avatar_url()
|
default_avatar_url = get_default_avatar_url()
|
||||||
for notice in notices:
|
for notice in notices:
|
||||||
|
notice.default_avatar_url = default_avatar_url
|
||||||
|
|
||||||
if notice.is_user_message():
|
if notice.is_user_message():
|
||||||
d = notice.user_message_detail_to_dict()
|
d = notice.user_message_detail_to_dict()
|
||||||
if d.get('msg_from') is not None:
|
if d.get('msg_from') is not None:
|
||||||
notice.msg_from = d.get('msg_from')
|
notice.msg_from = d.get('msg_from')
|
||||||
else:
|
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_group_msg():
|
elif notice.is_group_msg():
|
||||||
d = notice.group_message_detail_to_dict()
|
d = notice.group_message_detail_to_dict()
|
||||||
if d.get('msg_from') is not None:
|
if d.get('msg_from') is not None:
|
||||||
notice.msg_from = d.get('msg_from')
|
notice.msg_from = d.get('msg_from')
|
||||||
else:
|
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_file_uploaded_msg():
|
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_repo_share_msg():
|
elif notice.is_repo_share_msg():
|
||||||
try:
|
try:
|
||||||
@@ -162,7 +157,6 @@ def add_notice_from_info(notices):
|
|||||||
notice.msg_from = d['share_from']
|
notice.msg_from = d['share_from']
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_repo_share_to_group_msg():
|
elif notice.is_repo_share_to_group_msg():
|
||||||
try:
|
try:
|
||||||
@@ -170,7 +164,6 @@ def add_notice_from_info(notices):
|
|||||||
notice.msg_from = d['share_from']
|
notice.msg_from = d['share_from']
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_group_join_request():
|
elif notice.is_group_join_request():
|
||||||
try:
|
try:
|
||||||
@@ -178,7 +171,6 @@ def add_notice_from_info(notices):
|
|||||||
notice.msg_from = d['username']
|
notice.msg_from = d['username']
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_add_user_to_group():
|
elif notice.is_add_user_to_group():
|
||||||
try:
|
try:
|
||||||
@@ -186,7 +178,6 @@ def add_notice_from_info(notices):
|
|||||||
notice.msg_from = d['group_staff']
|
notice.msg_from = d['group_staff']
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
elif notice.is_file_comment_msg():
|
elif notice.is_file_comment_msg():
|
||||||
try:
|
try:
|
||||||
@@ -194,9 +185,5 @@ def add_notice_from_info(notices):
|
|||||||
notice.msg_from = d['author']
|
notice.msg_from = d['author']
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
notice.default_avatar_url = default_avatar_url
|
|
||||||
|
|
||||||
else:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return notices
|
return notices
|
||||||
|
@@ -37,6 +37,9 @@
|
|||||||
{% elif notice.is_file_comment_msg %}
|
{% elif notice.is_file_comment_msg %}
|
||||||
<p class="brief">{{ notice.format_file_comment_msg|safe }}</p>
|
<p class="brief">{{ notice.format_file_comment_msg|safe }}</p>
|
||||||
|
|
||||||
|
{% elif notice.is_guest_invitation_accepted_msg %}
|
||||||
|
<p class="brief">{{ notice.format_guest_invitation_accepted_msg|safe }}</p>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p class="time">{{ notice.timestamp|translate_seahub_time }}</p>
|
<p class="time">{{ notice.timestamp|translate_seahub_time }}</p>
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
|
import json
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from seahub.invitations.models import Invitation
|
from seahub.invitations.models import Invitation
|
||||||
|
from seahub.notifications.models import UserNotification
|
||||||
from seahub.test_utils import BaseTestCase
|
from seahub.test_utils import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -41,3 +43,15 @@ class TokenViewTest(BaseTestCase):
|
|||||||
})
|
})
|
||||||
self.assertEqual(302, resp.status_code)
|
self.assertEqual(302, resp.status_code)
|
||||||
assert Invitation.objects.get(pk=self.iv.pk).accept_time is None
|
assert Invitation.objects.get(pk=self.iv.pk).accept_time is None
|
||||||
|
|
||||||
|
def test_can_notify_inviter(self):
|
||||||
|
assert len(UserNotification.objects.filter(to_user=self.user.username)) == 0
|
||||||
|
resp = self.client.post(self.url, {
|
||||||
|
'password': 'passwd'
|
||||||
|
})
|
||||||
|
self.assertEqual(302, resp.status_code)
|
||||||
|
|
||||||
|
assert len(UserNotification.objects.filter(to_user=self.user.username)) == 1
|
||||||
|
obj = UserNotification.objects.all()[0]
|
||||||
|
d = json.loads(obj.detail)
|
||||||
|
assert d['invitation_id'] == self.iv.pk
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
|
|
||||||
|
from seahub.invitations.models import Invitation
|
||||||
from seahub.notifications.models import (
|
from seahub.notifications.models import (
|
||||||
UserNotification, repo_share_msg_to_json, file_comment_msg_to_json)
|
UserNotification, repo_share_msg_to_json, file_comment_msg_to_json,
|
||||||
|
guest_invitation_accepted_msg_to_json)
|
||||||
from seahub.profile.models import Profile
|
from seahub.profile.models import Profile
|
||||||
from seahub.test_utils import BaseTestCase
|
from seahub.test_utils import BaseTestCase
|
||||||
|
|
||||||
@@ -43,3 +45,18 @@ class CommandTest(BaseTestCase):
|
|||||||
assert mail.outbox[0].to[0] == 'a@a.com'
|
assert mail.outbox[0].to[0] == 'a@a.com'
|
||||||
assert 'new comment from user %s' % self.user.username in mail.outbox[0].body
|
assert 'new comment from user %s' % self.user.username in mail.outbox[0].body
|
||||||
assert '/foo' in mail.outbox[0].body
|
assert '/foo' in mail.outbox[0].body
|
||||||
|
|
||||||
|
def test_send_guest_invitation_notice(self):
|
||||||
|
self.assertEqual(len(mail.outbox), 0)
|
||||||
|
|
||||||
|
inv = Invitation.objects.add(self.user.username, 'test@test.com')
|
||||||
|
inv.accept()
|
||||||
|
|
||||||
|
detail = guest_invitation_accepted_msg_to_json(inv.pk)
|
||||||
|
UserNotification.objects.add_guest_invitation_accepted_msg(
|
||||||
|
inv.inviter, detail)
|
||||||
|
|
||||||
|
call_command('send_notices')
|
||||||
|
self.assertEqual(len(mail.outbox), 1)
|
||||||
|
assert mail.outbox[0].to[0] == self.user.username
|
||||||
|
assert 'Guest test@test.com' in mail.outbox[0].body
|
||||||
|
Reference in New Issue
Block a user