1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-30 13:23:14 +00:00

update cp/mv file/dir

This commit is contained in:
lian 2016-08-25 12:02:13 +08:00 committed by lian
parent cb837d5112
commit 18c60fd306
6 changed files with 164 additions and 298 deletions

View File

@ -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.

View File

@ -139,12 +139,8 @@ urlpatterns = patterns(
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dirents/copy/$', cp_dirents, name='cp_dirents'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/rename/$', rename_dirent, name='rename_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/delete/$', delete_dirent, name='delete_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/mv/$', mv_dir, name='mv_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dir/cp/$', cp_dir, name='cp_dir'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/rename/$', rename_dirent, name='rename_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/delete/$', delete_dirent, name='delete_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/mv/$', mv_file, name='mv_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/file/cp/$', cp_file, name='cp_file'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/current_commit/$', get_current_commit, name='get_current_commit'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/history/changes/$', repo_history_changes, name='repo_history_changes'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/encrypted_file/(?P<file_id>[0-9a-f]{40})/download/$', download_enc_file, name='download_enc_file'),

View File

@ -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.

View File

@ -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();

View File

@ -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");

View File

@ -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/';