1
0
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:
xiez
2018-01-09 13:23:50 +08:00
committed by GitHub
10 changed files with 124 additions and 17 deletions

View 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"])

View File

@@ -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', {

View File

@@ -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:

View File

@@ -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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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