1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-04 08:28:11 +00:00

update revert file/dir

This commit is contained in:
lian
2016-09-13 11:16:12 +08:00
parent 93a85e014c
commit 58737d62f7
8 changed files with 268 additions and 104 deletions

View File

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

View File

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

View File

@@ -70,7 +70,7 @@
<td>
{% if commit.id != repo.head_cmmt_id %}
{% if can_revert_file %}
<a href="#" data-url="{% url "repo_revert_file" repo.id %}?commit={{ commit.id }}&p={{commit.path|urlencode}}" class="op vh restore-file">{% trans 'Restore' %}</a>
<a href="#" class="op vh restore-file" data-commit_id="{{ commit.id }}" data-commit_path="{{ commit.path }}">{% trans 'Restore' %}</a>
{% endif %}
{% endif %}
<a href="{% url "download_file" repo.id commit.rev_file_id %}?p={{commit.path|urlencode}}" class="op vh">{% trans 'Download' %}</a>
@@ -86,6 +86,26 @@
{% block extra_script %}
<script type="text/javascript">
addFormPost($('.restore-file'));
$('.restore-file').click(function() {
var _this = $(this),
commit_id = _this.attr('data-commit_id'),
path = _this.attr('data-commit_path');
$.ajax({
url: "{% url 'api-v2.1-file-view' repo.id %}" + "?p=" + encodeURIComponent(path),
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;
});
</script>
{% endblock %}

View File

@@ -64,7 +64,7 @@
<td class="alc"><img src="{{ MEDIA_URL }}img/folder-24.png" alt="{% trans "Directory"%}" /></td>
<td><a href="{% url 'repo_history_view' repo.id %}?commit_id={{ current_commit.id }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}">{{ dirent.obj_name }}</a></td>
<td></td>
<td><a class="op vh restore-dir" href="#" data-url="{% url 'repo_revert_dir' repo.id %}?commit={{ current_commit.id }}&p={{ path|urlencode }}{{dirent.obj_name|urlencode}}">{% trans "Restore" %}</a></td>
<td><a class="op vh restore-dir" data-commit_id="{{ current_commit.id }}" data-commit_path="{{ path }}{{ dirent.obj_name }}" href="#">{% trans "Restore" %}</a></td>
</tr>
{% endfor %}
@@ -74,7 +74,7 @@
<td><a class="normal" href="{% url 'view_snapshot_file' repo.props.id %}?obj_id={{ dirent.props.obj_id }}&commit_id={{ current_commit.id }}&p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}" target="_blank">{{ dirent.props.obj_name }}</a></td>
<td>{{ dirent.file_size|filesizeformat }}</td>
<td>
<a class="op vh restore-file" data-url="{% url 'repo_revert_file' repo.id %}?commit={{ current_commit.id }}&p={{ path|urlencode }}{{dirent.obj_name|urlencode}}" href="#">{% trans "Restore" %}</a>
<a class="op vh restore-file" data-commit_id="{{ current_commit.id }}" data-commit_path="{{ path }}{{ dirent.obj_name }}" href="#">{% trans "Restore" %}</a>
<a class="op vh" href="{% url 'download_file' repo.id dirent.obj_id%}?file_name={{ dirent.obj_name|urlencode }}&p={{path|urlencode}}{{ dirent.obj_name|urlencode }}">{% trans "Download" %}</a>
</td>
</tr>
@@ -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;
});
</script>
{% endblock %}

View File

@@ -82,9 +82,7 @@ urlpatterns = patterns(
# url(r'^home/public/reply/(?P<msg_id>[\d]+)/$', innerpub_msg_reply, name='innerpub_msg_reply'),
# url(r'^home/owner/(?P<owner_name>[^/]+)/$', ownerhome, name='ownerhome'),
# revert file/dir/repo
url(r'^repo/revert_file/(?P<repo_id>[-0-9a-f]{36})/$', repo_revert_file, name='repo_revert_file'),
url(r'^repo/revert_dir/(?P<repo_id>[-0-9a-f]{36})/$', repo_revert_dir, name='repo_revert_dir'),
# revert repo
url(r'^repo/history/revert/(?P<repo_id>[-0-9a-f]{36})/$', repo_revert_history, name='repo_revert_history'),
(r'^repo/upload_check/$', validate_filename),

View File

@@ -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 <a href="%(root)s">root directory.</a>') % {"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 <a href="%(url)s">%(path)s</a>') % {"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 <a href="%(url)s">root directory.</a>') % {"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 <a href="%(url)s">%(path)s</a>') % {"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.

View File

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

View File

@@ -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):