diff --git a/seahub/api2/endpoints/groups.py b/seahub/api2/endpoints/groups.py index 988ae6cf28..d21c784280 100644 --- a/seahub/api2/endpoints/groups.py +++ b/seahub/api2/endpoints/groups.py @@ -20,9 +20,10 @@ from seahub.api2.authentication import TokenAuthentication from seahub.avatar.settings import GROUP_AVATAR_DEFAULT_SIZE from seahub.avatar.templatetags.group_avatar_tags import api_grp_avatar_url, \ get_default_group_avatar_url -from seahub.utils import is_org_context +from seahub.utils import is_org_context, is_valid_username from seahub.utils.timeutils import dt, utc_to_local from seahub.group.utils import validate_group_name, check_group_name_conflict +from seahub.group.views import remove_group_common from seahub.base.templatetags.seahub_tags import email2nickname, \ translate_seahub_time @@ -167,3 +168,125 @@ class Groups(APIView): "admins": self._get_group_admins(g.id), } return Response(new_group, status=status.HTTP_201_CREATED) + + def put(self, request, group_id): + """ Rename, transfer a group + """ + + group_id = int(group_id) + try: + group = seaserv.get_group(group_id) + except SearpcError as e: + logger.error(e) + error_msg = _(u'Internal Server Error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not group: + error_msg = _(u'Group does not exist.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # only group staff can rename or transfer a group + username = request.user.username + if not seaserv.check_group_staff(group_id, username): + error_msg = _(u'Permission denied') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + operation = request.DATA.get('operation', '') + if operation.lower() == 'rename': + new_group_name = request.DATA.get('new_group_name', None) + if not new_group_name: + error_msg = _(u'Argument missing') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # Check whether group name is validate. + if not validate_group_name(new_group_name): + error_msg = _(u'Group name can only contain letters, numbers, blank, hyphen or underscore') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # Check whether group name is duplicated. + if check_group_name_conflict(request, new_group_name): + error_msg = _(u'There is already a group with that name.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + seaserv.ccnet_threaded_rpc.set_group_name(group_id, new_group_name) + except SearpcError as e: + logger.error(e) + error_msg = _(u'Internal Server Error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + elif operation.lower() == 'transfer': + email = request.DATA.get('email') + if not email: + error_msg = _(u'Argument missing') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not is_valid_username(email): + error_msg = _('Email %s is not valid.') % email + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if email != username: + try: + if not seaserv.is_group_user(group_id, email): + seaserv.ccnet_threaded_rpc.group_add_member(group_id, username, email) + + seaserv.ccnet_threaded_rpc.set_group_creator(group_id, email) + seaserv.ccnet_threaded_rpc.group_set_admin(group_id, email) + except SearpcError as e: + logger.error(e) + error_msg = _(u'Internal Server Error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + else: + error_msg = _(u'Operation can only be rename or transfer.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + g = seaserv.get_group(group_id) + val = utc_to_local(dt(g.timestamp)) + avatar_url, is_default, date_uploaded = api_grp_avatar_url(group_id, + GROUP_AVATAR_DEFAULT_SIZE) + + group_info = { + "id": g.id, + "name": g.group_name, + "creator": g.creator_name, + "created_at": val.strftime("%Y-%m-%dT%H:%M:%S") + DateFormat(val).format('O'), + "avatar_url": request.build_absolute_uri(avatar_url), + "admins": self._get_group_admins(g.id), + } + return Response(group_info, status=status.HTTP_200_OK) + + def delete(self, request, group_id): + """ Delete a group + """ + + group_id = int(group_id) + try: + group = seaserv.get_group(group_id) + except SearpcError as e: + logger.error(e) + error_msg = _(u'Internal Server Error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not group: + error_msg = _(u'Group does not exist.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # only group staff can delete a group + username = request.user.username + if not seaserv.check_group_staff(group_id, username): + error_msg = _(u'Permission denied') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + org_id = None + if is_org_context(request): + org_id = request.user.org.org_id + + try: + remove_group_common(group_id, username, org_id=org_id) + except SearpcError as e: + logger.error(e) + error_msg = _(u'Internal Server Error') + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + return Response({'success': True}, status=status.HTTP_200_OK) + diff --git a/seahub/urls.py b/seahub/urls.py index 93606f1c37..08bdd35586 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -192,6 +192,7 @@ urlpatterns = patterns( ### Apps ### (r'^api2/', include('seahub.api2.urls')), url(r'^api/v2.1/groups/$', Groups.as_view(), name='api-v2.1-groups'), + url(r'^api/v2.1/groups/(?P\d+)/$', Groups.as_view(), name='api-v2.1-group'), (r'^avatar/', include('seahub.avatar.urls')), (r'^notification/', include('seahub.notifications.urls')), (r'^contacts/', include('seahub.contacts.urls')), diff --git a/tests/api/endpoints/test_groups.py b/tests/api/endpoints/test_groups.py index 9f2875fb69..66ff20411f 100644 --- a/tests/api/endpoints/test_groups.py +++ b/tests/api/endpoints/test_groups.py @@ -7,6 +7,7 @@ from seaserv import seafile_api from seahub.test_utils import BaseTestCase from seahub.api2.endpoints.groups import Groups +from tests.common.utils import randstring class GroupsTest(BaseTestCase): @@ -61,7 +62,7 @@ class GroupsTest(BaseTestCase): assert self.group_id in group_ids def test_create_group(self): - new_group_name = 'new-group-1' + new_group_name = 'new-group-' + randstring(6) resp = self.client.post(self.url, {'group_name': new_group_name}) self.assertEqual(201, resp.status_code) @@ -74,7 +75,7 @@ class GroupsTest(BaseTestCase): self.remove_group(json_resp['id']) def test_create_group_with_cn_name(self): - new_group_name = u'中文' + new_group_name = u'中文' + randstring(6) resp = self.client.post(self.url, {'group_name': new_group_name}) self.assertEqual(201, resp.status_code) @@ -90,15 +91,45 @@ class GroupsTest(BaseTestCase): self.assertEqual(400, resp.status_code) def test_can_not_create_group_with_invalid_name(self): - group_name = 'new%group-2' + new_group_name = 'new%group-' + randstring(6) - resp = self.client.post(self.url, {'group_name': group_name}) + resp = self.client.post(self.url, {'group_name': new_group_name}) self.assertEqual(400, resp.status_code) @patch.object(Groups, '_can_add_group') def test_can_not_create_group_with_invalid_permission(self, mock_can_add_group): mock_can_add_group.return_value = False - group_name = 'new-group-3' + new_group_name = 'new-group-' + randstring(6) - resp = self.client.post(self.url, {'group_name': group_name}) + resp = self.client.post(self.url, {'group_name': new_group_name}) self.assertEqual(403, resp.status_code) + + def test_can_rename_group(self): + new_group_name = 'new-group-' + randstring(6) + url = reverse('api-v2.1-group', args=[self.group_id]) + data = 'operation=rename&new_group_name=%s' % new_group_name + + resp = self.client.put(url, data, 'application/x-www-form-urlencoded') + self.assertEqual(200, resp.status_code) + + json_resp = json.loads(resp.content) + assert json_resp['name'] == new_group_name + + def test_can_transfer_group(self): + new_creator = self.admin.email + url = reverse('api-v2.1-group', args=[self.group_id]) + data = 'operation=transfer&email=%s' % new_creator + + resp = self.client.put(url, data, 'application/x-www-form-urlencoded') + self.assertEqual(200, resp.status_code) + + json_resp = json.loads(resp.content) + assert json_resp['creator'] == new_creator + + def test_can_delete_group(self): + url = reverse('api-v2.1-group', args=[self.group_id]) + resp = self.client.delete(url) + self.assertEqual(200, resp.status_code) + + json_resp = json.loads(resp.content) + assert json_resp['success'] is True