diff --git a/seahub/api2/urls.py b/seahub/api2/urls.py index 8a0f059c8d..62be47da33 100644 --- a/seahub/api2/urls.py +++ b/seahub/api2/urls.py @@ -41,13 +41,14 @@ urlpatterns = patterns('', url(r'^repos/(?P[-0-9-a-f]{36})/file/detail/$', FileDetailView.as_view()), url(r'^repos/(?P[-0-9-a-f]{36})/file/history/$', FileHistory.as_view()), url(r'^repos/(?P[-0-9-a-f]{36})/file/revision/$', FileRevision.as_view()), - url(r'^repos/(?P[-0-9-a-f]{36})/file/revert/$', FileRevert.as_view()), + url(r'^repos/(?P[-0-9-a-f]{36})/file/revert/$', FileRevert.as_view(), name='api2-file-revert'), url(r'^repos/(?P[-0-9-a-f]{36})/file/shared-link/$', FileSharedLinkView.as_view()), url(r'^repos/(?P[-0-9-a-f]{36})/dir/$', DirView.as_view(), name='DirView'), url(r'^repos/(?P[-0-9-a-f]{36})/dir/sub_repo/$', DirSubRepoView.as_view()), url(r'^repos/(?P[-0-9-a-f]{36})/dir/share/$', DirShareView.as_view()), url(r'^repos/(?P[-0-9-a-f]{36})/dir/shared_items/$', DirSharedItemsEndpoint.as_view(), name="api2-dir-shared-items"), url(r'^repos/(?P[-0-9-a-f]{36})/dir/download/$', DirDownloadView.as_view()), + url(r'^repos/(?P[-0-9-a-f]{36})/dir/revert/$', DirRevert.as_view(), name='api2-dir-revert'), url(r'^repos/(?P[-0-9-a-f]{36})/thumbnail/$', ThumbnailView.as_view(), name='api2-thumbnail'), url(r'^starredfiles/', StarredFileView.as_view(), name='starredfiles'), url(r'^shared-repos/$', SharedRepos.as_view(), name='sharedrepos'), diff --git a/seahub/api2/views.py b/seahub/api2/views.py index 454407db82..c938d933f8 100644 --- a/seahub/api2/views.py +++ b/seahub/api2/views.py @@ -2026,38 +2026,44 @@ class FileDetailView(APIView): content_type=json_content_type) class FileRevert(APIView): - authentication_classes = (TokenAuthentication, ) + authentication_classes = (TokenAuthentication, SessionAuthentication) permission_classes = (IsAuthenticated,) throttle_classes = (UserRateThrottle, ) def put(self, request, repo_id, format=None): - path = request.DATA.get('p', '') + path = request.DATA.get('p', None) + commit_id = request.DATA.get('commit_id', None) + if not path: - return api_error(status.HTTP_400_BAD_REQUEST, 'Path is missing.') + error_msg = 'path invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not commit_id: + error_msg = 'commit_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not seafile_api.get_repo(repo_id): + error_msg = 'library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if not seafile_api.get_file_id_by_commit_and_path(repo_id, commit_id, path): + error_msg = 'file %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, '/') != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) username = request.user.username - is_locked, locked_by_me = check_file_lock(repo_id, path, username) - if (is_locked, locked_by_me) == (None, None): - return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, 'Check file lock error') - - if is_locked and not locked_by_me: - return api_error(status.HTTP_403_FORBIDDEN, 'File is locked') - - parent_dir = os.path.dirname(path) - if check_folder_permission(request, repo_id, parent_dir) != 'rw': - return api_error(status.HTTP_403_FORBIDDEN, - 'You do not have permission to access this folder.') - - path = unquote(path.encode('utf-8')) - commit_id = unquote(request.DATA.get('commit_id', '').encode('utf-8')) try: - ret = seafserv_threaded_rpc.revert_file(repo_id, commit_id, - path, username) + seafile_api.revert_file(repo_id, commit_id, path, username) except SearpcError as e: logger.error(e) - return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, "Internal error") + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + return Response({'success': True}) - return HttpResponse(json.dumps({"ret": ret}), status=200, content_type=json_content_type) class FileRevision(APIView): authentication_classes = (TokenAuthentication, ) @@ -2457,6 +2463,45 @@ class DirDownloadView(APIView): return HttpResponse(json.dumps(redirect_url), status=200, content_type=json_content_type) +class DirRevert(APIView): + authentication_classes = (TokenAuthentication, SessionAuthentication) + permission_classes = (IsAuthenticated,) + throttle_classes = (UserRateThrottle, ) + + def put(self, request, repo_id): + path = request.DATA.get('p', None) + commit_id = request.DATA.get('commit_id', None) + + if not path: + error_msg = 'path invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not commit_id: + error_msg = 'commit_id invalid.' + return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + + if not seafile_api.get_repo(repo_id): + error_msg = 'library %s not found.' % repo_id + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if not seafile_api.get_dir_id_by_commit_and_path(repo_id, commit_id, path): + error_msg = 'folder %s not found.' % path + return api_error(status.HTTP_404_NOT_FOUND, error_msg) + + if check_folder_permission(request, repo_id, '/') != 'rw': + error_msg = 'Permission denied.' + return api_error(status.HTTP_403_FORBIDDEN, error_msg) + + username = request.user.username + try: + seafile_api.revert_dir(repo_id, commit_id, path, username) + except SearpcError as e: + logger.error(e) + error_msg = 'Internal Server Error' + return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) + + return Response({'success': True}) + class DirShareView(APIView): authentication_classes = (TokenAuthentication, ) permission_classes = (IsAuthenticated,) diff --git a/seahub/templates/repo_dir_recycle_view.html b/seahub/templates/repo_dir_recycle_view.html index 07745e5a75..67a025ae42 100644 --- a/seahub/templates/repo_dir_recycle_view.html +++ b/seahub/templates/repo_dir_recycle_view.html @@ -125,12 +125,31 @@ var get_more_trash = function(current_scan_stat) { }); } + $('table').on("click", ".restore-file, .restore-dir", function() { - $('
', { - "method": 'POST', - "action": $(this).data('url'), - "html": '' - }).appendTo(document.body).submit(); + var _this = $(this), + commit_id = _this.data('commit_id'), + path = _this.data('path'); + + $.ajax({ + url: _this.data('url'), + type: 'PUT', + dataType: 'json', + cache: false, + beforeSend: prepareCSRFToken, + data: {'commit_id': commit_id, 'p': path}, + success: function(data) { + _this.closest('tr').remove(); + feedback("{% trans "Success" %}", 'success'); + }, + error: function ajaxErrorHandler(xhr, textStatus, errorThrown) { + if (xhr.responseText) { + feedback($.parseJSON(xhr.responseText).error_msg, 'error'); + } else { + feedback("{% trans "Failed. Please check the network." %}", 'error'); + } + } + }); return false; }); diff --git a/seahub/templates/snippets/repo_dir_trash_tr.html b/seahub/templates/snippets/repo_dir_trash_tr.html index 459d761455..5b8245fd88 100644 --- a/seahub/templates/snippets/repo_dir_trash_tr.html +++ b/seahub/templates/snippets/repo_dir_trash_tr.html @@ -8,7 +8,7 @@ {{ dirent.obj_name }} {{ dirent.delete_time|translate_seahub_time }} - {% trans "Restore" %} + {% trans "Restore" %} {% else %} {{ dirent.obj_name }} @@ -23,7 +23,7 @@ {{ dirent.obj_name }} {{ dirent.delete_time|translate_seahub_time }} {{ dirent.file_size|filesizeformat }} - {% trans "Restore" %} + {% trans "Restore" %} {% else %} {{ dirent.obj_name }} @@ -33,4 +33,3 @@ {% endif %} {% endfor %} - diff --git a/tests/api/test_dir_revert.py b/tests/api/test_dir_revert.py new file mode 100644 index 0000000000..2083f42a4b --- /dev/null +++ b/tests/api/test_dir_revert.py @@ -0,0 +1,63 @@ +import os +import json +from seaserv import seafile_api +from django.core.urlresolvers import reverse +from seahub.test_utils import BaseTestCase + +class DirRevertTest(BaseTestCase): + + def setUp(self): + self.repo_id = self.repo.id + self.folder_path = self.folder + self.parent_dir = os.path.dirname(self.folder_path) + self.folder_name = os.path.basename(self.folder_path) + self.username = self.user.username + + self.url = reverse('api2-dir-revert', args=[self.repo_id]) + + def tearDown(self): + self.remove_repo() + + def delete_dir(self): + seafile_api.del_file(self.repo_id, self.parent_dir, + self.folder_name, self.username) + + def get_trash_dir_commit_id(self): + deleted_file = seafile_api.get_deleted(self.repo_id, 0, '/', None) + + return deleted_file[0].commit_id + + def get_lib_folder_name(self): + + url = reverse('list_lib_dir', args=[self.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 test_can_revert_dir(self): + self.login_as(self.user) + + # check file exist when init + assert self.get_lib_folder_name() == self.folder_name + + # delete + self.delete_dir() + + # check file not exist after delete + assert self.get_lib_folder_name() == None + + # get commit_id of deleted file + commit_id = self.get_trash_dir_commit_id() + + resp = self.client.put(self.url, + "p=%s&commit_id=%s" % (self.folder_path, commit_id), + 'application/x-www-form-urlencoded', + ) + self.assertEqual(200, resp.status_code) + + # check file has been reverted + assert self.get_lib_folder_name() == self.folder_name diff --git a/tests/api/test_file_revert.py b/tests/api/test_file_revert.py new file mode 100644 index 0000000000..09ad462691 --- /dev/null +++ b/tests/api/test_file_revert.py @@ -0,0 +1,63 @@ +import os +import json +from seaserv import seafile_api +from django.core.urlresolvers import reverse +from seahub.test_utils import BaseTestCase + +class FileRevertTest(BaseTestCase): + + def setUp(self): + self.repo_id = self.repo.id + self.file_path = self.file + self.parent_dir = os.path.dirname(self.file_path) + self.file_name = os.path.basename(self.file_path) + self.username = self.user.username + + self.url = reverse('api2-file-revert', args=[self.repo_id]) + + def tearDown(self): + self.remove_repo() + + def delete_file(self): + seafile_api.del_file(self.repo_id, self.parent_dir, + self.file_name, self.username) + + def get_trash_file_commit_id(self): + deleted_file = seafile_api.get_deleted(self.repo_id, 0, '/', None) + + return deleted_file[0].commit_id + + def get_lib_file_name(self): + + url = reverse('list_lib_dir', args=[self.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 test_can_revert_file(self): + self.login_as(self.user) + + # check file exist when init + assert self.get_lib_file_name() == self.file_name + + # delete + self.delete_file() + + # check file not exist after delete + assert self.get_lib_file_name() == None + + # get commit_id of deleted file + commit_id = self.get_trash_file_commit_id() + + resp = self.client.put(self.url, + "p=%s&commit_id=%s" % (self.file_path, commit_id), + 'application/x-www-form-urlencoded', + ) + self.assertEqual(200, resp.status_code) + + # check file has been reverted + assert self.get_lib_file_name() == self.file_name