From 7f00a5efb2970b458ffd3f0e2558cdafd6017298 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Wed, 22 Jan 2014 16:17:35 +0000 Subject: [PATCH 1/7] api call to rename repo --- seahub/api2/views.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/seahub/api2/views.py b/seahub/api2/views.py index a78c38e180..882d0423dd 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -68,7 +68,7 @@ from seaserv import seafserv_rpc, seafserv_threaded_rpc, server_repo_size, \ list_share_repos, get_group_repos_by_owner, get_group_repoids, list_inner_pub_repos_by_owner,\ list_inner_pub_repos,remove_share, unshare_group_repo, unset_inner_pub_repo, get_user_quota, \ get_user_share_usage, get_user_quota_usage, CALC_SHARE_USAGE, get_group, \ - get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE + get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE, edit_repo from seaserv import seafile_api @@ -556,6 +556,20 @@ class Repo(APIView): if resp: return resp return Response("success") + elif op == 'rename': + username = request.user.username + repo_name = request.POST.get('repo_name') + repo_desc = request.POST.get('repo_desc') + + if not seafile_api.is_repo_owner(username, repo_id): + return api_error(status.HTTP_403_FORBIDDEN, \ + 'Only library owner can perform this operation.') + + if edit_repo(repo_id, repo_name, repo_desc, username): + return Response("success") + else: + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, + "Unable to rename repo") return Response("unsupported operation") From 08e26cbf23da43f7f3218dbbf958f56535231cc0 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Wed, 22 Jan 2014 17:12:27 +0000 Subject: [PATCH 2/7] add api call to add user profile --- seahub/api2/views.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/seahub/api2/views.py b/seahub/api2/views.py index a78c38e180..f19fd38898 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -196,6 +196,19 @@ class Account(APIView): serializer.object['is_staff'], serializer.object['is_active']) + name = request.DATA.get("name", None) + note = request.DATA.get("note", None) + if name or note: + try: + profile = Profile.objects.get(user=user.username) + except Profile.DoesNotExist: + profile = Profile() + + profile.user = user.username + profile.nickname = name + profile.intro = note + profile.save() + if update: resp = Response('success') else: From 0d36ffa61c835056aa3fb456148c78e7874ea170 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Thu, 23 Jan 2014 12:39:38 +0000 Subject: [PATCH 3/7] api call to add a group --- seahub/api2/views.py | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/seahub/api2/views.py b/seahub/api2/views.py index a78c38e180..751c1948b4 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -68,7 +68,7 @@ from seaserv import seafserv_rpc, seafserv_threaded_rpc, server_repo_size, \ list_share_repos, get_group_repos_by_owner, get_group_repoids, list_inner_pub_repos_by_owner,\ list_inner_pub_repos,remove_share, unshare_group_repo, unset_inner_pub_repo, get_user_quota, \ get_user_share_usage, get_user_quota_usage, CALC_SHARE_USAGE, get_group, \ - get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE + get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE, ccnet_threaded_rpc from seaserv import seafile_api @@ -2185,6 +2185,47 @@ class Groups(APIView): res = {"groups": group_json, "replynum":replynum} return Response(res) + def put(self, request, format=None): + # modified slightly from groups/views.py::group_list + """ + Add a new group. + """ + result = {} + content_type = 'application/json; charset=utf-8' + + # check plan + num_of_groups = getattr(request.user, 'num_of_groups', -1) + if num_of_groups > 0: + current_groups = len(get_personal_groups_by_user(username)) + if current_groups > num_of_groups: + result['error'] = _(u'You can only create %d groups.') % num_of_groups + return HttpResponse(json.dumps(result), status=500, + content_type=content_type) + + group_name = request.DATA.get('group_name', None); + + # Check whether group name is duplicated. + if request.cloud_mode: + checked_groups = get_personal_groups_by_user(username) + else: + checked_groups = get_personal_groups(-1, -1) + for g in checked_groups: + if g.group_name == group_name: + result['error'] = _(u'There is already a group with that name.') + return HttpResponse(json.dumps(result), status=400, + content_type=content_type) + + # Group name is valid, create that group. + try: + ccnet_threaded_rpc.create_group(group_name.encode('utf-8'), + request.user.username) + return HttpResponse(json.dumps({'success': True}), + content_type=content_type) + except SearpcError, e: + result['error'] = _(e.msg) + return HttpResponse(json.dumps(result), status=500, + content_type=content_type) + class AjaxEvents(APIView): authentication_classes = (TokenAuthentication, ) permission_classes = (IsAuthenticated,) From 0562c2486a97cd72d52fc1c3c1741d6e5a5c32b0 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Thu, 23 Jan 2014 12:55:59 +0000 Subject: [PATCH 4/7] return newly created group_id --- seahub/api2/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/seahub/api2/views.py b/seahub/api2/views.py index 751c1948b4..d2721afbd7 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -2217,9 +2217,9 @@ class Groups(APIView): # Group name is valid, create that group. try: - ccnet_threaded_rpc.create_group(group_name.encode('utf-8'), + group_id = ccnet_threaded_rpc.create_group(group_name.encode('utf-8'), request.user.username) - return HttpResponse(json.dumps({'success': True}), + return HttpResponse(json.dumps({'success': True, 'group_id': group_id}), content_type=content_type) except SearpcError, e: result['error'] = _(e.msg) From 8027407a6904fb6dc38bc59b26343ce4ed9e6fbd Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Thu, 23 Jan 2014 15:45:03 +0000 Subject: [PATCH 5/7] api calls to add/remove group members --- seahub/api2/urls.py | 1 + seahub/api2/views.py | 66 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py index 500c5225e1..6bc1341e7e 100644 --- a/seahub/api2/urls.py +++ b/seahub/api2/urls.py @@ -38,6 +38,7 @@ urlpatterns = patterns('', url(r'^virtual-repos/$', VirtualRepos.as_view()), url(r'^groups/$', Groups.as_view()), + url(r'^group/(?P\d+)/manage/$', GroupManage.as_view()), url(r'^html/events/$', ActivityHtml.as_view()), url(r'^html/more_events/$', AjaxEvents.as_view(), name="more_events"), url(r'^html/repo_history_changes/(?P[-0-9a-f]{36})/$', RepoHistoryChangeHtml.as_view(), name='api_repo_history_changes'), diff --git a/seahub/api2/views.py b/seahub/api2/views.py index a78c38e180..069127fd85 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -68,8 +68,8 @@ from seaserv import seafserv_rpc, seafserv_threaded_rpc, server_repo_size, \ list_share_repos, get_group_repos_by_owner, get_group_repoids, list_inner_pub_repos_by_owner,\ list_inner_pub_repos,remove_share, unshare_group_repo, unset_inner_pub_repo, get_user_quota, \ get_user_share_usage, get_user_quota_usage, CALC_SHARE_USAGE, get_group, \ - get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE -from seaserv import seafile_api + get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE, ccnet_threaded_rpc +from seaserv import seafile_api, check_group_staff json_content_type = 'application/json; charset=utf-8' @@ -2185,6 +2185,68 @@ class Groups(APIView): res = {"groups": group_json, "replynum":replynum} return Response(res) +class GroupManage(APIView): + authentication_classes = (TokenAuthentication, ) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def put(self, request, group_id, format=None): + """ + Add group members. + """ + try: + group_id_int = int(group_id) + except ValueError: + return api_error(status.HTTP_404_NOT_FOUND, 'Invalid group id') + + group = get_group(group_id_int) + if not group: + return api_error(status.HTTP_404_NOT_FOUND, 'Unable to find group') + + if not is_group_staff(group, request.user): + return api_error(status.HTTP_403_FORBIDDEN, 'Only administrators can add group members') + + user_name = request.DATA.get('user_name', None) + if not is_registered_user(user_name): + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Not a valid user') + + try: + ccnet_threaded_rpc.group_add_member(group.id, request.user.username, user_name) + except SearpcError, e: + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Unable to add user to group') + + return HttpResponse(json.dumps({'success': True}), status=200, content_type=json_content_type) + + def delete(self, request, group_id, format=None): + """ + Delete group members. + """ + try: + group_id_int = int(group_id) + except ValueError: + return api_error(status.HTTP_404_NOT_FOUND, 'Invalid group id') + + group = get_group(group_id_int) + if not group: + return api_error(status.HTTP_404_NOT_FOUND, 'Unable to find group') + + if not is_group_staff(group, request.user): + return api_error(status.HTTP_403_FORBIDDEN, 'Only administrators can remove group members') + + user_name = request.DATA.get('user_name', None) + + try: + ccnet_threaded_rpc.group_remove_member(group.id, request.user.username, user_name) + except SearpcError, e: + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Unable to add user to group') + + return HttpResponse(json.dumps({'success': True}), status=200, content_type=json_content_type) + +def is_group_staff(group, user): + if user.is_anonymous(): + return False + return check_group_staff(group.id, user.username) + class AjaxEvents(APIView): authentication_classes = (TokenAuthentication, ) permission_classes = (IsAuthenticated,) From 9bc8bfb477a63fe7756062d475f321a8c1000c19 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Mon, 3 Feb 2014 14:31:06 +0000 Subject: [PATCH 6/7] add apis calls DownloadPrivateSharedFileView & DownloadSharedFileView --- seahub/api2/urls.py | 3 ++ seahub/api2/views.py | 84 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py index 500c5225e1..b6621ff656 100644 --- a/seahub/api2/urls.py +++ b/seahub/api2/urls.py @@ -37,6 +37,9 @@ urlpatterns = patterns('', url(r'^shared-files/$', SharedFilesView.as_view()), url(r'^virtual-repos/$', VirtualRepos.as_view()), + url(r'^s/f/(?P[a-f0-9]{10})/$', DownloadPrivateSharedFileView.as_view()), + url(r'^f/(?P[a-f0-9]{10})/$', DownloadSharedFileView.as_view()), + url(r'^groups/$', Groups.as_view()), url(r'^html/events/$', ActivityHtml.as_view()), url(r'^html/more_events/$', AjaxEvents.as_view(), name="more_events"), diff --git a/seahub/api2/views.py b/seahub/api2/views.py index a78c38e180..363b01c66c 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -70,7 +70,7 @@ from seaserv import seafserv_rpc, seafserv_threaded_rpc, server_repo_size, \ get_user_share_usage, get_user_quota_usage, CALC_SHARE_USAGE, get_group, \ get_commit, get_file_id_by_path, MAX_DOWNLOAD_DIR_SIZE from seaserv import seafile_api - +from django.db.models import F json_content_type = 'application/json; charset=utf-8' @@ -1656,6 +1656,88 @@ class VirtualRepos(APIView): return HttpResponse(json.dumps(result), status=500, content_type=content_type) return HttpResponse(json.dumps(result, cls=SearpcObjEncoder), content_type=content_type) + +class DownloadPrivateSharedFileView(APIView): + authentication_classes = (TokenAuthentication, ) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def get(self, request, token, format=None): + assert token is not None # Checked by URLconf + + try: + fileshare = PrivateFileDirShare.objects.get(token=token) + except PrivateFileDirShare.DoesNotExist: + return api_error(status.HTTP_404_NOT_FOUND, "Token not found") + + shared_by = fileshare.from_user + shared_to = fileshare.to_user + + if shared_to != request.user.username: + return api_error(status.HTTP_403_FORBIDDEN, "You don't have permission to view this file") + + repo_id = fileshare.repo_id + repo = get_repo(repo_id) + if not repo: + return api_error(status.HTTP_404_NOT_FOUND, "Repo not found") + + path = fileshare.path.rstrip('/') # Normalize file path + file_name = os.path.basename(path) + + file_id = None + try: + file_id = seafserv_threaded_rpc.get_file_id_by_path(repo_id, + path.encode('utf-8')) + except SearpcError, e: + return api_error(HTTP_520_OPERATION_FAILED, + "Failed to get file id by path.") + + if not file_id: + return api_error(status.HTTP_404_NOT_FOUND, "File not found") + + op = request.GET.get('op', 'download') + return get_repo_file(request, repo_id, file_id, file_name, op) + +class DownloadSharedFileView(APIView): +# Anyone should be able to access a Shared File assuming they have the token +# authentication_classes = (TokenAuthentication, ) +# permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def get(self, request, token, format=None): + assert token is not None # Checked by URLconf + + try: + fileshare = FileShare.objects.get(token=token) + except FileShare.DoesNotExist: + return api_error(status.HTTP_404_NOT_FOUND, "Token not found") + + shared_by = fileshare.username + repo_id = fileshare.repo_id + repo = get_repo(repo_id) + if not repo: + return api_error(status.HTTP_404_NOT_FOUND, "Repo not found") + + path = fileshare.path.rstrip('/') # Normalize file path + file_name = os.path.basename(path) + + file_id = None + try: + file_id = seafserv_threaded_rpc.get_file_id_by_path(repo_id, + path.encode('utf-8')) + except SearpcError, e: + return api_error(HTTP_520_OPERATION_FAILED, + "Failed to get file id by path.") + + if not file_id: + return api_error(status.HTTP_404_NOT_FOUND, "File not found") + + # Increase file shared link view_cnt, this operation should be atomic + fileshare.view_cnt = F('view_cnt') + 1 + fileshare.save() + + op = request.GET.get('op', 'download') + return get_repo_file(request, repo_id, file_id, file_name, op) class FileShareEncoder(json.JSONEncoder): def default(self, obj): From 31da5445eb8ca0c2e5d94403733a6881e6f93c06 Mon Sep 17 00:00:00 2001 From: Patrick McAndrew Date: Mon, 10 Feb 2014 12:08:35 +0000 Subject: [PATCH 7/7] Fix error messages --- seahub/api2/views.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/seahub/api2/views.py b/seahub/api2/views.py index d2721afbd7..a3ddbc8502 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -2196,9 +2196,10 @@ class Groups(APIView): # check plan num_of_groups = getattr(request.user, 'num_of_groups', -1) if num_of_groups > 0: + username = request.user.username current_groups = len(get_personal_groups_by_user(username)) if current_groups > num_of_groups: - result['error'] = _(u'You can only create %d groups.') % num_of_groups + result['error'] = 'You can only create %d groups.' % num_of_groups return HttpResponse(json.dumps(result), status=500, content_type=content_type) @@ -2211,7 +2212,7 @@ class Groups(APIView): checked_groups = get_personal_groups(-1, -1) for g in checked_groups: if g.group_name == group_name: - result['error'] = _(u'There is already a group with that name.') + result['error'] = 'There is already a group with that name.' return HttpResponse(json.dumps(result), status=400, content_type=content_type) @@ -2222,7 +2223,7 @@ class Groups(APIView): return HttpResponse(json.dumps({'success': True, 'group_id': group_id}), content_type=content_type) except SearpcError, e: - result['error'] = _(e.msg) + result['error'] = e.msg return HttpResponse(json.dumps(result), status=500, content_type=content_type)