diff --git a/seahub/api2/endpoints/group_members.py b/seahub/api2/endpoints/group_members.py new file mode 100644 index 0000000000..ca2bc5618d --- /dev/null +++ b/seahub/api2/endpoints/group_members.py @@ -0,0 +1,123 @@ +import logging + +from django.utils.translation import ugettext as _ + +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 rest_framework import status + +import seaserv +from pysearpc import SearpcError + +from seahub.profile.models import Profile +from seahub.api2.utils import api_error +from seahub.api2.throttling import UserRateThrottle +from seahub.api2.authentication import TokenAuthentication +from seahub.avatar.settings import AVATAR_DEFAULT_SIZE +from seahub.avatar.templatetags.avatar_tags import api_avatar_url, \ + get_default_avatar_url +from seahub.utils import is_valid_username +from seahub.base.templatetags.seahub_tags import email2nickname +from seahub.base.accounts import User + +from .utils import api_check_group_member, api_check_group_staff + +logger = logging.getLogger(__name__) + +def get_group_member_info(request, group_id, email, avatar_size=AVATAR_DEFAULT_SIZE): + p = Profile.objects.get_profile_by_user(email) + if p: + login_id = p.login_id if p.login_id != '' else '' + else: + login_id = '' + + try: + avatar_url, is_default, date_uploaded = api_avatar_url(email, avatar_size) + except Exception as e: + logger.error(e) + avatar_url = get_default_avatar_url() + + is_admin = seaserv.check_group_staff(group_id, email) + member_info = { + "name": email2nickname(email), + 'email': email, + "contact_email": Profile.objects.get_contact_email_by_user(email), + "login_id": login_id, + "avatar_url": request.build_absolute_uri(avatar_url), + "is_admin": is_admin, + } + + return member_info + + +class GroupMembers(APIView): + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + @api_check_group_member + def get(self, request, group_id, format=None): + """ + Get all group members. + """ + + try: + avatar_size = int(request.GET.get('avatar_size', + AVATAR_DEFAULT_SIZE)) + except ValueError: + avatar_size = AVATAR_DEFAULT_SIZE + + try: + members = seaserv.get_group_members(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) + + try: + only_admin = int(request.GET.get('only_admin', 0)) + except ValueError: + only_admin = 0 + + if only_admin not in (0, 1): + error_msg = _(u'Argument is not valid') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + group_members = [] + for m in members: + # only return group admins + if only_admin and not m.is_staff: + continue + + member_info = get_group_member_info(request, group_id, m.user_name, avatar_size) + group_members.append(member_info) + + return Response(group_members) + + @api_check_group_staff + def post(self, request, group_id): + """ + Add a group member. + """ + username = request.user.username + + email = request.data.get('email', None) + + try: + User.objects.get(email=email) + except User.DoesNotExist: + error_msg = _(u'Invalid username') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + seaserv.group_add_member(group_id, username, 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) + + member_info = get_group_member_info(request, group_id, email, AVATAR_DEFAULT_SIZE) + + return Response(member_info, status=status.HTTP_201_CREATED) diff --git a/seahub/api2/endpoints/groups.py b/seahub/api2/endpoints/groups.py index b9ef0015f7..f40a7699d9 100644 --- a/seahub/api2/endpoints/groups.py +++ b/seahub/api2/endpoints/groups.py @@ -19,7 +19,7 @@ from seahub.api2.authentication import TokenAuthentication from seahub.api2.throttling import UserRateThrottle 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 + get_default_group_avatar_url 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 @@ -27,9 +27,40 @@ from seahub.group.views import remove_group_common from seahub.base.templatetags.seahub_tags import email2nickname, \ translate_seahub_time +from .utils import api_check_group_staff logger = logging.getLogger(__name__) +def get_group_admins(group_id): + members = seaserv.get_group_members(group_id) + admin_members = filter(lambda m: m.is_staff, members) + + admins = [] + for u in admin_members: + admins.append(u.user_name) + + return admins + +def get_group_info(request, group_id, avatar_size=GROUP_AVATAR_DEFAULT_SIZE): + group = seaserv.get_group(group_id) + try: + avatar_url, is_default, date_uploaded = api_grp_avatar_url(group.id, avatar_size) + except Exception as e: + logger.error(e) + avatar_url = get_default_group_avatar_url() + + val = utc_to_local(dt(group.timestamp)) + group_info = { + "id": group.id, + "name": group.group_name, + "creator": group.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": get_group_admins(group.id), + } + + return group_info + class Groups(APIView): @@ -37,15 +68,6 @@ class Groups(APIView): permission_classes = (IsAuthenticated,) throttle_classes = (UserRateThrottle, ) - def _get_group_admins(self, group_id): - members = seaserv.get_group_members(group_id) - admin_members = filter(lambda m: m.is_staff, members) - - admins = [] - for u in admin_members: - admins.append(u.user_name) - return admins - def _can_add_group(self, request): return request.user.permissions.can_add_group() @@ -66,26 +88,18 @@ class Groups(APIView): except ValueError: size = GROUP_AVATAR_DEFAULT_SIZE - with_repos = request.GET.get('with_repos') - with_repos = True if with_repos == '1' else False + try: + with_repos = int(request.GET.get('with_repos', 0)) + except ValueError: + with_repos = 0 + + if with_repos not in (0, 1): + error_msg = _(u'Argument can only be 0 or 1') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) groups = [] for g in user_groups: - try: - avatar_url, is_default, date_uploaded = api_grp_avatar_url(g.id, size) - except Exception as e: - logger.error(e) - avatar_url = get_default_group_avatar_url() - - val = utc_to_local(dt(g.timestamp)) - group = { - "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), - } + group_info = get_group_info(request, g.id , size) if with_repos: if org_id: @@ -111,9 +125,9 @@ class Groups(APIView): } repos.append(repo) - group['repos'] = repos + group_info['repos'] = repos - groups.append(group) + groups.append(group_info) return Response(groups) @@ -125,7 +139,7 @@ class Groups(APIView): return api_error(status.HTTP_403_FORBIDDEN, error_msg) username = request.user.username - group_name = request.data.get('group_name', '') + group_name = request.data.get('name', '') group_name = group_name.strip() # Check whether group name is validate. @@ -138,7 +152,7 @@ class Groups(APIView): error_msg = _(u'There is already a group with that name.') return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - # Group name is valid, create that group. + # create group. try: group_id = seaserv.ccnet_threaded_rpc.create_group(group_name, username) except SearpcError as e: @@ -146,58 +160,29 @@ class Groups(APIView): error_msg = _(u'Failed') return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) - try: - size = int(request.data.get('avatar_size', GROUP_AVATAR_DEFAULT_SIZE)) - except ValueError: - size = GROUP_AVATAR_DEFAULT_SIZE + # get info of new group + group_info = get_group_info(request, group_id, GROUP_AVATAR_DEFAULT_SIZE) - g = seaserv.get_group(group_id) - try: - avatar_url, is_default, date_uploaded = api_grp_avatar_url(g.id, size) - except Exception as e: - logger.error(e) - avatar_url = get_default_group_avatar_url() + return Response(group_info, status=status.HTTP_201_CREATED) - val = utc_to_local(dt(g.timestamp)) - new_group = { - "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(new_group, status=status.HTTP_201_CREATED) +class Group(APIView): + + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + @api_check_group_staff def put(self, request, group_id): - """ Rename, transfer a group + """ Rename, transfer a specific 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 + group = seaserv.get_group(group_id) 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) + new_group_name = request.data.get('name', None) + if new_group_name: + # rename a group # 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') @@ -215,73 +200,41 @@ class Groups(APIView): 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') + new_creator= request.data.get('creator', None) + if new_creator: + # transfer a group + if not is_valid_username(new_creator): + error_msg = _('Creator %s is not valid.') % new_creator 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 == group.creator_name: - error_msg = _('%s is already group owner') % email + if new_creator == group.creator_name: + error_msg = _('%s is already group owner') % new_creator return api_error(status.HTTP_400_BAD_REQUEST, error_msg) try: - if not seaserv.is_group_user(group_id, email): - seaserv.ccnet_threaded_rpc.group_add_member(group_id, username, email) + if not seaserv.is_group_user(group_id, new_creator): + seaserv.ccnet_threaded_rpc.group_add_member(group_id, username, new_creator) - if not seaserv.check_group_staff(group_id, email): - seaserv.ccnet_threaded_rpc.group_set_admin(group_id, email) + if not seaserv.check_group_staff(group_id, new_creator): + seaserv.ccnet_threaded_rpc.group_set_admin(group_id, new_creator) - seaserv.ccnet_threaded_rpc.set_group_creator(group_id, email) + seaserv.ccnet_threaded_rpc.set_group_creator(group_id, new_creator) 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) + # get new info of this group + group_info = get_group_info(request, group_id, GROUP_AVATAR_DEFAULT_SIZE) - 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) + return Response(group_info) + @api_check_group_staff def delete(self, request, group_id): - """ Delete a group + """ Delete a specific 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): @@ -294,5 +247,4 @@ class Groups(APIView): 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) - + return Response({'success': True}) diff --git a/seahub/api2/endpoints/utils.py b/seahub/api2/endpoints/utils.py new file mode 100644 index 0000000000..e8c6f6ed1a --- /dev/null +++ b/seahub/api2/endpoints/utils.py @@ -0,0 +1,92 @@ +import logging + +from django.utils.translation import ugettext as _ + +from rest_framework import status + +import seaserv +from pysearpc import SearpcError + +from seahub.api2.utils import api_error + +logger = logging.getLogger(__name__) + +def api_check_group_member(func): + """ + Decorator for check if group valid and if is group member + """ + def _decorated(view, request, group_id, *args, **kwargs): + group_id = int(group_id) # Checked by URL Conf + 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) + + username = request.user.username + try: + is_group_member = seaserv.is_group_user(group_id, + username) + 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 is_group_member: + error_msg = _(u'Permission denied') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + return func(view, request, group_id, *args, **kwargs) + + return _decorated + + +def api_check_group_staff(func): + """ + Decorator for check if group valid and if is group staff + """ + def _decorated(view, request, group_id, *args, **kwargs): + group_id = int(group_id) # Checked by URL Conf + 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) + + username = request.user.username + try: + is_group_member = seaserv.is_group_user(group_id, + username) + 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 is_group_member: + error_msg = _(u'Permission denied') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + try: + is_group_staff = seaserv.check_group_staff(group_id, username) + 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 is_group_staff: + error_msg = _(u'Permission denied') + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + return func(view, request, group_id, *args, **kwargs) + + return _decorated diff --git a/seahub/api2/views.py b/seahub/api2/views.py index ff72a3ad12..00f6648b2c 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -3470,63 +3470,9 @@ class Groups(APIView): "Operation can only be rename.") class GroupMembers(APIView): - authentication_classes = (TokenAuthentication, SessionAuthentication) + authentication_classes = (TokenAuthentication) permission_classes = (IsAuthenticated,) - throttle_classes = (UserRateThrottle, ) - - def get(self, request, group_id, format=None): - """ - Get group members. - """ - try: - group_id_int = int(group_id) - except ValueError: - return api_error(status.HTTP_400_BAD_REQUEST, 'Invalid group ID') - - try: - avatar_size = int(request.GET.get('avatar_size', - AVATAR_DEFAULT_SIZE)) - except ValueError: - avatar_size = AVATAR_DEFAULT_SIZE - - try: - group = seaserv.get_group(group_id_int) - except SearpcError as e: - logger.error(e) - return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, - 'Failed to get group.') - if not group: - return api_error(status.HTTP_404_NOT_FOUND, 'Group not found') - - try: - is_group_user = seaserv.is_group_user(group_id_int, - request.user.username) - except SearpcError as e: - logger.error(e) - return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, - 'Failed to check if is group user.') - if not is_group_user: - return api_error(status.HTTP_403_FORBIDDEN, 'Permission denied.') - - try: - members = seaserv.get_group_members(group.id) - except SearpcError as e: - logger.error(e) - return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, - 'Failed to get group members.') - members_json = [] - for m in members: - avatar_url, is_default, date_uploaded = api_avatar_url(m.user_name, - avatar_size) - - members_json.append({ - 'username': m.user_name, - "fullname": email2nickname(m.user_name), - "avatar_url": request.build_absolute_uri(avatar_url), - }) - - return HttpResponse(json.dumps(members_json), - content_type=json_content_type) + throttle_classes = (UserRateThrottle,) def put(self, request, group_id, format=None): """ diff --git a/seahub/urls.py b/seahub/urls.py index 0555c5b0b7..f91c8ce262 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -19,7 +19,8 @@ from seahub.views.wiki import personal_wiki, personal_wiki_pages, \ personal_wiki_page_delete, personal_wiki_use_lib from seahub.views.sysadmin import * from seahub.views.ajax import * -from seahub.api2.endpoints.groups import Groups +from seahub.api2.endpoints.groups import Groups, Group +from seahub.api2.endpoints.group_members import GroupMembers # Uncomment the next two lines to enable the admin: #from django.contrib import admin @@ -193,7 +194,8 @@ 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'), + url(r'^api/v2.1/groups/(?P\d+)/$', Group.as_view(), name='api-v2.1-group'), + url(r'^api/v2.1/groups/(?P\d+)/members/$', GroupMembers.as_view(), name='api-v2.1-group-members'), (r'^avatar/', include('seahub.avatar.urls')), (r'^notification/', include('seahub.notifications.urls')), (r'^contacts/', include('seahub.contacts.urls')),