diff --git a/seahub/api2/endpoints/dir.py b/seahub/api2/endpoints/dir.py
index 7d8b6433b8..c6c0a1fdac 100644
--- a/seahub/api2/endpoints/dir.py
+++ b/seahub/api2/endpoints/dir.py
@@ -110,10 +110,12 @@ class DirView(APIView):
return get_dir_entrys_by_id(request, repo, path, dir_id, request_type)
def post(self, request, repo_id, format=None):
- """ Create, rename dir.
+ """ Create, rename, revert dir.
Permission checking:
- 1. user with 'rw' permission.
+ 1. create: user with 'rw' permission for current dir's parent dir;
+ 2. rename: user with 'rw' permission for current dir;
+ 3. revert: user with 'rw' permission for current dir's parent dir;
"""
# argument check
@@ -123,7 +125,7 @@ class DirView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if path == '/':
- error_msg = 'Can not make or rename root dir.'
+ error_msg = 'Can not operate root dir.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
operation = request.data.get('operation', None)
@@ -132,8 +134,8 @@ class DirView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
operation = operation.lower()
- if operation not in ('mkdir', 'rename'):
- error_msg = "operation can only be 'mkdir', 'rename'."
+ if operation not in ('mkdir', 'rename', 'revert'):
+ error_msg = "operation can only be 'mkdir', 'rename' or 'revert'."
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check
@@ -211,6 +213,32 @@ class DirView(APIView):
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+ if operation == 'revert':
+ commit_id = request.data.get('commit_id', None)
+ if not commit_id:
+ error_msg = 'commit_id invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if seafile_api.get_dir_id_by_path(repo_id, path):
+ # dir exists in repo
+ if check_folder_permission(request, repo_id, path) != 'rw':
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+ else:
+ # dir NOT exists in repo
+ if check_folder_permission(request, repo_id, '/') != 'rw':
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ try:
+ seafile_api.revert_dir(repo_id, commit_id, path, username)
+ except Exception 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})
+
def delete(self, request, repo_id, format=None):
""" Delete dir.
diff --git a/seahub/api2/endpoints/file.py b/seahub/api2/endpoints/file.py
index 750ebdada0..28af56ee9d 100644
--- a/seahub/api2/endpoints/file.py
+++ b/seahub/api2/endpoints/file.py
@@ -9,6 +9,8 @@ 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 seahub.api2.throttling import UserRateThrottle
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.utils import api_error
@@ -92,13 +94,14 @@ class FileView(APIView):
return Response(file_info)
def post(self, request, repo_id, format=None):
- """ Create, rename, move, copy file
+ """ Create, rename, move, copy, revert file
Permission checking:
1. create: user with 'rw' permission for current parent dir;
2. rename: user with 'rw' permission for current file;
3. move : user with 'rw' permission for current file, 'rw' permission for dst parent dir;
4. copy : user with 'r' permission for current file, 'rw' permission for dst parent dir;
+ 4. revert: user with 'rw' permission for current file's parent dir;
"""
# argument check
@@ -113,8 +116,8 @@ class FileView(APIView):
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
operation = operation.lower()
- if operation not in ('create', 'rename', 'move', 'copy'):
- error_msg = "operation can only be 'create', 'rename', 'move' or 'copy'."
+ if operation not in ('create', 'rename', 'move', 'copy', 'revert'):
+ error_msg = "operation can only be 'create', 'rename', 'move', 'copy' or 'revert'."
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check
@@ -346,6 +349,42 @@ class FileView(APIView):
dst_file_info = self.get_file_info(username, dst_repo_id, dst_file_path)
return Response(dst_file_info)
+ if operation == 'revert':
+ commit_id = request.data.get('commit_id', None)
+ if not commit_id:
+ error_msg = 'commit_id invalid.'
+ return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
+
+ if seafile_api.get_file_id_by_path(repo_id, path):
+ # file exists in repo
+ if check_folder_permission(request, repo_id, parent_dir) != 'rw':
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ is_locked, locked_by_me = check_file_lock(repo_id, path, username)
+ if (is_locked, locked_by_me) == (None, None):
+ error_msg = _("Check file lock error")
+ return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
+
+ if is_locked and not locked_by_me:
+ error_msg = _("File is locked")
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ else:
+ # file NOT exists in repo
+ if check_folder_permission(request, repo_id, '/') != 'rw':
+ error_msg = 'Permission denied.'
+ return api_error(status.HTTP_403_FORBIDDEN, error_msg)
+
+ try:
+ seafile_api.revert_file(repo_id, commit_id, path, username)
+ except Exception 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})
+
def put(self, request, repo_id, format=None):
""" Currently only for lock and unlock file operation.
diff --git a/seahub/templates/file_revisions.html b/seahub/templates/file_revisions.html
index 2f6346a30b..3079977a79 100644
--- a/seahub/templates/file_revisions.html
+++ b/seahub/templates/file_revisions.html
@@ -70,7 +70,7 @@
{% if commit.id != repo.head_cmmt_id %}
{% if can_revert_file %}
- {% trans 'Restore' %}
+ {% trans 'Restore' %}
{% endif %}
{% endif %}
{% trans 'Download' %}
@@ -86,6 +86,26 @@
{% block extra_script %}
{% endblock %}
diff --git a/seahub/templates/repo_history_view.html b/seahub/templates/repo_history_view.html
index 6e65bc82ca..51b450761b 100644
--- a/seahub/templates/repo_history_view.html
+++ b/seahub/templates/repo_history_view.html
@@ -64,7 +64,7 @@
|  |
{{ dirent.obj_name }} |
|
- {% trans "Restore" %} |
+ {% trans "Restore" %} |
{% endfor %}
@@ -74,7 +74,7 @@
{{ dirent.props.obj_name }} |
{{ dirent.file_size|filesizeformat }} |
- {% trans "Restore" %}
+ {% trans "Restore" %}
{% trans "Download" %}
|
@@ -90,6 +90,33 @@ addConfirmTo($('#repo-revert'), {
'post':true
});
-addFormPost($('.restore-file, .restore-dir'));
+$('.restore-file, .restore-dir').click(function() {
+ var _this = $(this),
+ commit_id = _this.attr('data-commit_id'),
+ path = _this.attr('data-commit_path'),
+ url;
+
+ if (_this.hasClass('restore-file')) {
+ url = "{% url 'api-v2.1-file-view' repo.id %}" + "?p=" + encodeURIComponent(path);
+ } else {
+ url = "{% url 'api-v2.1-dir-view' repo.id %}" + "?p=" + encodeURIComponent(path);
+ }
+
+ $.ajax({
+ url: url,
+ type: 'POST',
+ data: {'operation': 'revert', 'commit_id': commit_id},
+ cache: false,
+ dataType: 'json',
+ beforeSend: prepareCSRFToken,
+ success: function() {
+ _this.closest('tr').remove();
+ feedback("{% trans "Success" %}", 'success');
+ },
+ error: ajaxErrorHandler
+ });
+ return false;
+});
+
{% endblock %}
diff --git a/seahub/urls.py b/seahub/urls.py
index 4c334ae0d7..5df6598786 100644
--- a/seahub/urls.py
+++ b/seahub/urls.py
@@ -82,9 +82,7 @@ urlpatterns = patterns(
# url(r'^home/public/reply/(?P[\d]+)/$', innerpub_msg_reply, name='innerpub_msg_reply'),
# url(r'^home/owner/(?P[^/]+)/$', ownerhome, name='ownerhome'),
- # revert file/dir/repo
- url(r'^repo/revert_file/(?P[-0-9a-f]{36})/$', repo_revert_file, name='repo_revert_file'),
- url(r'^repo/revert_dir/(?P[-0-9a-f]{36})/$', repo_revert_dir, name='repo_revert_dir'),
+ # revert repo
url(r'^repo/history/revert/(?P[-0-9a-f]{36})/$', repo_revert_history, name='repo_revert_history'),
(r'^repo/upload_check/$', validate_filename),
diff --git a/seahub/views/__init__.py b/seahub/views/__init__.py
index 47023c9ca0..d9f921c20b 100644
--- a/seahub/views/__init__.py
+++ b/seahub/views/__init__.py
@@ -936,94 +936,6 @@ def render_file_revisions (request, repo_id):
'days': days,
}, context_instance=RequestContext(request))
-@login_required
-@require_POST
-def repo_revert_file(request, repo_id):
- repo = get_repo(repo_id)
- if not repo:
- raise Http404
-
- commit_id = request.GET.get('commit')
- path = request.GET.get('p')
-
- if not (commit_id and path):
- return render_error(request, _(u"Invalid arguments"))
-
- referer = request.META.get('HTTP_REFERER', None)
- next = settings.SITE_ROOT if referer is None else referer
-
- username = request.user.username
- # perm check
- if check_folder_permission(request, repo.id, path) != 'rw':
- messages.error(request, _("Permission denied"))
- return HttpResponseRedirect(next)
-
- is_locked, locked_by_me = check_file_lock(repo_id, path, username)
- if (is_locked, locked_by_me) == (None, None):
- messages.error(request, _("Check file lock error"))
- return HttpResponseRedirect(next)
-
- if is_locked and not locked_by_me:
- messages.error(request, _("File is locked"))
- return HttpResponseRedirect(next)
-
- try:
- ret = seafile_api.revert_file(repo_id, commit_id, path, username)
- except Exception as e:
- logger.error(e)
- messages.error(request, _('Failed to restore, please try again later.'))
- return HttpResponseRedirect(next)
-
- if ret == 1:
- root_url = reverse('view_common_lib_dir', args=[repo_id, ''])
- msg = _(u'Successfully revert %(path)s to root directory.') % {"path": escape(path.lstrip('/')), "root": root_url}
- messages.success(request, msg, extra_tags='safe')
- else:
- file_view_url = reverse('view_lib_file', args=[repo_id, path.encode('utf-8')])
- msg = _(u'Successfully revert %(path)s') % {"url": file_view_url, "path": escape(path.lstrip('/'))}
- messages.success(request, msg, extra_tags='safe')
-
- return HttpResponseRedirect(next)
-
-@login_required
-@require_POST
-def repo_revert_dir(request, repo_id):
- repo = get_repo(repo_id)
- if not repo:
- raise Http404
-
- commit_id = request.GET.get('commit')
- path = request.GET.get('p')
-
- if not (commit_id and path):
- return render_error(request, _(u"Invalid arguments"))
-
- referer = request.META.get('HTTP_REFERER', None)
- next = settings.SITE_ROOT if referer is None else referer
-
- # perm check
- if check_folder_permission(request, repo.id, path) != 'rw':
- messages.error(request, _("Permission denied"))
- return HttpResponseRedirect(next)
-
- try:
- ret = seafile_api.revert_dir(repo_id, commit_id, path, request.user.username)
- except Exception as e:
- logger.error(e)
- messages.error(request, _('Failed to restore, please try again later.'))
- return HttpResponseRedirect(next)
-
- if ret == 1:
- root_url = reverse('view_common_lib_dir', args=[repo_id, ''])
- msg = _(u'Successfully revert %(path)s to root directory.') % {"path": escape(path.lstrip('/')), "url": root_url}
- messages.success(request, msg, extra_tags='safe')
- else:
- dir_view_url = reverse('view_common_lib_dir', args=[repo_id, path.strip('/').encode('utf-8')])
- msg = _(u'Successfully revert %(path)s') % {"url": dir_view_url, "path": escape(path.lstrip('/'))}
- messages.success(request, msg, extra_tags='safe')
-
- return HttpResponseRedirect(next)
-
@login_required
def file_revisions(request, repo_id):
"""List file revisions in file version history page.
diff --git a/tests/api/endpoints/test_dir_view.py b/tests/api/endpoints/test_dir_view.py
index f1c437277d..fda1bab488 100644
--- a/tests/api/endpoints/test_dir_view.py
+++ b/tests/api/endpoints/test_dir_view.py
@@ -235,6 +235,76 @@ class DirViewTest(BaseTestCase):
resp = self.client.post(self.url + '?p=' + self.folder_path, data)
self.assertEqual(403, resp.status_code)
+ def test_can_revert_folder(self):
+ self.login_as(self.user)
+
+ # first rename dir
+ new_name = randstring(6)
+ seafile_api.rename_file(self.repo_id, '/', self.folder_name,
+ new_name, self.user_name)
+ new_dir_path = '/' + new_name
+
+ # get commit list
+ commits = seafile_api.get_commit_list(self.repo_id, -1, -1)
+
+ # then revert dir
+ data = {
+ 'operation': 'revert',
+ 'commit_id': commits[0].id
+ }
+ resp = self.client.post(self.url + '?p=' + new_dir_path, data)
+
+ self.assertEqual(200, resp.status_code)
+
+ def test_revert_folder_with_invalid_user_permission(self):
+ # first rename dir
+ new_name = randstring(6)
+ seafile_api.rename_file(self.repo_id, '/', self.folder_name,
+ new_name, self.user_name)
+ new_dir_path = '/' + new_name
+
+ # get commit list
+ commits = seafile_api.get_commit_list(self.repo_id, -1, -1)
+
+ # then revert dir
+ data = {
+ 'operation': 'revert',
+ 'commit_id': commits[0].id
+ }
+ resp = self.client.post(self.url + '?p=' + new_dir_path, data)
+ self.assertEqual(403, resp.status_code)
+
+ def test_revert_folder_with_r_permission(self):
+ # first rename dir
+ new_name = randstring(6)
+ seafile_api.rename_file(self.repo_id, '/', self.folder_name,
+ new_name, self.user_name)
+ new_dir_path = '/' + new_name
+
+ # get commit list
+ commits = seafile_api.get_commit_list(self.repo_id, -1, -1)
+
+ self.share_repo_to_admin_with_r_permission()
+ self.login_as(self.admin)
+
+ # then revert dir
+ data = {
+ 'operation': 'revert',
+ 'commit_id': commits[0].id
+ }
+ resp = self.client.post(self.url + '?p=' + new_dir_path, data)
+ self.assertEqual(403, resp.status_code)
+
+ def test_revert_folder_without_commit_id(self):
+ self.login_as(self.user)
+
+ # then revert dir
+ data = {
+ 'operation': 'revert',
+ }
+ resp = self.client.post(self.url + '?p=' + self.folder_path, data)
+ self.assertEqual(400, resp.status_code)
+
# for test http DELETE request
def test_can_delete_folder(self):
self.login_as(self.user)
diff --git a/tests/api/endpoints/test_file_view.py b/tests/api/endpoints/test_file_view.py
index 07a46b8df6..8c6231b858 100644
--- a/tests/api/endpoints/test_file_view.py
+++ b/tests/api/endpoints/test_file_view.py
@@ -464,6 +464,76 @@ class FileViewTest(BaseTestCase):
resp = self.client.post(url + '?p=/' + admin_file_name, data)
self.assertEqual(403, resp.status_code)
+ def test_can_revert_file(self):
+ self.login_as(self.user)
+
+ # first rename file
+ new_name = randstring(6)
+ seafile_api.rename_file(self.repo_id, '/', self.file_name,
+ new_name, self.user_name)
+ new_file_path = '/' + new_name
+
+ # get file revisions
+ commits = seafile_api.get_file_revisions(
+ self.repo_id, new_file_path, -1, -1, 100)
+
+ # then revert file
+ data = {
+ 'operation': 'revert',
+ 'commit_id': commits[0].id
+ }
+ resp = self.client.post(self.url + '?p=' + new_file_path, data)
+
+ self.assertEqual(200, resp.status_code)
+
+ def test_revert_file_with_invalid_user_permission(self):
+ # first rename file
+ new_name = randstring(6)
+ seafile_api.rename_file(self.repo_id, '/', self.file_name,
+ new_name, self.user_name)
+ new_file_path = '/' + new_name
+
+ # get file revisions
+ commits = seafile_api.get_file_revisions(
+ self.repo_id, new_file_path, -1, -1, 100)
+
+ # then revert file
+ data = {
+ 'operation': 'revert',
+ 'commit_id': commits[0].id
+ }
+ resp = self.client.post(self.url + '?p=' + new_file_path, data)
+ self.assertEqual(403, resp.status_code)
+
+ def test_revert_file_with_r_permission(self):
+ # first rename file
+ new_name = randstring(6)
+ seafile_api.rename_file(self.repo_id, '/', self.file_name,
+ new_name, self.user_name)
+ new_file_path = '/' + new_name
+
+ # get file revisions
+ commits = seafile_api.get_file_revisions(
+ self.repo_id, new_file_path, -1, -1, 100)
+
+ self.share_repo_to_admin_with_r_permission()
+ self.login_as(self.admin)
+ # then revert file
+ data = {
+ 'operation': 'revert',
+ 'commit_id': commits[0].id
+ }
+ resp = self.client.post(self.url + '?p=' + new_file_path, data)
+ self.assertEqual(403, resp.status_code)
+
+ def test_revert_file_without_commit_id(self):
+ self.login_as(self.user)
+ data = {
+ 'operation': 'revert',
+ }
+ resp = self.client.post(self.url + '?p=' + self.file_path, data)
+ self.assertEqual(400, resp.status_code)
+
# for test http PUT request
def test_can_lock_file(self):