diff --git a/seahub/api2/endpoints/dir.py b/seahub/api2/endpoints/dir.py new file mode 100644 index 0000000000..153d18f867 --- /dev/null +++ b/seahub/api2/endpoints/dir.py @@ -0,0 +1,224 @@ +import os +import logging + +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 + +from seahub.api2.throttling import UserRateThrottle +from seahub.api2.authentication import TokenAuthentication +from seahub.api2.utils import api_error +from seahub.api2.views import reloaddir, get_dir_recursively, \ + get_dir_entrys_by_id + +from seahub.views import check_folder_permission +from seahub.utils import check_filename_with_rename, is_pro_version + +from seaserv import seafile_api +from pysearpc import SearpcError + +logger = logging.getLogger(__name__) + +class DirView(APIView): + """ + Support uniform interface for directory operations, including + create/delete/rename/list, etc. + """ + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated, ) + throttle_classes = (UserRateThrottle, ) + + def get(self, request, repo_id, format=None): + # list dir + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = request.GET.get('p', '/') + if path[-1] != '/': + path = path + '/' + + try: + dir_id = seafile_api.get_dir_id_by_path(repo_id, path) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not dir_id: + error_msg = 'Folder %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, path) is None: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + old_oid = request.GET.get('oid', None) + if old_oid and old_oid == dir_id: + resp = Response({'success': True}) + resp["oid"] = dir_id + return resp + else: + request_type = request.GET.get('t', None) + if request_type and request_type not in ('f', 'd'): + error_msg = "'t'(type) should be 'f' or 'd'." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if request_type == 'd': + recursive = request.GET.get('recursive', '0') + if recursive not in ('1', '0'): + error_msg = "If you want to get recursive dir entries, you should set 'recursive' argument as '1'." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if recursive == '1': + username = request.user.username + dir_list = get_dir_recursively(username, repo_id, path, []) + dir_list.sort(lambda x, y: cmp(x['name'].lower(), y['name'].lower())) + + resp = Response(dir_list) + resp["oid"] = dir_id + resp["dir_perm"] = seafile_api.check_permission_by_path(repo_id, path, username) + return resp + + return get_dir_entrys_by_id(request, repo, path, dir_id, request_type) + + def post(self, request, repo_id, format=None): + repo = seafile_api.get_repo(repo_id) + if not repo: + return api_error(status.HTTP_404_NOT_FOUND, 'Library not found.') + + path = request.GET.get('p', '') + if not path or path[0] != '/': + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if path == '/': + error_msg = 'Can not make or rename root dir.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if path[-1] == '/': + path = path[:-1] + + username = request.user.username + parent_dir = os.path.dirname(path) + operation = request.POST.get('operation', '') + if operation.lower() == 'mkdir': + parent_dir = os.path.dirname(path) + if check_folder_permission(request, repo_id, parent_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + create_parents = request.POST.get('create_parents', '').lower() in ('true', '1') + if not create_parents: + parent_dir_id = seafile_api.get_dir_id_by_path(repo_id, parent_dir) + if not parent_dir_id: + error_msg = 'Folder %s not found.' % parent_dir + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + new_dir_name = os.path.basename(path) + new_dir_name = check_filename_with_rename(repo_id, parent_dir, new_dir_name) + try: + seafile_api.post_dir(repo_id, parent_dir, new_dir_name, username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + else: + if not is_pro_version(): + error_msg = 'Feature not supported.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + try: + seafile_api.mkdir_with_parents(repo_id, '/', path[1:], username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + resp = reloaddir(request, repo, parent_dir) + else: + resp = Response({'success': True}) + + return resp + + elif operation.lower() == 'rename': + dir_id = seafile_api.get_dir_id_by_path(repo_id, path) + if not dir_id: + error_msg = 'Folder %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo.id, path) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + parent_dir = os.path.dirname(path) + old_dir_name = os.path.basename(path) + newname = request.POST.get('newname', '') + if not newname: + error_msg = 'newname invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if newname == old_dir_name: + return Response({'success': True}) + + try: + # rename duplicate name + checked_newname = check_filename_with_rename(repo_id, parent_dir, newname) + # rename dir + seafile_api.rename_file(repo_id, parent_dir, old_dir_name, + checked_newname, username) + return Response({'success': True}) + except SearpcError, e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + else: + error_msg = "Operation not supported." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + def delete(self, request, repo_id, format=None): + # delete dir or file + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = request.GET.get('p', None) + if not path: + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + dir_id = seafile_api.get_dir_id_by_path(repo_id, path) + if not dir_id: + error_msg = 'Folder %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, path) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + if path == '/': + error_msg = 'Can not delete root path.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if path[-1] == '/': + path = path[:-1] + + username = request.user.username + parent_dir = os.path.dirname(path) + file_name = os.path.basename(path) + try: + seafile_api.del_file(repo_id, parent_dir, file_name, username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + return reloaddir(request, repo, parent_dir) + else: + return Response({'success': True}) diff --git a/seahub/api2/endpoints/file.py b/seahub/api2/endpoints/file.py new file mode 100644 index 0000000000..c514ee8c60 --- /dev/null +++ b/seahub/api2/endpoints/file.py @@ -0,0 +1,434 @@ +import os +import logging + +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 + +from seahub.api2.throttling import UserRateThrottle +from seahub.api2.authentication import TokenAuthentication +from seahub.api2.utils import api_error +from seahub.api2.views import reloaddir, get_shared_link + +from seahub.utils import gen_file_get_url, EMPTY_SHA1, \ + check_filename_with_rename +from seahub.views import check_folder_permission, check_file_lock +from seahub.views.file import send_file_access_msg + +from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, FILE_LOCK_EXPIRATION_DAYS + +from seaserv import seafile_api +from pysearpc import SearpcError + +logger = logging.getLogger(__name__) + +class FileView(APIView): + """ + Support uniform interface for file related operations, + including create/delete/rename/view, etc. + """ + + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated, ) + throttle_classes = (UserRateThrottle, ) + + def get(self, request, repo_id, format=None): + + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = request.GET.get('p', None) + if not path: + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + try: + file_id = seafile_api.get_file_id_by_path(repo_id, path) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not file_id: + error_msg = 'File %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, path) is None: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # send stats message + send_file_access_msg(request, repo, path, 'api') + op = request.GET.get('op', 'download') + if op == 'download': + reuse = request.GET.get('reuse', '0') + if reuse not in ('1', '0'): + error_msg = "If you want to reuse file server access token for download file, you should set 'reuse' argument as '1'." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + use_onetime = False if reuse == '1' else True + + file_name = os.path.basename(path) + token = seafile_api.get_fileserver_access_token(repo_id, + file_id, op, request.user.username, use_onetime) + redirect_url = gen_file_get_url(token, file_name) + + return Response({"url": redirect_url}) + + elif op == 'downloadblks': + blklist = [] + encrypted = False + enc_version = 0 + if file_id != EMPTY_SHA1: + try: + blks = seafile_api.list_blocks_by_file_id(repo_id, file_id) + blklist = blks.split('\n') + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + blklist = [i for i in blklist if len(i) == 40] + if len(blklist) > 0: + repo = seafile_api.get_repo(repo_id) + encrypted = repo.encrypted + enc_version = repo.enc_version + + res = { + 'file_id': file_id, + 'blklist': blklist, + 'encrypted': encrypted, + 'enc_version': enc_version, + } + + return Response(res) + + elif op == 'sharelink': + link = get_shared_link(request, repo_id, path) + return Response({"link": link}) + + else: + error_msg = 'op invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + def post(self, request, repo_id, format=None): + # rename, move, copy or create file + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = request.GET.get('p', '') + if not path or path[0] != '/': + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + username = request.user.username + parent_dir = os.path.dirname(path) + operation = request.POST.get('operation', '') + if operation.lower() == 'rename': + try: + file_id = seafile_api.get_file_id_by_path(repo_id, path) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not file_id: + error_msg = 'File %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, path) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + newname = request.POST.get('newname', '') + if not newname: + error_msg = 'newname invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if len(newname) > MAX_UPLOAD_FILE_NAME_LEN: + error_msg = 'newname is too long.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + oldname = os.path.basename(path) + if oldname == newname: + error_msg = 'The new name is the same to the old' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + newname = check_filename_with_rename(repo_id, parent_dir, newname) + try: + seafile_api.rename_file(repo_id, parent_dir, oldname, newname, username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + return reloaddir(request, repo, parent_dir) + else: + return Response({'success': True}) + + elif operation.lower() == 'move': + try: + file_id = seafile_api.get_file_id_by_path(repo_id, path) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not file_id: + error_msg = 'File %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # check src validation + src_repo_id = repo_id + src_dir = os.path.dirname(path) + if check_folder_permission(request, src_repo_id, src_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # check dst validation + dst_repo_id = request.POST.get('dst_repo', '') + dst_dir = request.POST.get('dst_dir', '') + if not dst_repo_id: + error_msg = 'dst_repo_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + dst_repo = seafile_api.get_repo(dst_repo_id) + if not dst_repo: + error_msg = 'Library %s not found.' % dst_repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if not dst_dir: + error_msg = 'dst_dir invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + dst_dir_id = seafile_api.get_dir_id_by_path(dst_repo_id, dst_dir) + if not dst_dir_id: + error_msg = 'Folder %s not found.' % dst_dir + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, dst_repo_id, dst_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # move file + if dst_dir[-1] != '/': # Append '/' to the end of directory if necessary + dst_dir += '/' + + if src_repo_id == dst_repo_id and src_dir == dst_dir: + return Response({'success': True}) + + filename = os.path.basename(path) + new_filename = check_filename_with_rename(dst_repo_id, dst_dir, filename) + try: + seafile_api.move_file(src_repo_id, src_dir, filename, dst_repo_id, + dst_dir, new_filename, username, 0, synchronous=1) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + return reloaddir(request, dst_repo, dst_dir) + else: + return Response({'success': True}) + + elif operation.lower() == 'copy': + try: + file_id = seafile_api.get_file_id_by_path(repo_id, path) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not file_id: + error_msg = 'File %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + # check src validation + src_repo_id = repo_id + src_dir = os.path.dirname(path) + if check_folder_permission(request, src_repo_id, src_dir) is None: + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # check dst validation + dst_repo_id = request.POST.get('dst_repo', '') + dst_dir = request.POST.get('dst_dir', '') + if not dst_repo_id: + error_msg = 'dst_repo_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + dst_repo = seafile_api.get_repo(dst_repo_id) + if not dst_repo: + error_msg = 'Library %s not found.' % dst_repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if not dst_dir: + error_msg = 'dst_dir invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + dst_dir_id = seafile_api.get_dir_id_by_path(dst_repo_id, dst_dir) + if not dst_dir_id: + error_msg = 'Folder %s not found.' % dst_dir + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, dst_repo_id, dst_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # copy file + if dst_dir[-1] != '/': # Append '/' to the end of directory if necessary + dst_dir += '/' + + if src_repo_id == dst_repo_id and src_dir == dst_dir: + return Response({'success': True}) + + filename = os.path.basename(path) + new_filename = check_filename_with_rename(dst_repo_id, dst_dir, filename) + try: + seafile_api.copy_file(src_repo_id, src_dir, filename, dst_repo_id, + dst_dir, new_filename, username, 0, synchronous=1) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + return reloaddir(request, dst_repo, dst_dir) + else: + return Response({'success': True}) + + elif operation.lower() == 'create': + try: + parent_dir_id = seafile_api.get_dir_id_by_path(repo_id, parent_dir) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if not parent_dir_id: + error_msg = 'Folder %s not found.' % parent_dir + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, parent_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + new_file_name = os.path.basename(path) + new_file_name = check_filename_with_rename(repo_id, parent_dir, new_file_name) + + try: + seafile_api.post_empty_file(repo_id, parent_dir, new_file_name, username) + except SearpcError, e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + return reloaddir(request, repo, parent_dir) + else: + return Response({'success': True}) + else: + error_msg = "Operation can only be rename, create or move." + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + def put(self, request, repo_id, format=None): + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = request.GET.get('p', '') + if not path: + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + file_id = seafile_api.get_file_id_by_path(repo_id, path) + if not file_id: + error_msg = 'File %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, path) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + username = request.user.username + operation = request.data.get('operation', '') + if operation.lower() == 'lock': + is_locked, locked_by_me = check_file_lock(repo_id, path, username) + if is_locked: + error_msg = 'File is already locked.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # lock file + expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS) + try: + seafile_api.lock_file(repo_id, path.lstrip('/'), username, expire) + return Response({'success': True}) + except SearpcError, e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if operation.lower() == 'unlock': + is_locked, locked_by_me = check_file_lock(repo_id, path, username) + if not is_locked: + error_msg = 'File is not locked.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + if not locked_by_me: + error_msg = 'You can not unlock this file.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + # unlock file + try: + seafile_api.unlock_file(repo_id, path.lstrip('/')) + return Response({'success': True}) + except SearpcError, e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + else: + error_msg = "Operation can only be lock or unlock" + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + def delete(self, request, repo_id, format=None): + # delete file + repo = seafile_api.get_repo(repo_id) + if not repo: + error_msg = 'Library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + path = request.GET.get('p', None) + if not path: + error_msg = 'p invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + file_id = seafile_api.get_file_id_by_path(repo_id, path) + if not file_id: + return Response({'success': True}) + + parent_dir = os.path.dirname(path) + if check_folder_permission(request, repo_id, parent_dir) != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + parent_dir = os.path.dirname(path) + file_name = os.path.basename(path) + try: + seafile_api.del_file(repo_id, parent_dir, + file_name, request.user.username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + if request.GET.get('reloaddir', '').lower() == 'true': + return reloaddir(request, repo, parent_dir) + else: + return Response({'success': True}) diff --git a/seahub/api2/views.py b/seahub/api2/views.py index aa0b13ce4e..3c0cb0c9f6 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -1499,7 +1499,7 @@ def get_shared_link(request, repo_id, path): domain = RequestSite(request).domain file_shared_link = '%s://%s%sf/%s/' % (http_or_https, domain, settings.SITE_ROOT, token) - return Response(file_shared_link) + return file_shared_link def get_repo_file(request, repo_id, file_id, file_name, op, use_onetime=True): if op == 'download': @@ -1546,7 +1546,9 @@ def get_repo_file(request, repo_id, file_id, file_name, op, use_onetime=True): path = request.GET.get('p', None) if path is None: return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.') - return get_shared_link(request, repo_id, path) + + file_shared_link = get_shared_link(request, repo_id, path) + return Response(file_shared_link) def reloaddir(request, repo, parent_dir): try: @@ -1935,7 +1937,7 @@ class FileView(APIView): if not newname: return api_error(status.HTTP_400_BAD_REQUEST, 'New name is missing') - newname = unquote(newname.encode('utf-8')) + newname = newname.encode('utf-8') if len(newname) > settings.MAX_UPLOAD_FILE_NAME_LEN: return api_error(status.HTTP_400_BAD_REQUEST, 'New name is too long') @@ -1987,15 +1989,6 @@ class FileView(APIView): return api_error(status.HTTP_403_FORBIDDEN, 'You do not have permission to move file.') - # names = obj_names.split(':') - # names = map(lambda x: unquote(x).decode('utf-8'), names) - - # if dst_dir.startswith(src_dir): - # for obj_name in names: - # if dst_dir.startswith('/'.join([src_dir, obj_name])): - # return api_error(status.HTTP_409_CONFLICT, - # 'Can not move a dirctory to its subdir') - filename = os.path.basename(path) filename_utf8 = filename.encode('utf-8') new_filename_utf8 = check_filename_with_rename_utf8(dst_repo_id, diff --git a/seahub/urls.py b/seahub/urls.py index 0b9cb0111d..cdf32c35b7 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -23,6 +23,8 @@ from seahub.api2.endpoints.groups import Groups, Group from seahub.api2.endpoints.group_members import GroupMembers, GroupMembersBulk, GroupMember from seahub.api2.endpoints.share_links import ShareLinks, ShareLink from seahub.api2.endpoints.upload_links import UploadLinks, UploadLink +from seahub.api2.endpoints.file import FileView +from seahub.api2.endpoints.dir import DirView # Uncomment the next two lines to enable the admin: #from django.contrib import admin @@ -197,6 +199,8 @@ urlpatterns = patterns( url(r'^api/v2.1/share-link/(?P[a-f0-9]{10})/$', ShareLink.as_view(), name='api-v2.1-share-link'), url(r'^api/v2.1/upload-links/$', UploadLinks.as_view(), name='api-v2.1-upload-links'), url(r'^api/v2.1/upload-link/(?P[a-f0-9]{10})/$', UploadLink.as_view(), name='api-v2.1-upload-link'), + url(r'^api/v2.1/repos/(?P[-0-9-a-f]{36})/file/$', FileView.as_view(), name='api-v2.1-file-view'), + url(r'^api/v2.1/repos/(?P[-0-9-a-f]{36})/dir/$', DirView.as_view(), name='api-v2.1-dir-view'), (r'^avatar/', include('seahub.avatar.urls')), (r'^notification/', include('seahub.notifications.urls')), diff --git a/tests/api/endpoints/test_dir_view.py b/tests/api/endpoints/test_dir_view.py new file mode 100644 index 0000000000..fe238c0fb6 --- /dev/null +++ b/tests/api/endpoints/test_dir_view.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +import os +import json + +from seaserv import seafile_api + +from django.core.urlresolvers import reverse + +from seahub.test_utils import BaseTestCase +from seahub.share.models import FileShare + +from tests.common.utils import randstring + +class FileViewTest(BaseTestCase): + + def create_new_repo(self): + new_repo_id = seafile_api.create_repo(name='test-repo-2', desc='', + username=self.user.username, passwd=None) + + return new_repo_id + + def get_lib_folder_name(self, repo_id): + + url = reverse('list_lib_dir', args=[repo_id]) + resp = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + json_resp = json.loads(resp.content) + + if len(json_resp['dirent_list']) == 0: + return None + + return json_resp['dirent_list'][0]['obj_name'] + + def setUp(self): + self.repo_id = self.repo.id + self.folder_path = self.folder + self.folder_name = os.path.basename(self.folder_path) + + self.url = reverse('api-v2.1-dir-view', args=[self.repo_id]) + + def tearDown(self): + self.remove_repo() + + def test_can_get_dir(self): + self.login_as(self.user) + resp = self.client.get(self.url) + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + + assert json_resp[0]['type'] == 'dir' + assert json_resp[0]['name'] == self.folder_name + + def test_can_create_folder(self): + self.login_as(self.user) + + # delete old folder + resp = self.client.delete(self.url + '?p=' + self.folder_path, {}, 'application/x-www-form-urlencoded') + assert None == self.get_lib_folder_name(self.repo_id) + + # check folder has been deleted + assert None == self.get_lib_folder_name(self.repo_id) + + new_name = randstring(6) + new_folder_path = '/' + new_name + + # create file + data = { + 'operation': 'mkdir', + } + resp = self.client.post(self.url + '?p=' + new_folder_path, data) + + self.assertEqual(200, resp.status_code) + + # check new folder has been created + assert new_name == self.get_lib_folder_name(self.repo_id) + + def test_can_delete_folder(self): + self.login_as(self.user) + + # check folder exist when init + assert self.folder_name == self.get_lib_folder_name(self.repo_id) + + # delete folder + resp = self.client.delete(self.url + '?p=' + self.folder_path, {}, 'application/x-www-form-urlencoded') + assert None == self.get_lib_folder_name(self.repo_id) + + self.assertEqual(200, resp.status_code) + + # check folder has been deleted + assert None == self.get_lib_folder_name(self.repo_id) + + def test_can_rename_folder(self): + self.login_as(self.user) + new_name = randstring(6) + + # check old folder exist + assert self.folder_name == self.get_lib_folder_name(self.repo_id) + + data = {'operation': 'rename', 'newname': new_name} + resp = self.client.post(self.url + '?p=' + self.folder_path, data) + + self.assertEqual(200, resp.status_code) + # check old file has been renamed to new_name + assert new_name == self.get_lib_folder_name(self.repo_id) + + def test_can_post_operation_invalid(self): + self.login_as(self.user) + + data = { + 'operation': 'invalid', + } + resp = self.client.post(self.url + '?p=' + self.folder_path, data) + + self.assertEqual(400, resp.status_code) + diff --git a/tests/api/endpoints/test_file_view.py b/tests/api/endpoints/test_file_view.py new file mode 100644 index 0000000000..f1232af7c1 --- /dev/null +++ b/tests/api/endpoints/test_file_view.py @@ -0,0 +1,235 @@ +# -*- coding: utf-8 -*- +import os +import json + +from seaserv import seafile_api + +from django.core.urlresolvers import reverse + +from seahub.test_utils import BaseTestCase +from seahub.share.models import FileShare + +from tests.common.utils import randstring + +try: + from seahub.settings import LOCAL_PRO_DEV_ENV +except ImportError: + LOCAL_PRO_DEV_ENV = False + + +class FileViewTest(BaseTestCase): + + def create_new_repo(self): + new_repo_id = seafile_api.create_repo(name='test-repo-2', desc='', + username=self.user.username, passwd=None) + + return new_repo_id + + def get_lib_file_name(self, repo_id): + + url = reverse('list_lib_dir', args=[repo_id]) + resp = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + json_resp = json.loads(resp.content) + + if len(json_resp['dirent_list']) == 0: + return None + + return json_resp['dirent_list'][0]['obj_name'] + + def setUp(self): + self.repo_id = self.repo.id + self.file_path = self.file + self.file_name = os.path.basename(self.file_path) + + self.url = reverse('api-v2.1-file-view', args=[self.repo_id]) + + def tearDown(self): + self.remove_repo() + + def test_can_get_file_download_url(self): + self.login_as(self.user) + resp = self.client.get(self.url + '?p=' + self.file_path) + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + assert '8082' in json_resp['url'] + + def test_can_get_file_downloadblks_info(self): + file_id = seafile_api.get_file_id_by_path(self.repo_id, self.file_path) + + self.login_as(self.user) + resp = self.client.get(self.url + '?p=' + self.file_path + '&op=downloadblks') + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + assert json_resp['file_id'] == file_id + + def test_can_get_file_share_link_info(self): + fs = FileShare.objects.create_file_link(self.user.username, + self.repo_id, self.file_path, None, None) + + self.login_as(self.user) + + resp = self.client.get(self.url + '?p=' + self.file_path + '&op=sharelink') + self.assertEqual(200, resp.status_code) + json_resp = json.loads(resp.content) + assert fs.token in json_resp['link'] + + def test_get_operation_invalid(self): + self.login_as(self.user) + resp = self.client.get(self.url + '?p=' + self.file_path + '&op=invalid') + self.assertEqual(400, resp.status_code) + + def test_can_rename_file(self): + self.login_as(self.user) + new_name = randstring(6) + + # check old file name exist + assert self.file_name == self.get_lib_file_name(self.repo_id) + + data = {'operation': 'rename', 'newname': new_name} + resp = self.client.post(self.url + '?p=' + self.file_path, data) + + self.assertEqual(200, resp.status_code) + # check old file has been renamed to new_name + assert new_name == self.get_lib_file_name(self.repo_id) + + def test_can_move_file(self): + self.login_as(self.user) + + # check old file name exist + assert self.file_name == self.get_lib_file_name(self.repo_id) + + # move file + dst_repo_id = self.create_new_repo() + data = { + 'operation': 'move', + 'dst_repo': dst_repo_id, + 'dst_dir': '/', + } + resp = self.client.post(self.url + '?p=' + self.file_path, data) + + self.assertEqual(200, resp.status_code) + + # check old file has been delete + assert self.get_lib_file_name(self.repo_id) == None + + # check old file has been moved to dst repo + assert self.file_name == self.get_lib_file_name(dst_repo_id) + + self.remove_repo(dst_repo_id) + + def test_can_copy_file(self): + self.login_as(self.user) + + # check old file name exist + assert self.file_name == self.get_lib_file_name(self.repo_id) + + # copy file + dst_repo_id = self.create_new_repo() + data = { + 'operation': 'copy', + 'dst_repo': dst_repo_id, + 'dst_dir': '/', + } + resp = self.client.post(self.url + '?p=' + self.file_path, data) + + self.assertEqual(200, resp.status_code) + + # check old file still in old repo + assert self.file_name == self.get_lib_file_name(self.repo_id) + + # check old file has been moved to dst repo + assert self.file_name == self.get_lib_file_name(dst_repo_id) + + self.remove_repo(dst_repo_id) + + def test_can_delete_file(self): + self.login_as(self.user) + + # check old file name exist + assert self.file_name == self.get_lib_file_name(self.repo_id) + + # delete file + resp = self.client.delete(self.url + '?p=' + self.file_path, {}, 'application/x-www-form-urlencoded') + + self.assertEqual(200, resp.status_code) + + # check old file has been deleted + assert None == self.get_lib_file_name(self.repo_id) + + def test_can_create_file(self): + self.login_as(self.user) + # delete old file + resp = self.client.delete(self.url + '?p=' + self.file_path, {}, 'application/x-www-form-urlencoded') + assert None == self.get_lib_file_name(self.repo_id) + + new_name = randstring(6) + new_file_path = '/' + new_name + + # create file + data = { + 'operation': 'create', + } + resp = self.client.post(self.url + '?p=' + new_file_path, data) + + self.assertEqual(200, resp.status_code) + + # check old file still in old repo + assert new_name == self.get_lib_file_name(self.repo_id) + + def test_post_operation_invalid(self): + self.login_as(self.user) + # create file + data = { + 'operation': 'invalid', + } + resp = self.client.post(self.url + '?p=' + self.file_path, data) + self.assertEqual(400, resp.status_code) + + def test_can_lock_file(self): + if not LOCAL_PRO_DEV_ENV: + return + + self.login_as(self.user) + + # check file NOT locked when init + return_value = seafile_api.check_file_lock(self.repo_id, + self.file_path.lstrip('/'), self.user.username) + + assert return_value == 0 + + # lock file + data = 'operation=lock' + resp = self.client.put(self.url + '?p=' + self.file_path, data, 'application/x-www-form-urlencoded') + + self.assertEqual(200, resp.status_code) + + # check file has been locked + return_value = seafile_api.check_file_lock(self.repo_id, + self.file_path.lstrip('/'), self.user.username) + assert return_value == 2 + + def test_can_unlock_file(self): + if not LOCAL_PRO_DEV_ENV: + return + + self.login_as(self.user) + + # lock file for test + seafile_api.lock_file(self.repo_id, self.file_path.lstrip('/'), + self.user.username, -1) + + # check file has been locked when init + return_value = seafile_api.check_file_lock(self.repo_id, + self.file_path.lstrip('/'), self.user.username) + assert return_value == 2 + + # unlock file + data = 'operation=unlock' + resp = self.client.put(self.url + '?p=' + self.file_path, data, 'application/x-www-form-urlencoded') + + self.assertEqual(200, resp.status_code) + + # check file has been unlocked + return_value = seafile_api.check_file_lock(self.repo_id, + self.file_path.lstrip('/'), self.user.username) + assert return_value == 0