From 18c60fd3067dbe52f1cc9daa107fbdf8fb7cd247 Mon Sep 17 00:00:00 2001 From: lian Date: Thu, 25 Aug 2016 12:02:13 +0800 Subject: [PATCH] update cp/mv file/dir --- seahub/api2/endpoints/copy_move_task.py | 146 ++++++++++ seahub/urls.py | 4 - seahub/views/ajax.py | 272 ------------------ .../scripts/app/views/dialogs/dirent-mvcp.js | 20 +- static/scripts/app/views/dir.js | 16 +- static/scripts/common.js | 4 - 6 files changed, 164 insertions(+), 298 deletions(-) diff --git a/seahub/api2/endpoints/copy_move_task.py b/seahub/api2/endpoints/copy_move_task.py index 79c1080678..ae417b1145 100644 --- a/seahub/api2/endpoints/copy_move_task.py +++ b/seahub/api2/endpoints/copy_move_task.py @@ -1,4 +1,5 @@ # Copyright (c) 2012-2016 Seafile Ltd. +import posixpath import logging from rest_framework.authentication import SessionAuthentication @@ -7,11 +8,16 @@ from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import status from django.utils.translation import ugettext as _ +from django.utils.html import escape from seahub.api2.throttling import UserRateThrottle from seahub.api2.authentication import TokenAuthentication from seahub.api2.utils import api_error +from seahub.views import check_folder_permission +from seahub.utils import check_filename_with_rename +from seahub.settings import MAX_PATH + from seaserv import seafile_api logger = logging.getLogger(__name__) @@ -22,6 +28,146 @@ class CopyMoveTaskView(APIView): permission_classes = (IsAuthenticated,) throttle_classes = (UserRateThrottle,) + def post(self, request): + """ Copy/move file/dir, and return task id. + + Permission checking: + 1. move: user with 'rw' permission for current file, 'rw' permission for dst parent dir; + 2. copy: user with 'r' permission for current file, 'rw' permission for dst parent dir; + """ + src_repo_id = request.data.get('src_repo_id', None) + src_parent_dir = request.data.get('src_parent_dir', None) + src_dirent_name = request.data.get('src_dirent_name', None) + dst_repo_id = request.data.get('dst_repo_id', None) + dst_parent_dir = request.data.get('dst_parent_dir', None) + operation = request.data.get('operation', None) + dirent_type = request.data.get('dirent_type', None) + + # argument check + if not src_repo_id: + error_msg = 'src_repo_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not src_parent_dir: + error_msg = 'src_parent_dir invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not src_dirent_name: + error_msg = 'src_dirent_name invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not dst_repo_id: + error_msg = 'dst_repo_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not dst_parent_dir: + error_msg = 'dst_parent_dir invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not operation: + error_msg = 'operation invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not dirent_type: + error_msg = 'dirent_type invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if src_repo_id == dst_repo_id and src_parent_dir == dst_parent_dir: + error_msg = _('Invalid destination path') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if len(dst_parent_dir + src_dirent_name) > MAX_PATH: + error_msg = _('Destination path is too long.') + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + operation = operation.lower() + if operation not in ('move', 'copy'): + error_msg = "operation can only be 'move' or 'copy'." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + dirent_type = dirent_type.lower() + if dirent_type not in ('file', 'dir'): + error_msg = "operation can only be 'file' or 'dir'." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # src resource check + src_dirent_path = posixpath.join(src_parent_dir, src_dirent_name) + if dirent_type == 'file': + if not seafile_api.get_file_id_by_path(src_repo_id, + src_dirent_path): + error_msg = 'File %s not found.' % src_dirent_path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if dirent_type == 'dir': + if not seafile_api.get_dir_id_by_path(src_repo_id, + src_dirent_path): + error_msg = 'Folder %s not found.' % src_dirent_path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # dst resource check + if not seafile_api.get_dir_id_by_path(dst_repo_id, + dst_parent_dir): + error_msg = 'Folder %s not found.' % dst_parent_dir + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # permission check for dst parent dir + if check_folder_permission(request, dst_repo_id, dst_parent_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + new_dirent_name = check_filename_with_rename(dst_repo_id, + dst_parent_dir, src_dirent_name) + + username = request.user.username + if operation == 'move': + if dirent_type == 'dir' and src_repo_id == dst_repo_id and \ + dst_parent_dir.startswith(src_parent_dir + '/'): + + error_msg = _(u'Can not move directory %(src)s to its subdirectory %(des)s') \ + % {'src': escape(src_parent_dir), 'des': escape( dst_parent_dir)} + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + # permission check for src parent dir + if check_folder_permission(request, src_repo_id, src_parent_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + try: + res = seafile_api.move_file(src_repo_id, src_parent_dir, + src_dirent_name, dst_repo_id, dst_parent_dir, + new_dirent_name, replace=False, username=username, + need_progress=1) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if operation == 'copy': + # permission check for src parent dir + if not check_folder_permission(request, src_repo_id, src_parent_dir): + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + try: + res = seafile_api.copy_file(src_repo_id, src_parent_dir, + src_dirent_name, dst_repo_id, dst_parent_dir, + new_dirent_name, username=username, + need_progress=1) + except Exception as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not res: + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + result = {} + if res.background: + result['task_id'] = res.task_id + + return Response(result) + def delete(self, request): """ Cancel file/dir mv/cp. diff --git a/seahub/urls.py b/seahub/urls.py index 430f94f51a..717b883756 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -139,12 +139,8 @@ urlpatterns = patterns( url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/copy/$', cp_dirents, name='cp_dirents'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/rename/$', rename_dirent, name='rename_dir'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/delete/$', delete_dirent, name='delete_dir'), - url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/mv/$', mv_dir, name='mv_dir'), - url(r'^ajax/repo/(?P[-0-9a-f]{36})/dir/cp/$', cp_dir, name='cp_dir'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/file/rename/$', rename_dirent, name='rename_file'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/file/delete/$', delete_dirent, name='delete_file'), - url(r'^ajax/repo/(?P[-0-9a-f]{36})/file/mv/$', mv_file, name='mv_file'), - url(r'^ajax/repo/(?P[-0-9a-f]{36})/file/cp/$', cp_file, name='cp_file'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/current_commit/$', get_current_commit, name='get_current_commit'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/history/changes/$', repo_history_changes, name='repo_history_changes'), url(r'^ajax/repo/(?P[-0-9a-f]{36})/encrypted_file/(?P[0-9a-f]{40})/download/$', download_enc_file, name='download_enc_file'), diff --git a/seahub/views/ajax.py b/seahub/views/ajax.py index ddd2ad806d..9aceae120c 100644 --- a/seahub/views/ajax.py +++ b/seahub/views/ajax.py @@ -527,278 +527,6 @@ def delete_dirents(request, repo_id): return HttpResponse(json.dumps({'deleted': deleted, 'undeleted': undeleted}), content_type=content_type) -def copy_move_common(): - """Decorator for common logic in copying/moving dir/file. - """ - def _method_wrapper(view_method): - def _arguments_wrapper(request, repo_id, *args, **kwargs): - if request.method != 'POST': - raise Http404 - - result = {} - content_type = 'application/json; charset=utf-8' - - # arguments check for src - path = request.GET.get('path') - obj_name = request.GET.get('obj_name') - if not (path and obj_name): - result['error'] = _('Argument missing') - return HttpResponse(json.dumps(result), status=400, - content_type=content_type) - - # resource check for src - repo = get_repo(repo_id) - if not repo: - result['error'] = _(u'Library does not exist.') - return HttpResponse(json.dumps(result), status=404, - content_type=content_type) - - full_obj_path = posixpath.join(path, obj_name) - file_obj_id = seafile_api.get_file_id_by_path(repo_id, full_obj_path) - dir_obj_id = seafile_api.get_dir_id_by_path(repo_id, full_obj_path) - - if not file_obj_id and not dir_obj_id: - result['error'] = _(u'"%s" does not exist.') % full_obj_path - return HttpResponse(json.dumps(result), status=404, - content_type=content_type) - - # arguments chech for dst - dst_repo_id = request.POST.get('dst_repo') - dst_path = request.POST.get('dst_path') - if not (dst_repo_id and dst_path): - result['error'] = _('Argument missing') - return HttpResponse(json.dumps(result), status=400, - content_type=content_type) - - # resource check for dst - dst_repo = seafile_api.get_repo(dst_repo_id) - if not dst_repo: - result['error'] = _(u'Library does not exist.') - return HttpResponse(json.dumps(result), status=404, - content_type=content_type) - - # resource check for dst - # check file path - if len(dst_path + obj_name) > settings.MAX_PATH: - result['error'] = _('Destination path is too long.') - return HttpResponse(json.dumps(result), status=400, - content_type=content_type) - - # resource check for dst - # return error when dst is the same as src - if repo_id == dst_repo_id and path == dst_path: - result['error'] = _('Invalid destination path') - return HttpResponse(json.dumps(result), status=400, - content_type=content_type) - - # permission check for dst - # check whether user has write permission to dest repo - # Leave src folder/file permission checking to corresponding views. - # For 'move', check has read-write perm to src folder; - # For 'cp', check has read perm to src folder. - if check_folder_permission(request, dst_repo_id, dst_path) != 'rw': - result['error'] = _('Permission denied') - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - # check quota for dst - if file_obj_id: - obj_size = seafile_api.get_file_size(repo.store_id, - repo.version, file_obj_id) - - if dir_obj_id: - obj_size = seafile_api.get_dir_size(repo.store_id, - repo.version, dir_obj_id) - - # check quota - out_of_quota = False - try: - if seafile_api.is_repo_owner(request.user.username, dst_repo_id): - # if dst repo is my own repo, only check quota when copy. - if view_method.__name__ in ('cp_file', 'cp_dir'): - out_of_quota = seafile_api.check_quota(dst_repo_id, delta=obj_size) - else: - # if dst repo is NOT my own repo, - # check quota when copy AND move. - out_of_quota = seafile_api.check_quota(dst_repo_id, delta=obj_size) - except Exception as e: - logger.error(e) - result['error'] = _(u'Internal server error') - return HttpResponse(json.dumps(result), status=500, - content_type=content_type) - - if out_of_quota: - result['error'] = _('Out of quota.') - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - return view_method(request, repo_id, path, - dst_repo_id, dst_path, obj_name) - - return _arguments_wrapper - - return _method_wrapper - -@login_required_ajax -@copy_move_common() -def mv_file(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name): - result = {} - content_type = 'application/json; charset=utf-8' - username = request.user.username - - # check parent dir perm - if check_folder_permission(request, src_repo_id, src_path) != 'rw': - result['error'] = _('Permission denied') - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name) - try: - res = seafile_api.move_file(src_repo_id, src_path, obj_name, - dst_repo_id, dst_path, new_obj_name, - replace=False, username=username, need_progress=1) - except SearpcError as e: - logger.error(e) - res = None - - # res can be None or an object - if not res: - result['error'] = _(u'Internal server error') - return HttpResponse(json.dumps(result), status=500, - content_type=content_type) - - result['success'] = True - msg = _(u'Successfully moved %(name)s') % {"name": escape(obj_name)} - result['msg'] = msg - if res.background: - result['task_id'] = res.task_id - - return HttpResponse(json.dumps(result), content_type=content_type) - -@login_required_ajax -@copy_move_common() -def cp_file(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name): - result = {} - content_type = 'application/json; charset=utf-8' - username = request.user.username - - # check parent dir perm - if not check_folder_permission(request, src_repo_id, src_path): - result['error'] = _('Permission denied') - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name) - try: - res = seafile_api.copy_file(src_repo_id, src_path, obj_name, - dst_repo_id, dst_path, new_obj_name, - username, need_progress=1) - except SearpcError as e: - logger.error(e) - res = None - - if not res: - result['error'] = _(u'Internal server error') - return HttpResponse(json.dumps(result), status=500, - content_type=content_type) - - result['success'] = True - msg = _(u'Successfully copied %(name)s') % {"name": escape(obj_name)} - result['msg'] = msg - - if res.background: - result['task_id'] = res.task_id - - return HttpResponse(json.dumps(result), content_type=content_type) - -@login_required_ajax -@copy_move_common() -def mv_dir(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name): - result = {} - content_type = 'application/json; charset=utf-8' - username = request.user.username - - src_dir = posixpath.join(src_path, obj_name) - if dst_path.startswith(src_dir + '/'): - error_msg = _(u'Can not move directory %(src)s to its subdirectory %(des)s') \ - % {'src': escape(src_dir), 'des': escape(dst_path)} - result['error'] = error_msg - return HttpResponse(json.dumps(result), status=400, content_type=content_type) - - # check dir perm - if check_folder_permission(request, src_repo_id, src_dir) != 'rw': - result['error'] = _('Permission denied') - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name) - try: - res = seafile_api.move_file(src_repo_id, src_path, obj_name, - dst_repo_id, dst_path, new_obj_name, - replace=False, username=username, need_progress=1) - except SearpcError as e: - logger.error(e) - res = None - - # res can be None or an object - if not res: - result['error'] = _(u'Internal server error') - return HttpResponse(json.dumps(result), status=500, - content_type=content_type) - - result['success'] = True - msg = _(u'Successfully moved %(name)s') % {"name": escape(obj_name)} - result['msg'] = msg - if res.background: - result['task_id'] = res.task_id - - return HttpResponse(json.dumps(result), content_type=content_type) - -@login_required_ajax -@copy_move_common() -def cp_dir(request, src_repo_id, src_path, dst_repo_id, dst_path, obj_name): - result = {} - content_type = 'application/json; charset=utf-8' - username = request.user.username - - # check src dir perm - if not check_folder_permission(request, src_repo_id, src_path): - result['error'] = _('Permission denied') - return HttpResponse(json.dumps(result), status=403, - content_type=content_type) - - src_dir = posixpath.join(src_path, obj_name) - if dst_path.startswith(src_dir): - error_msg = _(u'Can not copy directory %(src)s to its subdirectory %(des)s') \ - % {'src': escape(src_dir), 'des': escape(dst_path)} - result['error'] = error_msg - return HttpResponse(json.dumps(result), status=400, content_type=content_type) - - new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name) - - try: - res = seafile_api.copy_file(src_repo_id, src_path, obj_name, - dst_repo_id, dst_path, new_obj_name, - username, need_progress=1) - except SearpcError, e: - logger.error(e) - res = None - - # res can be None or an object - if not res: - result['error'] = _(u'Internal server error') - return HttpResponse(json.dumps(result), status=500, - content_type=content_type) - - result['success'] = True - msg = _(u'Successfully copied %(name)s') % {"name": escape(obj_name)} - result['msg'] = msg - if res.background: - result['task_id'] = res.task_id - - return HttpResponse(json.dumps(result), content_type=content_type) - - def dirents_copy_move_common(): """ Decorator for common logic in copying/moving dirs/files in batch. diff --git a/static/scripts/app/views/dialogs/dirent-mvcp.js b/static/scripts/app/views/dialogs/dirent-mvcp.js index 5bdb28c630..e1327d002e 100644 --- a/static/scripts/app/views/dialogs/dirent-mvcp.js +++ b/static/scripts/app/views/dialogs/dirent-mvcp.js @@ -96,6 +96,7 @@ define([ url: Common.getUrl({name: 'query_copy_move_progress'}) + '?task_id=' + encodeURIComponent(task_id), dataType: 'json', success: function(data) { + console.log(data); var bar = $('.ui-progressbar-value', $('#mv-progress')); if (!data['failed'] && !data['canceled'] && !data['successful']) { if (data['done'] == data['total']) { @@ -182,17 +183,16 @@ define([ this.$error.html(gettext("Invalid destination path")).removeClass('hide'); return false; } - var options = { repo_id: repo_id }; - if (obj_type == 'dir') { - options.name = this.op_type == 'mv' ? 'mv_dir' : 'cp_dir'; - } else { - options.name = this.op_type == 'mv' ? 'mv_file' : 'cp_file'; - } - var post_url = Common.getUrl(options) + '?path=' + encodeURIComponent(path) - + '&obj_name=' + encodeURIComponent(obj_name); + + var post_url = Common.getUrl({'name': 'copy_move_task'}); var post_data = { - 'dst_repo': dst_repo, - 'dst_path': dst_path + 'src_repo_id': repo_id, + 'src_parent_dir': path, + 'src_dirent_name': obj_name, + 'dst_repo_id': dst_repo, + 'dst_parent_dir': dst_path, + 'operation': this.op_type == 'mv' ? 'move' : 'copy', + 'dirent_type': obj_type == 'dir' ? 'dir' : 'file' }; var after_op_success = function(data) { $.modal.close(); diff --git a/static/scripts/app/views/dir.js b/static/scripts/app/views/dir.js index ac52c574b5..9eba6be00c 100644 --- a/static/scripts/app/views/dir.js +++ b/static/scripts/app/views/dir.js @@ -1182,15 +1182,15 @@ define([ post_url, post_data; - if (op == 'mv') { - url_obj.name = obj_type == 'dir' ? 'mv_dir' : 'mv_file'; - } else { - url_obj.name = obj_type == 'dir' ? 'cp_dir' : 'cp_file'; - } - post_url = Common.getUrl(url_obj) + '?path=' + encodeURIComponent(cur_path) + '&obj_name=' + encodeURIComponent(obj_name); + post_url = Common.getUrl({'name': 'copy_move_task'}); post_data = { - 'dst_repo': dst_repo, - 'dst_path': dst_path + 'src_repo_id': dirents.repo_id, + 'src_parent_dir': cur_path, + 'src_dirent_name': obj_name, + 'dst_repo_id': dst_repo, + 'dst_parent_dir': dst_path, + 'operation': op == 'mv' ? 'move' : 'copy', + 'dirent_type': obj_type == 'dir' ? 'dir' : 'file' }; var after_op_success = function (data) { var det_text = op == 'mv' ? gettext("Moving file %(index)s of %(total)s") : gettext("Copying file %(index)s of %(total)s"); diff --git a/static/scripts/common.js b/static/scripts/common.js index 110b5e4daf..4b8854dcf6 100644 --- a/static/scripts/common.js +++ b/static/scripts/common.js @@ -88,10 +88,6 @@ define([ case 'query_copy_move_progress': return siteRoot + 'api/v2.1/query-copy-move-progress/'; case 'rename_dir': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/dir/'; case 'rename_file': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/'; - case 'mv_dir': return siteRoot + 'ajax/repo/' + options.repo_id + '/dir/mv/'; - case 'cp_dir': return siteRoot + 'ajax/repo/' + options.repo_id + '/dir/cp/'; - case 'mv_file': return siteRoot + 'ajax/repo/' + options.repo_id + '/file/mv/'; - case 'cp_file': return siteRoot + 'ajax/repo/' + options.repo_id + '/file/cp/'; case 'lock_or_unlock_file': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/'; case 'new_dir': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/dir/'; case 'new_file': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/';