1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-25 14:50:29 +00:00

[repo] added feature 'select multi dirents, and del/mv/cp' and fixed sort bug

This commit is contained in:
llj
2013-08-30 13:46:56 +08:00
parent 698c2ab592
commit aad993163e
7 changed files with 494 additions and 26 deletions

View File

@@ -262,7 +262,17 @@ input.btn-disabled:hover {/*for input*/
*[data-href] { *[data-href] {
cursor: pointer; cursor: pointer;
} }
.op-icon-btn {/* icon btn */
font-size:14px;
padding:4px 12px;
line-height:20px;
text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);
background-color:#f5f5f5;background-image:-moz-linear-gradient(top, #ffffff, #e6e6e6);background-image:-webkit-linear-gradient(top, #ffffff, #e6e6e6);background-image:linear-gradient(to bottom, #ffffff, #e6e6e6);background-repeat:repeat-x;border:1px solid #cccccc;border-bottom-color:#b3b3b3;border-radius:4px;box-shadow:inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
}
.op-icon-btn:hover {
background-color: #E6E6E6;
background-position: 0 -15px; /* to rm background-image */
}
.op-list li, .op-list li,
.modalCloseImg, .modalCloseImg,
.add { .add {
@@ -439,8 +449,8 @@ textarea:-moz-placeholder {/* for FF */
} }
.checkbox-orig, .checkbox-orig,
.checkbox { .checkbox {
width:13px; width:14px;
height:13px; height:14px;
} }
.checkbox-checked { .checkbox-checked {
background:transparent url('../img/tick.png') no-repeat scroll 1px 3px; background:transparent url('../img/tick.png') no-repeat scroll 1px 3px;
@@ -1376,15 +1386,18 @@ textarea:-moz-placeholder {/* for FF */
table-layout:auto; table-layout:auto;
margin:0; margin:0;
} }
.repo-file-list .select {
width:15px;
}
.repo-file-list .star { .repo-file-list .star {
width:30px; width:25px;
} }
.repo-file-list .dirent-icon { .repo-file-list .dirent-icon {
width:32px; width:27px;
} }
.repo-file-list .dirent-name { .repo-file-list .dirent-name {
display:inline-block; display:inline-block;
width:405px; width:400px;
} }
.repo-file-list .dirent-size { .repo-file-list .dirent-size {
width:89px; width:89px;
@@ -1395,15 +1408,20 @@ textarea:-moz-placeholder {/* for FF */
.repo-file-list .dirent-op { .repo-file-list .dirent-op {
width:250px; width:250px;
} }
.repo-file-list .checkbox-label,
.repo-file-list .checkbox {
margin:0;
}
.repo-file-list th { .repo-file-list th {
padding-top:20px; padding-top:20px;
} }
.repo-file-list .fixed-hd { #repo-file-list .fixed-hd {
position:fixed; position:fixed;
width:950px; width:950px;
background:#fff; background:#fff;
top:0; top:0;
z-index:11; /*make it on top of dirent op(popup)*/ z-index:11; /*make it on top of dirent op(popup)*/
border-color:#efefef; /*for repo-file-list-topbar*/
} }
.repo-file-list .fixed-hd th { .repo-file-list .fixed-hd th {
box-shadow:0 2px 2px -3px #aaa; box-shadow:0 2px 2px -3px #aaa;
@@ -1416,6 +1434,7 @@ textarea:-moz-placeholder {/* for FF */
.file-star { .file-star {
cursor:pointer; cursor:pointer;
line-height:19px; line-height:19px;
vertical-align:middle;
} }
.repo-file-list .repo-file-op { .repo-file-list .repo-file-op {
position:relative; position:relative;
@@ -1485,6 +1504,18 @@ textarea:-moz-placeholder {/* for FF */
color:#666; color:#666;
margin-left:5px; margin-left:5px;
} }
#dirents-op {
position:fixed;
}
#dirents-op .op-icon-btn {
display:block;
margin-bottom:6px;
}
#dirents-op .op-icon-btn .icon-trash {
display:inline-block;
width:14px;
text-align:center;
}
.lsch, .lsch,
.lsch-encrypted, .lsch-encrypted,
.file-diff { .file-diff {

View File

@@ -466,6 +466,8 @@ $(document).click(function(e) {
{'name':'plus-sign-alt', 'con':'f0fe'}, {'name':'plus-sign-alt', 'con':'f0fe'},
{'name':'upload', 'con':'f01b'}, {'name':'upload', 'con':'f01b'},
{'name':'ban-circle', 'con':'f05e'}, {'name':'ban-circle', 'con':'f05e'},
{'name':'move', 'con':'f047'},
{'name':'copy', 'con':'f0c5'},
{'name':'upload-alt', 'con':'f093'} {'name':'upload-alt', 'con':'f093'}
]; ];
function setCon(icon, icon_class_prefix, icon_list) { function setCon(icon, icon_class_prefix, icon_list) {

View File

@@ -44,6 +44,13 @@
</div> </div>
<!-- popups --> <!-- popups -->
{% if user_perm == 'rw' %}
<div id="dirents-op" class="hide">
<button id="del-dirents" title="{% trans "Delete"%}" class="op-icon-btn"><span class="icon-trash"></span></button>
<button id="mv-dirents" title="{% trans "Move"%}" class="op-icon-btn"><span class="icon-move"></span></button>
<button id="cp-dirents" title="{% trans "Copy"%}" class="op-icon-btn"><span class="icon-copy"></span></button>
</div>
{% endif %}
<div id="upload-file-dialog" class="hide"> <div id="upload-file-dialog" class="hide">
<h3>{% trans "Upload Files" %}</h3> <h3>{% trans "Upload Files" %}</h3>
{% if no_quota %} {% if no_quota %}
@@ -312,6 +319,211 @@ $('#repo-setting-form').submit(function() {
}); });
{% endif %} {% endif %}
function setDirentsOpPos() {
var dirents_op = $('#dirents-op');
dirents_op.css({
'left': $('#repo-file-list').offset().left - dirents_op.outerWidth(true) - 20,
'top': $(window).height()/2
});
}
$('#del-dirents').click(function() {
$('#confirm-popup').modal({appendTo:'#main'});
$('#simplemodal-container').css({'height':'auto'});
$('#confirm-con').html('<h3>' + "{% trans "Delete Items" %}" + '</h3><p>' + "{% trans "Are you sure you want to delete these selected items?" %}" + '</p>');
$('#confirm-yes').unbind().click(del_dirents);
});
var del_dirents = function() {
$('#confirm-popup').append('<p style="color:red;">' + "{% trans "Processing..." %}" + '</p>');
var dirents = $('.checkbox-checked').parents('.dir-item, .file-item'),
dirents_names = [];
dirents.each(function() {
dirents_names.push($(this).data('name'));
});
$.ajax({
url: '{% url 'delete_dirents' repo.id %}' + '?parent_dir=' + e(cur_path),
type: 'POST',
dataType: 'json',
beforeSend: prepareCSRFToken,
traditional: true,
data: {
'dirents_names': dirents_names
},
success: function(data) {
var deleted_len = data['deleted'].length,
msg_s, msg_f;
if (deleted_len > 0) {
if (deleted_len == dirents_names.length) {
dirents.remove();
} else {
dirents.each(function() {
if ($(this).data('name') in data['deleted']) {
$(this).remove();
}
});
}
if (data['deleted'].length > 1) {
msg_s = "{% trans "Successfully deleted %(name)s and %(amount)s other items." %}";
} else {
msg_s = "{% trans "Successfully deleted %(name)s." %}";
}
msg_s = msg_s.replace('%(name)s', data['deleted'][0]).replace('%(amount)s', data['deleted'].length - 1);
feedback(msg_s, 'success');
updateCmt();
}
if (data['undeleted'].length > 0) {
if (data['undeleted'].length > 1) {
msg_f = "{% trans "Internal error. Failed to delete %(name)s and %(amount)s other items." %}"
} else {
msg_f = "{% trans "Internal error. Failed to delete %(name)s." %}"
}
msg_f = msg_f.replace('%(name)s', data['undeleted'][0]).replace('%(amount)s', data['undeleted'].length - 1);
feedback(msg_f, 'error');
}
$.modal.close();
$('#dirents-op').addClass('hide');
$('th.select .checkbox').removeClass('checkbox-checked');
},
error: function(xhr, textStatus, errorThrown) {
$.modal.close();
ajaxErrorHandler(xhr, textStatus, errorThrown);
}
});
};
$('#mv-dirents, #cp-dirents').click(function() {
var form = $('#mv-form'), op;
form.modal({appendTo:'#main', autoResize:true, focus:false});
$('#simplemodal-container').css({'width':'auto', 'height':'auto'});
if ($(this).attr('id') == 'mv-dirents') {
op = 'mv';
form.prepend("<h3>{% trans "Move selected directories/files to:" %}</h3>");
} else {
op = 'cp';
form.prepend("<h3>{% trans "Copy selected directories/files to:" %}</h3>");
}
var file_tree = new FileTree();
file_tree.renderDirTree($('#current-repo-dirs').data('site_root', '{{SITE_ROOT}}'), form, current_repo);
var files = $('.checkbox-checked').parents('.file-item'),
dirs = $('.checkbox-checked').parents('.dir-item'),
dir_names = [], file_names = [];
files.each(function() {
file_names.push($(this).data('name'));
});
dirs.each(function() {
dir_names.push($(this).data('name'));
});
form.submit(function() {
var dst_repo = $('[name="dst_repo"]', form).val(),
dst_path = $('[name="dst_path"]', form).val(),
url_main;
if (!$.trim(dst_repo) || !$.trim(dst_path)) {
$('.error', form).removeClass('hide');
return false;
}
if (dst_repo == '{{repo.id }}' && dst_path == cur_path) {
$('.error', form).html("{% trans "Invalid destination path" %}").removeClass('hide');
return false;
}
if (op == 'mv') {
url_main = '{% url 'mv_dirents' repo.id %}';
} else {
url_main = '{% url 'cp_dirents' repo.id %}';
}
disable($('[type="submit"]', form));
form.append('<p style="color:red;">' + "{% trans "Processing..." %}" + '</p>');
$.ajax({
url: url_main + '?parent_dir=' + e(cur_path),
type: 'POST',
dataType: 'json',
beforeSend: prepareCSRFToken,
traditional: true,
data: {
'file_names': file_names,
'dir_names': dir_names,
'dst_repo': dst_repo,
'dst_path': dst_path
},
success: function(data) {
var success_len = data['success'].length,
msg_s, msg_f;
$.modal.close();
$('#dirents-op').addClass('hide');
$('th.select .checkbox').removeClass('checkbox-checked');
if (success_len > 0) {
if (op == 'mv') {
if (success_len == files.length + dirs.length) {
files.remove();
dirs.remove();
} else {
files.each(function() {
if ($(this).data('name') in data['success']) {
$(this).remove();
}
});
dirs.each(function() {
if ($(this).data('name') in data['success']) {
$(this).remove();
}
});
}
if (data['success'].length > 1) {
msg_s = "{% trans "Successfully moved %(name)s and %(amount)s other items." %}";
} else {
msg_s = "{% trans "Successfully moved %(name)s." %}";
}
} else {
$('.checkbox').removeClass('checkbox-checked');
if (data['success'].length > 1) {
msg_s = "{% trans "Successfully copied %(name)s and %(amount)s other items." %}";
} else {
msg_s = "{% trans "Successfully copied %(name)s." %}";
}
}
msg_s = msg_s.replace('%(name)s', data['success'][0]).replace('%(amount)s', data['success'].length - 1);
msg_s += ' <a href="' + data['url'] + '">' + "{% trans "View" %}" + '</a>';
feedback(msg_s, 'success');
updateCmt();
}
if (data['failed'].length > 0) {
if (op == 'mv') {
if (data['failed'].length > 1) {
msg_f = "{% trans "Internal error. Failed to move %(name)s and %(amount)s other items." %}";
} else {
msg_f = "{% trans "Internal error. Failed to move %(name)s." %}";
}
} else {
if (data['failed'].length > 1) {
msg_f = "{% trans "Internal error. Failed to copy %(name)s and %(amount)s other items." %}";
} else {
msg_f = "{% trans "Internal error. Failed to copy %(name)s." %}";
}
}
msg_f = msg_f.replace('%(name)s', data['failed'][0]).replace('%(amount)s', data['failed'].length - 1);
feedback(msg_f, 'error');
}
},
error: function(xhr, textStatus, errorThrown) {
$.modal.close();
ajaxErrorHandler(xhr, textStatus, errorThrown);
}
});
return false;
});
});
// js on repo file list // js on repo file list
var no_file_op_popup = true; var no_file_op_popup = true;
@@ -376,6 +588,28 @@ $('#share-cur-dir').click(function() {
showSharePopup(op, name, aj_url, type, cur_path); showSharePopup(op, name, aj_url, type, cur_path);
}); });
//select all or not
$('th .checkbox-orig').unbind().click(function() {
var dirents_op = $('#dirents-op');
// keep all checkbox in the same state: selected or not
// a case: select all, and then scroll to req 'more'...
$(this).parent().toggleClass('checkbox-checked');
if ($(this).parent().hasClass('checkbox-checked')) {
$('.checkbox').addClass('checkbox-checked');
} else {
$('.checkbox').removeClass('checkbox-checked');
}
// show buttons or not
if ($('.checkbox-checked', $('.dir-item, .file-item')).length > 0) {
dirents_op.removeClass('hide');
setDirentsOpPos();
} else {
dirents_op.addClass('hide');
}
});
//sort //sort
$('#name-down, #name-up, #time-up, #time-down').click(sortDirent); $('#name-down, #name-up, #time-up, #time-down').click(sortDirent);
@@ -410,11 +644,12 @@ function sortDirent() {
case 'time-down': by = function(a, b) { return a.time < b.time ? 1 : -1 };break; case 'time-down': by = function(a, b) { return a.time < b.time ? 1 : -1 };break;
} }
// when 'name' is like '123', `data('name')` return number 123
$('.dir-item').each(function() { $('.dir-item').each(function() {
dir_list.push({'name':$(this).data('name'), 'time':$(this).data('time'), 'element':this}); dir_list.push({'name':$(this).attr('data-name'), 'time':$(this).data('time'), 'element':this});
}); });
$('.file-item').each(function() { $('.file-item').each(function() {
file_list.push({'name':$(this).data('name'), 'time':$(this).data('time'), 'element':this}); file_list.push({'name':$(this).attr('data-name'), 'time':$(this).data('time'), 'element':this});
}); });
dir_list.sort(by); dir_list.sort(by);
@@ -464,19 +699,28 @@ function changeLocation(data) {
} }
} }
function reqDirData(url, func) { function reqDirData(url, func) {
var orig_wtop = $(window).scrollTop();
$.ajax({ $.ajax({
url: url, url: url,
cache: false, cache: false,
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
$('#repo-file-list').html(data['html']); $('#repo-file-list').html(data['html']);
// both original dir list and new dir list are long
var repo_top = $('#repo-top'),
h = repo_top.offset().top + repo_top.outerHeight(true);
if (orig_wtop > h && $(window).scrollTop() == orig_wtop) {
$(window).scrollTop(h);
}
cur_path = data['path']; // update cur_path cur_path = data['path']; // update cur_path
if (func) { if (func) {
func(data); func(data);
} }
dirOP(); dirOP();
// in case the 'discuss' popup is open before this request // in case the 'discuss' & #dirents-op popup is open before this request
$('#discuss-to-group, #discuss-to-group-caret').addClass('hide'); $('#discuss-to-group, #discuss-to-group-caret, #dirents-op').addClass('hide');
}, },
error:function(xhr, textStatus, errorThrown) { error:function(xhr, textStatus, errorThrown) {
if (xhr.responseText) { if (xhr.responseText) {
@@ -536,7 +780,21 @@ var current_repo = [],
{% endif %} {% endif %}
function opOnDirent(context) { // added param 'context' for 'more' dirents requested with 'list_dir_more' function opOnDirent(context) { // added param 'context' for 'more' dirents requested with 'list_dir_more'
var context = context || $('.dir-item, .file-item'); var context = context || $('.dir-item, .file-item');
// select
$('.checkbox-orig', context).unbind().click(function() {
var dirents_op = $('#dirents-op');
$(this).parent().toggleClass('checkbox-checked');
// show buttons or not
if ($('.checkbox-checked', context).length > 0) {
dirents_op.removeClass('hide');
setDirentsOpPos();
} else {
dirents_op.addClass('hide');
}
});
$('.dir-link', context).click(dirlinkClick); $('.dir-link', context).click(dirlinkClick);
@@ -629,13 +887,7 @@ $('.dir-del, .file-del', context).click(function() {
updateCmt(); updateCmt();
} }
}, },
error:function(xhr, textStatus, errorThrown) { error: ajaxErrorHandler
if (xhr.responseText) {
feedback(jQuery.parseJSON(xhr.responseText).error, 'error');
} else {
feedback("{% trans "Failed. Please check the network." %}", 'error');
}
}
}); });
return false; return false;
}); });
@@ -938,6 +1190,11 @@ $('#add-new-file-form, #add-new-dir-form, #rename-form, #mv-form').submit(functi
obj_name = $('[name="obj_name"]', form).val(), obj_name = $('[name="obj_name"]', form).val(),
obj_type = $('[name="obj_type"]', form).val(), obj_type = $('[name="obj_type"]', form).val(),
op_obj = form.data('op_obj'); op_obj = form.data('op_obj');
if (!op_obj) { // for mv-dirents, cp-dirents
return false;
}
if (!$.trim(dst_repo) || !$.trim(dst_path)) { if (!$.trim(dst_repo) || !$.trim(dst_path)) {
$('.error', form).removeClass('hide'); $('.error', form).removeClass('hide');
return false; return false;
@@ -1026,11 +1283,17 @@ $('#private-share-form').submit(function() {
}); });
var last_start = 0; // for 'list_dir_more' var last_start = 0; // for 'list_dir_more'
$(window).scroll(function() { $(window).scroll(function() {
var file_topbar = $('.repo-file-list-topbar'), var repo_top = $('#repo-top'),
list_hd = $('.repo-file-list tr:first-child'); file_topbar = $('.repo-file-list-topbar'),
if ($(window).scrollTop() > file_topbar.offset().top + file_topbar.outerHeight(true)) { list_hd = $('.repo-file-list tr:first-child'),
list_hd.addClass('fixed-hd').css({'left':file_topbar.offset().left}); repo_top_left = $('.block-inner', repo_top).offset().left,
file_topbar_h = file_topbar.outerHeight(true);
if ($(window).scrollTop() > repo_top.offset().top + repo_top.outerHeight(true)) {
file_topbar.addClass('fixed-hd').css({'left': repo_top_left});
list_hd.addClass('fixed-hd').css({'left': repo_top_left, 'top':file_topbar_h});
} else { } else {
file_topbar.removeClass('fixed-hd');
list_hd.removeClass('fixed-hd'); list_hd.removeClass('fixed-hd');
} }
@@ -1057,7 +1320,6 @@ $(window).scroll(function() {
} }
); );
opOnDirent(more_dirents); opOnDirent(more_dirents);
$('#name-down, #name-up, #time-up, #time-down').unbind().click(sortDirent);
if (data['dirent_more']) { if (data['dirent_more']) {
ele_more.data('start', data['more_start']); ele_more.data('start', data['more_start']);
} else { } else {

View File

@@ -27,6 +27,9 @@
<table class="repo-file-list"> <table class="repo-file-list">
<tr> <tr>
<th class="select">
<span class="checkbox"><input type="checkbox" class="checkbox-orig" /></span>
</th>
<th class="star"></th> <th class="star"></th>
<th class="dirent-icon"></th> <th class="dirent-icon"></th>
<th><span class="dirent-name">{% trans "Name"%} <span id="name-up" class="tri-bg tri-up-order-bg"></span> <span id="name-down" class="tri-bg tri-down-order-bg"></span></span></th> <th><span class="dirent-name">{% trans "Name"%} <span id="name-up" class="tri-bg tri-up-order-bg"></span> <span id="name-down" class="tri-bg tri-down-order-bg"></span></span></th>

View File

@@ -1,6 +1,9 @@
{% load seahub_tags i18n %} {% load seahub_tags i18n %}
{% for dirent in dir_list %} {% for dirent in dir_list %}
<tr class="dir-item" data-name="{{dirent.obj_name}}" data-time="{% if dirent.last_modified %}{{ dirent.last_modified }}{%else %}0{% endif %}"> <tr class="dir-item" data-name="{{dirent.obj_name}}" data-time="{% if dirent.last_modified %}{{ dirent.last_modified }}{%else %}0{% endif %}">
<td class="select">
<span class="checkbox"><input type="checkbox" class="checkbox-orig" /></span>
</td>
<td class="star"></td> <td class="star"></td>
<td class="dirent-icon"><img src="{{ MEDIA_URL }}img/folder-icon-24.png" alt="{% trans "Directory icon"%}" /></td> <td class="dirent-icon"><img src="{{ MEDIA_URL }}img/folder-icon-24.png" alt="{% trans "Directory icon"%}" /></td>
<td> <td>
@@ -41,6 +44,9 @@
{% endfor %} {% endfor %}
{% for dirent in file_list %} {% for dirent in file_list %}
<tr class="file-item" data-name="{{dirent.obj_name}}" data-time="{% if dirent.last_modified %}{{ dirent.last_modified }} {%else %}0{% endif %}"> <tr class="file-item" data-name="{{dirent.obj_name}}" data-time="{% if dirent.last_modified %}{{ dirent.last_modified }} {%else %}0{% endif %}">
<td class="select">
<span class="checkbox"><input type="checkbox" class="checkbox-orig" /></span>
</td>
<td class="star alc"> <td class="star alc">
{% if dirent.starred %} {% if dirent.starred %}
<span title="{% trans 'starred' %}" class="icon-star file-star" data-status="starred"></span> <span title="{% trans 'starred' %}" class="icon-star file-star" data-status="starred"></span>

View File

@@ -103,6 +103,9 @@ urlpatterns = patterns('',
### Ajax ### ### Ajax ###
(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dirents/$', get_dirents), (r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dirents/$', get_dirents),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dirents/delete/$', delete_dirents, name='delete_dirents'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dirents/move/$', mv_dirents, name='mv_dirents'),
url(r'^ajax/repo/(?P<repo_id>[-0-9a-f]{36})/dirents/copy/$', cp_dirents, name='cp_dirents'),
url(r'^ajax/group/(?P<group_id>\d+)/repos/$', get_group_repos, name='get_group_repos'), url(r'^ajax/group/(?P<group_id>\d+)/repos/$', get_group_repos, name='get_group_repos'),
url(r'^ajax/my-unenc-repos/$', get_my_unenc_repos, name='get_my_unenc_repos'), url(r'^ajax/my-unenc-repos/$', get_my_unenc_repos, name='get_my_unenc_repos'),
url(r'^ajax/contacts/$', get_contacts, name='get_contacts'), url(r'^ajax/contacts/$', get_contacts, name='get_contacts'),

View File

@@ -469,6 +469,50 @@ def delete_dirent(request, repo_id):
return HttpResponse(json.dumps({'error': err_msg}), return HttpResponse(json.dumps({'error': err_msg}),
status=500, content_type=content_type) status=500, content_type=content_type)
@login_required
def delete_dirents(request, repo_id):
"""
Delete multi files/dirs with ajax.
"""
if not request.is_ajax():
raise Http404
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
err_msg = _(u'Library does not exist.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
# permission checking
username = request.user.username
if check_repo_access_permission(repo.id, username) != 'rw':
err_msg = _(u'Permission denied.')
return HttpResponse(json.dumps({'error': err_msg}),
status=403, content_type=content_type)
# argument checking
parent_dir = request.GET.get("parent_dir")
dirents_names = request.POST.getlist('dirents_names')
if not (parent_dir and dirents_names):
err_msg = _(u'Argument missing.')
return HttpResponse(json.dumps({'error': err_msg}),
status=400, content_type=content_type)
deleted = []
undeleted = []
for dirent_name in dirents_names:
try:
seafile_api.del_file(repo_id, parent_dir, dirent_name, username)
deleted.append(dirent_name)
except SearpcError, e:
logger.error(e)
undeleted.append(dirent_name)
return HttpResponse(json.dumps({'deleted': deleted,'undeleted': undeleted}),
content_type=content_type)
def copy_move_common(func): def copy_move_common(func):
"""Decorator for common logic in copying/moving dir/file. """Decorator for common logic in copying/moving dir/file.
""" """
@@ -518,8 +562,8 @@ def copy_move_common(func):
# do nothing when dst is the same as src # do nothing when dst is the same as src
if repo_id == dst_repo_id and path == dst_path: if repo_id == dst_repo_id and path == dst_path:
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urlquote(path)) result['error'] = _('Invalid destination path')
return HttpResponseRedirect(url) return HttpResponse(json.dumps(result), status=400, content_type=content_type)
return func(repo_id, path, dst_repo_id, dst_path, obj_name, username) return func(repo_id, path, dst_repo_id, dst_path, obj_name, username)
return _decorated return _decorated
@@ -626,6 +670,123 @@ def cp_dir(src_repo_id, src_path, dst_repo_id, dst_path, obj_name, username):
return HttpResponse(json.dumps(result), status=500, return HttpResponse(json.dumps(result), status=500,
content_type=content_type) content_type=content_type)
def dirents_copy_move_common(func):
"""
Decorator for common logic in copying/moving dirs/files.
"""
def _decorated(request, repo_id, *args, **kwargs):
if request.method != 'POST' or not request.is_ajax():
raise Http404
result = {}
content_type = 'application/json; charset=utf-8'
repo = get_repo(repo_id)
if not repo:
result['error'] = _(u'Library does not exist.')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# permission checking
username = request.user.username
if check_repo_access_permission(repo.id, username) != 'rw':
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
# arguments validation
parent_dir = request.GET.get('parent_dir')
obj_file_names = request.POST.getlist('file_names')
obj_dir_names = request.POST.getlist('dir_names')
dst_repo_id = request.POST.get('dst_repo')
dst_path = request.POST.get('dst_path')
if not (parent_dir and dst_repo_id and dst_path) and not (obj_file_names or obj_dir_names):
result['error'] = _('Argument missing')
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# check file path
for obj_name in obj_file_names + obj_dir_names:
if len(dst_path+obj_name) > settings.MAX_PATH:
result['error'] = _('Destination path is too long for %s.') % obj_name
return HttpResponse(json.dumps(result), status=400,
content_type=content_type)
# check whether user has write permission to dest repo
if check_repo_access_permission(dst_repo_id, username) != 'rw':
result['error'] = _('Permission denied')
return HttpResponse(json.dumps(result), status=403,
content_type=content_type)
# when dst is the same as src
if repo_id == dst_repo_id and parent_dir == dst_path:
result['error'] = _('Invalid destination path')
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
return func(repo_id, parent_dir, dst_repo_id, dst_path, obj_file_names, obj_dir_names, username)
return _decorated
@login_required
@dirents_copy_move_common
def mv_dirents(src_repo_id, src_path, dst_repo_id, dst_path, obj_file_names, obj_dir_names, username):
content_type = 'application/json; charset=utf-8'
for obj_name in obj_dir_names:
src_dir = os.path.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': src_dir, 'des': dst_path}
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
success = []
failed = []
url = None
for obj_name in obj_file_names + obj_dir_names:
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
seafile_api.move_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name, username)
success.append(obj_name)
except SearpcError, e:
failed.append(obj_name)
if len(success) > 0:
url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path)
return HttpResponse(json.dumps({'success': success, 'failed': failed, 'url': url}), content_type=content_type)
@login_required
@dirents_copy_move_common
def cp_dirents(src_repo_id, src_path, dst_repo_id, dst_path, obj_file_names, obj_dir_names, username):
content_type = 'application/json; charset=utf-8'
for obj_name in obj_dir_names:
src_dir = os.path.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': src_dir, 'des': dst_path}
result['error'] = error_msg
return HttpResponse(json.dumps(result), status=400, content_type=content_type)
success = []
failed = []
url = None
for obj_name in obj_file_names + obj_dir_names:
new_obj_name = check_filename_with_rename(dst_repo_id, dst_path, obj_name)
try:
seafile_api.copy_file(src_repo_id, src_path, obj_name,
dst_repo_id, dst_path, new_obj_name, username)
success.append(obj_name)
except SearpcError, e:
failed.append(obj_name)
if len(success) > 0:
url = reverse('repo', args=[dst_repo_id]) + '?p=' + urlquote(dst_path)
return HttpResponse(json.dumps({'success': success, 'failed': failed, 'url': url}), content_type=content_type)
@login_required @login_required
def repo_star_file(request, repo_id): def repo_star_file(request, repo_id):
if not request.is_ajax(): if not request.is_ajax():