mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-17 15:53:28 +00:00
[api2] Add list/post/delete group discussions
This commit is contained in:
42
seahub/api2/endpoints/group_discussion.py
Normal file
42
seahub/api2/endpoints/group_discussion.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from seaserv import ccnet_api
|
||||||
|
|
||||||
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
|
from seahub.api2.utils import api_error
|
||||||
|
from seahub.group.models import GroupMessage
|
||||||
|
from .utils import api_check_group
|
||||||
|
|
||||||
|
json_content_type = 'application/json; charset=utf-8'
|
||||||
|
|
||||||
|
class GroupDiscussion(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
@api_check_group
|
||||||
|
def delete(self, request, group_id, discuss_id, format=None):
|
||||||
|
"""Remove a group discussion.
|
||||||
|
Only discussion creator or group admin can perform this op.
|
||||||
|
"""
|
||||||
|
username = request.user.username
|
||||||
|
group_id = int(group_id)
|
||||||
|
|
||||||
|
try:
|
||||||
|
discussion = GroupMessage.objects.get(pk=discuss_id)
|
||||||
|
except GroupMessage.DoesNotExist:
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, 'Bad discussion id')
|
||||||
|
|
||||||
|
# perm check
|
||||||
|
if not ccnet_api.check_group_staff(group_id, username) and \
|
||||||
|
discussion.from_email != username:
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, 'Permission error.')
|
||||||
|
|
||||||
|
discussion.delete()
|
||||||
|
|
||||||
|
return Response(status=204)
|
88
seahub/api2/endpoints/group_discussions.py
Normal file
88
seahub/api2/endpoints/group_discussions.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.paginator import EmptyPage, InvalidPage
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.utils.dateformat import DateFormat
|
||||||
|
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.authentication import SessionAuthentication
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from seahub.api2.authentication import TokenAuthentication
|
||||||
|
from seahub.api2.permissions import IsGroupMember
|
||||||
|
from seahub.api2.throttling import UserRateThrottle
|
||||||
|
from seahub.api2.utils import api_error
|
||||||
|
from seahub.group.models import GroupMessage
|
||||||
|
from seahub.utils.paginator import Paginator
|
||||||
|
from .utils import api_check_group
|
||||||
|
|
||||||
|
json_content_type = 'application/json; charset=utf-8'
|
||||||
|
|
||||||
|
class GroupDiscussions(APIView):
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated, IsGroupMember)
|
||||||
|
throttle_classes = (UserRateThrottle, )
|
||||||
|
|
||||||
|
@api_check_group
|
||||||
|
def get(self, request, group_id, format=None):
|
||||||
|
"""List all group discussions. Only group members can perform this op.
|
||||||
|
"""
|
||||||
|
# 1 <= page, defaults to 1
|
||||||
|
try:
|
||||||
|
page = int(request.GET.get('page', '1'))
|
||||||
|
except ValueError:
|
||||||
|
page = 1
|
||||||
|
if page < 0:
|
||||||
|
page = 1
|
||||||
|
|
||||||
|
# 1 <= per_page <= 100, defaults to 20
|
||||||
|
try:
|
||||||
|
per_page = int(request.GET.get('per_page', '20'))
|
||||||
|
except ValueError:
|
||||||
|
per_page = 20
|
||||||
|
if per_page < 1 or per_page > 100:
|
||||||
|
per_page = 20
|
||||||
|
|
||||||
|
paginator = Paginator(GroupMessage.objects.filter(
|
||||||
|
group_id=group_id).order_by('-timestamp'), per_page)
|
||||||
|
|
||||||
|
try:
|
||||||
|
group_msgs = paginator.page(page)
|
||||||
|
except (EmptyPage, InvalidPage):
|
||||||
|
group_msgs = paginator.page(paginator.num_pages)
|
||||||
|
|
||||||
|
msgs = []
|
||||||
|
for e in group_msgs:
|
||||||
|
msgs.append({
|
||||||
|
"group_id": group_id,
|
||||||
|
"discussion_id": e.pk,
|
||||||
|
"user": e.from_email,
|
||||||
|
"content": e.message,
|
||||||
|
"created_at": e.timestamp.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(e.timestamp).format('O'),
|
||||||
|
})
|
||||||
|
|
||||||
|
return HttpResponse(json.dumps(msgs), status=200,
|
||||||
|
content_type=json_content_type)
|
||||||
|
|
||||||
|
@api_check_group
|
||||||
|
def post(self, request, group_id, format=None):
|
||||||
|
"""Post a group discussions. Only group members can perform this op.
|
||||||
|
"""
|
||||||
|
content = request.data.get('content', '')
|
||||||
|
if not content:
|
||||||
|
return api_error(status.HTTP_400_BAD_REQUEST, 'content can not be empty')
|
||||||
|
|
||||||
|
username = request.user.username
|
||||||
|
discuss = GroupMessage.objects.create(group_id=group_id,
|
||||||
|
from_email=username,
|
||||||
|
message=content)
|
||||||
|
|
||||||
|
return Response({
|
||||||
|
"group_id": group_id,
|
||||||
|
"discussion_id": discuss.pk,
|
||||||
|
"user": username,
|
||||||
|
"content": discuss.message,
|
||||||
|
"created_at": discuss.timestamp.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(discuss.timestamp).format('O'),
|
||||||
|
}, status=201)
|
@@ -4,7 +4,7 @@ Provides a set of pluggable permission policies.
|
|||||||
|
|
||||||
from rest_framework.permissions import BasePermission
|
from rest_framework.permissions import BasePermission
|
||||||
|
|
||||||
from seaserv import check_permission, is_repo_owner
|
from seaserv import check_permission, is_repo_owner, ccnet_api
|
||||||
|
|
||||||
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
|
SAFE_METHODS = ['GET', 'HEAD', 'OPTIONS']
|
||||||
|
|
||||||
@@ -43,4 +43,13 @@ class IsRepoOwner(BasePermission):
|
|||||||
user = request.user.username if request.user else ''
|
user = request.user.username if request.user else ''
|
||||||
|
|
||||||
return True if is_repo_owner(user, repo_id) else False
|
return True if is_repo_owner(user, repo_id) else False
|
||||||
|
|
||||||
|
|
||||||
|
class IsGroupMember(BasePermission):
|
||||||
|
"""
|
||||||
|
Check whether user is in a group.
|
||||||
|
"""
|
||||||
|
def has_permission(self, request, view, obj=None):
|
||||||
|
group_id = int(view.kwargs.get('group_id', ''))
|
||||||
|
username = request.user.username if request.user else ''
|
||||||
|
return True if ccnet_api.is_group_user(group_id, username) else False
|
||||||
|
@@ -8,6 +8,8 @@ from .endpoints.account import Account
|
|||||||
from .endpoints.shared_upload_links import SharedUploadLinksView
|
from .endpoints.shared_upload_links import SharedUploadLinksView
|
||||||
from .endpoints.be_shared_repo import BeSharedReposView
|
from .endpoints.be_shared_repo import BeSharedReposView
|
||||||
from .endpoints.search_user import SearchUser
|
from .endpoints.search_user import SearchUser
|
||||||
|
from .endpoints.group_discussions import GroupDiscussions
|
||||||
|
from .endpoints.group_discussion import GroupDiscussion
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^ping/$', Ping.as_view()),
|
url(r'^ping/$', Ping.as_view()),
|
||||||
@@ -87,6 +89,8 @@ urlpatterns = patterns('',
|
|||||||
url(r'^groups/(?P<group_id>\d+)/members/$', GroupMembers.as_view()),
|
url(r'^groups/(?P<group_id>\d+)/members/$', GroupMembers.as_view()),
|
||||||
url(r'^groups/(?P<group_id>\d+)/repos/$', GroupRepos.as_view(), name="api2-grouprepos"),
|
url(r'^groups/(?P<group_id>\d+)/repos/$', GroupRepos.as_view(), name="api2-grouprepos"),
|
||||||
url(r'^groups/(?P<group_id>\d+)/repos/(?P<repo_id>[-0-9a-f]{36})/$', GroupRepo.as_view(), name="api2-grouprepo"),
|
url(r'^groups/(?P<group_id>\d+)/repos/(?P<repo_id>[-0-9a-f]{36})/$', GroupRepo.as_view(), name="api2-grouprepo"),
|
||||||
|
url(r'^groups/(?P<group_id>\d+)/discussions/$', GroupDiscussions.as_view(), name="api2-group-discussions"),
|
||||||
|
url(r'^groups/(?P<group_id>\d+)/discussions/(?P<discuss_id>\d+)/$', GroupDiscussion.as_view(), name="api2-group-discussion"),
|
||||||
|
|
||||||
url(r'^html/events/$', EventsHtml.as_view()),
|
url(r'^html/events/$', EventsHtml.as_view()),
|
||||||
url(r'^html/more_events/$', AjaxEvents.as_view(), name="more_events"),
|
url(r'^html/more_events/$', AjaxEvents.as_view(), name="more_events"),
|
||||||
|
31
tests/api/endpoints/test_group_discussion.py
Normal file
31
tests/api/endpoints/test_group_discussion.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from seahub.group.models import GroupMessage
|
||||||
|
from seahub.test_utils import BaseTestCase
|
||||||
|
|
||||||
|
class GroupDiscussionTest(BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.username = self.user.username
|
||||||
|
self.login_as(self.user)
|
||||||
|
self.discuss = GroupMessage.objects.create(group_id=self.group.id,
|
||||||
|
from_email=self.username,
|
||||||
|
message="msg 1")
|
||||||
|
self.endpoint = reverse('api2-group-discussion', args=[
|
||||||
|
self.group.id, self.discuss.pk])
|
||||||
|
|
||||||
|
def test_can_delete_discussion(self):
|
||||||
|
assert len(GroupMessage.objects.all()) == 1
|
||||||
|
|
||||||
|
resp = self.client.delete(self.endpoint)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
assert len(GroupMessage.objects.all()) == 0
|
||||||
|
|
||||||
|
def test_can_not_delete_discussion_when_invalid_user(self):
|
||||||
|
self.logout()
|
||||||
|
|
||||||
|
self.login_as(self.admin)
|
||||||
|
resp = self.client.delete(self.endpoint)
|
||||||
|
self.assertEqual(403, resp.status_code)
|
76
tests/api/endpoints/test_group_discussions.py
Normal file
76
tests/api/endpoints/test_group_discussions.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from seahub.group.models import GroupMessage
|
||||||
|
from seahub.test_utils import BaseTestCase
|
||||||
|
|
||||||
|
class GroupDiscussionsTest(BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.login_as(self.user)
|
||||||
|
self.endpoint = reverse('api2-group-discussions', args=[self.group.id])
|
||||||
|
self.username = self.user.username
|
||||||
|
|
||||||
|
def test_can_list(self):
|
||||||
|
GroupMessage(group_id=self.group.id, from_email=self.username,
|
||||||
|
message="msg 1").save()
|
||||||
|
|
||||||
|
resp = self.client.get(self.endpoint)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert len(json_resp) == 1
|
||||||
|
|
||||||
|
def test_can_list_with_paginator(self):
|
||||||
|
for i in range(10):
|
||||||
|
GroupMessage(group_id=self.group.id, from_email=self.username,
|
||||||
|
message="msg %s" % i).save()
|
||||||
|
|
||||||
|
resp = self.client.get(self.endpoint + '?page=1&per_page=5')
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert len(json_resp) == 5
|
||||||
|
assert json_resp[0]['content'] == 'msg 9'
|
||||||
|
|
||||||
|
resp = self.client.get(self.endpoint + '?page=2&per_page=5')
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert len(json_resp) == 5
|
||||||
|
assert json_resp[-1]['content'] == 'msg 0'
|
||||||
|
|
||||||
|
resp = self.client.get(self.endpoint + '?page=3&per_page=5')
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert len(json_resp) == 5
|
||||||
|
assert json_resp[-1]['content'] == 'msg 0'
|
||||||
|
|
||||||
|
def test_can_not_list_when_invalid_user(self):
|
||||||
|
self.logout()
|
||||||
|
|
||||||
|
self.login_as(self.admin)
|
||||||
|
resp = self.client.get(self.endpoint)
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
def test_can_post_a_discussion(self):
|
||||||
|
assert len(GroupMessage.objects.all()) == 0
|
||||||
|
resp = self.client.post(self.endpoint, {
|
||||||
|
'content': 'msg 1'
|
||||||
|
})
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
|
||||||
|
assert len(GroupMessage.objects.all()) == 1
|
||||||
|
assert json_resp['content'] == 'msg 1'
|
||||||
|
|
||||||
|
def test_can_not_post_empty_content(self):
|
||||||
|
resp = self.client.post(self.endpoint, {
|
||||||
|
'content': ''
|
||||||
|
})
|
||||||
|
self.assertEqual(400, resp.status_code)
|
||||||
|
|
||||||
|
def test_can_not_post_content_when_invalid_user(self):
|
||||||
|
self.logout()
|
||||||
|
|
||||||
|
self.login_as(self.admin)
|
||||||
|
resp = self.client.post(self.endpoint, {
|
||||||
|
'content': 'msg 1'
|
||||||
|
})
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
Reference in New Issue
Block a user