mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-12 21:30:39 +00:00
Add notification when comment a file
This commit is contained in:
@@ -14,6 +14,8 @@ from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.utils import api_error, user_to_dict
|
||||
from seahub.avatar.settings import AVATAR_DEFAULT_SIZE
|
||||
from seahub.base.models import FileComment
|
||||
from seahub.utils.repo import get_repo_owner, get_repo_shared_users
|
||||
from seahub.signals import comment_file_successful
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -77,6 +79,19 @@ class FileCommentsView(APIView):
|
||||
username = request.user.username
|
||||
o = FileComment.objects.add_by_file_path(
|
||||
repo_id=repo_id, file_path=path, author=username, comment=comment)
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
repo_owner = get_repo_owner(request, repo.id)
|
||||
notify_users = get_repo_shared_users(repo.id, repo_owner)
|
||||
notify_users.append(repo_owner)
|
||||
notify_users = [x for x in notify_users if x != username]
|
||||
|
||||
comment_file_successful.send(sender=None,
|
||||
repo=repo,
|
||||
file_path=path,
|
||||
comment=comment,
|
||||
author=username,
|
||||
notify_users=notify_users)
|
||||
|
||||
comment = o.to_dict()
|
||||
comment.update(user_to_dict(request.user.username, request=request,
|
||||
avatar_size=avatar_size))
|
||||
|
@@ -171,6 +171,17 @@ class Command(BaseCommand):
|
||||
notice.group_name = group.group_name
|
||||
return notice
|
||||
|
||||
def format_file_comment_msg(self, notice):
|
||||
d = json.loads(notice.detail)
|
||||
repo_id = d['repo_id']
|
||||
file_path = d['file_path']
|
||||
author = d['author']
|
||||
|
||||
notice.file_url = reverse('view_lib_file', args=[repo_id, file_path])
|
||||
notice.file_name = os.path.basename(file_path)
|
||||
notice.author = author
|
||||
return notice
|
||||
|
||||
def get_user_language(self, username):
|
||||
return Profile.objects.get_user_language(username)
|
||||
|
||||
@@ -257,6 +268,9 @@ class Command(BaseCommand):
|
||||
elif notice.is_add_user_to_group():
|
||||
notice = self.format_add_user_to_group(notice)
|
||||
|
||||
elif notice.is_file_comment_msg():
|
||||
notice = self.format_file_comment_msg(notice)
|
||||
|
||||
notices.append(notice)
|
||||
|
||||
if not notices:
|
||||
|
@@ -42,6 +42,7 @@ MSG_TYPE_FILE_UPLOADED = 'file_uploaded'
|
||||
MSG_TYPE_REPO_SHARE = 'repo_share'
|
||||
MSG_TYPE_REPO_SHARE_TO_GROUP = 'repo_share_to_group'
|
||||
MSG_TYPE_USER_MESSAGE = 'user_message'
|
||||
MSG_TYPE_FILE_COMMENT = 'file_comment'
|
||||
|
||||
def file_uploaded_msg_to_json(file_name, repo_id, uploaded_to):
|
||||
"""Encode file uploaded message to json string.
|
||||
@@ -70,6 +71,12 @@ def add_user_to_group_to_json(group_staff, group_id):
|
||||
return json.dumps({'group_staff': group_staff,
|
||||
'group_id': group_id})
|
||||
|
||||
def file_comment_msg_to_json(repo_id, file_path, author, comment):
|
||||
return json.dumps({'repo_id': repo_id,
|
||||
'file_path': file_path,
|
||||
'author': author,
|
||||
'comment': comment})
|
||||
|
||||
|
||||
class UserNotificationManager(models.Manager):
|
||||
def _add_user_notification(self, to_user, msg_type, detail):
|
||||
@@ -162,7 +169,6 @@ class UserNotificationManager(models.Manager):
|
||||
except UserNotification.InvalidDetailError:
|
||||
continue
|
||||
|
||||
|
||||
def seen_user_msg_notices(self, to_user, from_user):
|
||||
"""Mark priv message notices of a user as seen.
|
||||
"""
|
||||
@@ -250,6 +256,11 @@ class UserNotificationManager(models.Manager):
|
||||
return self._add_user_notification(to_user,
|
||||
MSG_TYPE_USER_MESSAGE, detail)
|
||||
|
||||
def add_file_comment_msg(self, to_user, detail):
|
||||
"""Notify ``to_user`` that others comment a file he can access.
|
||||
"""
|
||||
return self._add_user_notification(to_user, MSG_TYPE_FILE_COMMENT, detail)
|
||||
|
||||
class UserNotification(models.Model):
|
||||
to_user = LowerCaseCharField(db_index=True, max_length=255)
|
||||
msg_type = models.CharField(db_index=True, max_length=30)
|
||||
@@ -338,6 +349,9 @@ class UserNotification(models.Model):
|
||||
"""
|
||||
return self.msg_type == MSG_TYPE_ADD_USER_TO_GROUP
|
||||
|
||||
def is_file_comment_msg(self):
|
||||
return self.msg_type == MSG_TYPE_FILE_COMMENT
|
||||
|
||||
def group_message_detail_to_dict(self):
|
||||
"""Parse group message detail, returns dict contains ``group_id`` and
|
||||
``msg_from``.
|
||||
@@ -646,11 +660,37 @@ class UserNotification(models.Model):
|
||||
}
|
||||
return msg
|
||||
|
||||
def format_file_comment_msg(self):
|
||||
try:
|
||||
d = json.loads(self.detail)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
return _(u"Internal error")
|
||||
|
||||
repo_id = d['repo_id']
|
||||
file_path = d['file_path']
|
||||
author = d['author']
|
||||
comment = d['comment']
|
||||
|
||||
repo = seafile_api.get_repo(repo_id)
|
||||
if repo is None or not seafile_api.get_file_id_by_path(repo.id,
|
||||
file_path):
|
||||
self.delete()
|
||||
return None
|
||||
|
||||
file_name = os.path.basename(file_path)
|
||||
msg = _("File <a href='%(file_url)s'>%(file_name)s</a> has a new comment from user %(author)s") % {
|
||||
'file_url': reverse('view_lib_file', args=[repo_id, file_path]),
|
||||
'file_name': file_name,
|
||||
'author': escape(email2nickname(author)),
|
||||
}
|
||||
return msg
|
||||
|
||||
########## handle signals
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.dispatch import receiver
|
||||
|
||||
from seahub.signals import upload_file_successful
|
||||
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.share.signals import share_repo_to_user_successful, \
|
||||
share_repo_to_group_successful
|
||||
@@ -751,3 +791,15 @@ def add_user_to_group_cb(sender, **kwargs):
|
||||
|
||||
UserNotification.objects.set_add_user_to_group_notice(to_user=added_user,
|
||||
detail=detail)
|
||||
|
||||
@receiver(comment_file_successful)
|
||||
def comment_file_successful_cb(sender, **kwargs):
|
||||
repo = kwargs['repo']
|
||||
file_path = kwargs['file_path']
|
||||
comment = kwargs['comment']
|
||||
author = kwargs['author']
|
||||
notify_users = kwargs['notify_users']
|
||||
|
||||
for u in notify_users:
|
||||
detail = file_comment_msg_to_json(repo.id, file_path, author, comment)
|
||||
UserNotification.objects.add_file_comment_msg(u, detail)
|
||||
|
@@ -50,7 +50,10 @@ You've got {{num}} new notices on {{ site_name }}:
|
||||
{% elif notice.is_add_user_to_group %}
|
||||
<p style="line-height:1.5; margin:.2em 10px .2em 0;">{% blocktrans with user_url=notice.group_staff_profile_url user=notice.notice_from grp_url=notice.group_url grp_name=notice.group_name %}User <a href="{{url_base}}{{user_url}}">{{user}}</a> has added you to group <a href="{{url_base}}{{grp_url}}">{{grp_name}}</a>{% endblocktrans %}</p>
|
||||
|
||||
{% endif %}
|
||||
{% 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>
|
||||
|
||||
{% endif %}
|
||||
</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>
|
||||
</tr>
|
||||
|
@@ -37,6 +37,9 @@
|
||||
|
||||
{% elif notice.is_group_join_request %}
|
||||
<p class="brief">{{ notice.format_group_join_request|safe }}</p>
|
||||
{% elif notice.is_file_comment_msg %}
|
||||
<p class="brief">{{ notice.format_file_comment_msg|safe }}</p>
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ notice.timestamp|translate_seahub_time }}</td>
|
||||
|
@@ -187,6 +187,14 @@ def add_notice_from_info(notices):
|
||||
logger.error(e)
|
||||
notice.default_avatar_url = default_avatar_url
|
||||
|
||||
elif notice.is_file_comment_msg():
|
||||
try:
|
||||
d = json.loads(notice.detail)
|
||||
notice.msg_from = d['author']
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
notice.default_avatar_url = default_avatar_url
|
||||
|
||||
else:
|
||||
pass
|
||||
|
||||
|
@@ -4,3 +4,4 @@ import django.dispatch
|
||||
repo_created = django.dispatch.Signal(providing_args=["org_id", "creator", "repo_id", "repo_name"])
|
||||
repo_deleted = django.dispatch.Signal(providing_args=["org_id", "usernames", "repo_owner", "repo_id", "repo_name"])
|
||||
upload_file_successful = django.dispatch.Signal(providing_args=["repo_id", "file_path", "owner"])
|
||||
comment_file_successful = django.dispatch.Signal(providing_args=["repo", "file_path", "comment", "author", "notify_users"])
|
||||
|
@@ -39,6 +39,9 @@
|
||||
{% elif notice.is_add_user_to_group %}
|
||||
<p class="brief">{{ notice.format_add_user_to_group|safe }}</p>
|
||||
|
||||
{% elif notice.is_file_comment_msg %}
|
||||
<p class="brief">{{ notice.format_file_comment_msg|safe }}</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<p class="time">{{ notice.timestamp|translate_seahub_time }}</p>
|
||||
|
@@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
|
||||
import seaserv
|
||||
from seaserv import seafile_api
|
||||
|
||||
from seahub.utils import EMPTY_SHA1
|
||||
from seahub.utils import EMPTY_SHA1, is_org_context
|
||||
from seahub.base.accounts import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -29,3 +29,22 @@ def get_sub_repo_abbrev_origin_path(repo_name, origin_path):
|
||||
return repo_name + '/...' + abbrev_path
|
||||
else:
|
||||
return repo_name + origin_path
|
||||
|
||||
def get_repo_owner(request, repo_id):
|
||||
if is_org_context(request):
|
||||
return seafile_api.get_org_repo_owner(repo_id)
|
||||
else:
|
||||
return seafile_api.get_repo_owner(repo_id)
|
||||
|
||||
def get_repo_shared_users(repo_id, repo_owner, include_groups=True):
|
||||
"""Return a list contains users and group users. Repo owner is ommited.
|
||||
"""
|
||||
ret = []
|
||||
users = seafile_api.list_repo_shared_to(repo_owner, repo_id)
|
||||
ret += [x.user for x in users]
|
||||
if include_groups:
|
||||
for e in seafile_api.list_repo_shared_group_by_user(repo_owner, repo_id):
|
||||
g_members = seaserv.get_group_members(e.group_id)
|
||||
ret += [x.user_name for x in g_members if x.user_name != repo_owner]
|
||||
|
||||
return list(set(ret))
|
||||
|
@@ -1,8 +1,11 @@
|
||||
import json
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
import seaserv
|
||||
from seaserv import seafile_api, ccnet_api
|
||||
|
||||
from seahub.base.models import FileComment
|
||||
from seahub.notifications.models import UserNotification
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
class FileCommentsTest(BaseTestCase):
|
||||
@@ -75,3 +78,43 @@ class FileCommentsTest(BaseTestCase):
|
||||
'comment': 'new comment'
|
||||
})
|
||||
self.assertEqual(403, resp.status_code)
|
||||
|
||||
def test_can_notify_others(self):
|
||||
assert len(UserNotification.objects.all()) == 0
|
||||
|
||||
username = self.user.username
|
||||
seafile_api.share_repo(self.repo.id, username,
|
||||
self.admin.username, 'rw')
|
||||
|
||||
resp = self.client.post(self.endpoint, {
|
||||
'comment': 'new comment'
|
||||
})
|
||||
self.assertEqual(201, resp.status_code)
|
||||
|
||||
assert len(UserNotification.objects.all()) == 1
|
||||
assert UserNotification.objects.all()[0].to_user == self.admin.username
|
||||
|
||||
def test_can_notify_others_including_group(self):
|
||||
self.logout()
|
||||
self.login_as(self.tmp_user)
|
||||
|
||||
assert len(UserNotification.objects.all()) == 0
|
||||
|
||||
# share repo to tmp_user
|
||||
username = self.user.username
|
||||
seafile_api.share_repo(self.repo.id, username,
|
||||
self.tmp_user.username, 'rw')
|
||||
|
||||
# share repo to group(owner, admin)
|
||||
ccnet_api.group_add_member(self.group.id, username,
|
||||
self.admin.username)
|
||||
seafile_api.set_group_repo(self.repo.id, self.group.id,
|
||||
username, 'rw')
|
||||
|
||||
# tmp_user comment a file
|
||||
resp = self.client.post(self.endpoint, {
|
||||
'comment': 'new comment'
|
||||
})
|
||||
self.assertEqual(201, resp.status_code)
|
||||
|
||||
assert len(UserNotification.objects.all()) == 2
|
||||
|
@@ -1,7 +1,8 @@
|
||||
from django.core import mail
|
||||
from django.core.management import call_command
|
||||
|
||||
from seahub.notifications.models import UserNotification, repo_share_msg_to_json
|
||||
from seahub.notifications.models import (
|
||||
UserNotification, repo_share_msg_to_json, file_comment_msg_to_json)
|
||||
from seahub.profile.models import Profile
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
@@ -29,3 +30,16 @@ class CommandTest(BaseTestCase):
|
||||
call_command('send_notices')
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
assert mail.outbox[0].to[0] == 'contact@foo.com'
|
||||
|
||||
def test_send_file_comment_notice(self):
|
||||
self.assertEqual(len(mail.outbox), 0)
|
||||
|
||||
detail = file_comment_msg_to_json(self.repo.id, '/foo',
|
||||
self.user.username, 'test comment')
|
||||
UserNotification.objects.add_file_comment_msg('a@a.com', detail)
|
||||
|
||||
call_command('send_notices')
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
assert mail.outbox[0].to[0] == 'a@a.com'
|
||||
assert 'new comment from user %s' % self.user.username in mail.outbox[0].body
|
||||
assert '/foo' in mail.outbox[0].body
|
||||
|
14
tests/seahub/notifications/test_models.py
Normal file
14
tests/seahub/notifications/test_models.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from seahub.notifications.models import (
|
||||
UserNotification, repo_share_msg_to_json, file_comment_msg_to_json)
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
|
||||
class UserNotificationTest(BaseTestCase):
|
||||
def test_format_file_comment_msg(self):
|
||||
detail = file_comment_msg_to_json(self.repo.id, self.file,
|
||||
self.user.username, 'test comment')
|
||||
notice = UserNotification.objects.add_file_comment_msg('a@a.com', detail)
|
||||
|
||||
msg = notice.format_file_comment_msg()
|
||||
assert msg is not None
|
||||
assert 'new comment from user' in msg
|
39
tests/seahub/utils/test_repo.py
Normal file
39
tests/seahub/utils/test_repo.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from seahub.utils.repo import get_repo_shared_users, get_repo_owner
|
||||
from seahub.test_utils import BaseTestCase
|
||||
|
||||
import seaserv
|
||||
from seaserv import seafile_api, ccnet_api
|
||||
|
||||
|
||||
class GetRepoOwnerTest(BaseTestCase):
|
||||
def test_can_get(self):
|
||||
assert get_repo_owner(self.fake_request, self.repo.id) == self.user.username
|
||||
|
||||
class GetRepoSharedUsersTest(BaseTestCase):
|
||||
def setUp(self):
|
||||
self.user2 = self.create_user()
|
||||
ccnet_api.group_add_member(self.group.id, self.user.username,
|
||||
self.user2.username)
|
||||
g_members = [x.user_name for x in seaserv.get_group_members(self.group.id)]
|
||||
assert self.user2.username in g_members
|
||||
|
||||
def tearDown(self):
|
||||
self.remove_user(self.user2.username)
|
||||
self.remove_group()
|
||||
|
||||
def test_can_get(self):
|
||||
username = self.user.username
|
||||
|
||||
owner = get_repo_owner(self.fake_request, self.repo.id)
|
||||
assert owner == username
|
||||
assert get_repo_shared_users(self.repo.id, owner) == []
|
||||
|
||||
# user share a repo to admin
|
||||
seafile_api.share_repo(self.repo.id, username,
|
||||
self.admin.username, 'rw')
|
||||
assert get_repo_shared_users(self.repo.id, owner) == [self.admin.username]
|
||||
|
||||
# user share a repo to group
|
||||
seafile_api.set_group_repo(self.repo.id, self.group.id,
|
||||
username, 'rw')
|
||||
assert get_repo_shared_users(self.repo.id, owner) == [self.admin.username, self.user2.username]
|
Reference in New Issue
Block a user