mirror of
https://github.com/haiwen/seahub.git
synced 2025-09-25 06:33:48 +00:00
[repo] added feature 'select multi dirents, and del/mv/cp' and fixed sort bug
This commit is contained in:
@@ -262,7 +262,17 @@ input.btn-disabled:hover {/*for input*/
|
||||
*[data-href] {
|
||||
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,
|
||||
.modalCloseImg,
|
||||
.add {
|
||||
@@ -439,8 +449,8 @@ textarea:-moz-placeholder {/* for FF */
|
||||
}
|
||||
.checkbox-orig,
|
||||
.checkbox {
|
||||
width:13px;
|
||||
height:13px;
|
||||
width:14px;
|
||||
height:14px;
|
||||
}
|
||||
.checkbox-checked {
|
||||
background:transparent url('../img/tick.png') no-repeat scroll 1px 3px;
|
||||
@@ -1376,15 +1386,18 @@ textarea:-moz-placeholder {/* for FF */
|
||||
table-layout:auto;
|
||||
margin:0;
|
||||
}
|
||||
.repo-file-list .select {
|
||||
width:15px;
|
||||
}
|
||||
.repo-file-list .star {
|
||||
width:30px;
|
||||
width:25px;
|
||||
}
|
||||
.repo-file-list .dirent-icon {
|
||||
width:32px;
|
||||
width:27px;
|
||||
}
|
||||
.repo-file-list .dirent-name {
|
||||
display:inline-block;
|
||||
width:405px;
|
||||
width:400px;
|
||||
}
|
||||
.repo-file-list .dirent-size {
|
||||
width:89px;
|
||||
@@ -1395,15 +1408,20 @@ textarea:-moz-placeholder {/* for FF */
|
||||
.repo-file-list .dirent-op {
|
||||
width:250px;
|
||||
}
|
||||
.repo-file-list .checkbox-label,
|
||||
.repo-file-list .checkbox {
|
||||
margin:0;
|
||||
}
|
||||
.repo-file-list th {
|
||||
padding-top:20px;
|
||||
}
|
||||
.repo-file-list .fixed-hd {
|
||||
#repo-file-list .fixed-hd {
|
||||
position:fixed;
|
||||
width:950px;
|
||||
background:#fff;
|
||||
top:0;
|
||||
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 {
|
||||
box-shadow:0 2px 2px -3px #aaa;
|
||||
@@ -1416,6 +1434,7 @@ textarea:-moz-placeholder {/* for FF */
|
||||
.file-star {
|
||||
cursor:pointer;
|
||||
line-height:19px;
|
||||
vertical-align:middle;
|
||||
}
|
||||
.repo-file-list .repo-file-op {
|
||||
position:relative;
|
||||
@@ -1485,6 +1504,18 @@ textarea:-moz-placeholder {/* for FF */
|
||||
color:#666;
|
||||
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-encrypted,
|
||||
.file-diff {
|
||||
|
@@ -466,6 +466,8 @@ $(document).click(function(e) {
|
||||
{'name':'plus-sign-alt', 'con':'f0fe'},
|
||||
{'name':'upload', 'con':'f01b'},
|
||||
{'name':'ban-circle', 'con':'f05e'},
|
||||
{'name':'move', 'con':'f047'},
|
||||
{'name':'copy', 'con':'f0c5'},
|
||||
{'name':'upload-alt', 'con':'f093'}
|
||||
];
|
||||
function setCon(icon, icon_class_prefix, icon_list) {
|
||||
|
@@ -44,6 +44,13 @@
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
<h3>{% trans "Upload Files" %}</h3>
|
||||
{% if no_quota %}
|
||||
@@ -312,6 +319,211 @@ $('#repo-setting-form').submit(function() {
|
||||
});
|
||||
{% 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
|
||||
var no_file_op_popup = true;
|
||||
@@ -376,6 +588,28 @@ $('#share-cur-dir').click(function() {
|
||||
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
|
||||
$('#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;
|
||||
}
|
||||
|
||||
// when 'name' is like '123', `data('name')` return number 123
|
||||
$('.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_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);
|
||||
@@ -464,19 +699,28 @@ function changeLocation(data) {
|
||||
}
|
||||
}
|
||||
function reqDirData(url, func) {
|
||||
var orig_wtop = $(window).scrollTop();
|
||||
$.ajax({
|
||||
url: url,
|
||||
cache: false,
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
$('#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
|
||||
if (func) {
|
||||
func(data);
|
||||
}
|
||||
dirOP();
|
||||
// in case the 'discuss' popup is open before this request
|
||||
$('#discuss-to-group, #discuss-to-group-caret').addClass('hide');
|
||||
// in case the 'discuss' & #dirents-op popup is open before this request
|
||||
$('#discuss-to-group, #discuss-to-group-caret, #dirents-op').addClass('hide');
|
||||
},
|
||||
error:function(xhr, textStatus, errorThrown) {
|
||||
if (xhr.responseText) {
|
||||
@@ -536,7 +780,21 @@ var current_repo = [],
|
||||
{% endif %}
|
||||
|
||||
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);
|
||||
|
||||
@@ -629,13 +887,7 @@ $('.dir-del, .file-del', context).click(function() {
|
||||
updateCmt();
|
||||
}
|
||||
},
|
||||
error:function(xhr, textStatus, errorThrown) {
|
||||
if (xhr.responseText) {
|
||||
feedback(jQuery.parseJSON(xhr.responseText).error, 'error');
|
||||
} else {
|
||||
feedback("{% trans "Failed. Please check the network." %}", 'error');
|
||||
}
|
||||
}
|
||||
error: ajaxErrorHandler
|
||||
});
|
||||
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_type = $('[name="obj_type"]', form).val(),
|
||||
op_obj = form.data('op_obj');
|
||||
|
||||
if (!op_obj) { // for mv-dirents, cp-dirents
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$.trim(dst_repo) || !$.trim(dst_path)) {
|
||||
$('.error', form).removeClass('hide');
|
||||
return false;
|
||||
@@ -1026,11 +1283,17 @@ $('#private-share-form').submit(function() {
|
||||
});
|
||||
var last_start = 0; // for 'list_dir_more'
|
||||
$(window).scroll(function() {
|
||||
var file_topbar = $('.repo-file-list-topbar'),
|
||||
list_hd = $('.repo-file-list tr:first-child');
|
||||
if ($(window).scrollTop() > file_topbar.offset().top + file_topbar.outerHeight(true)) {
|
||||
list_hd.addClass('fixed-hd').css({'left':file_topbar.offset().left});
|
||||
var repo_top = $('#repo-top'),
|
||||
file_topbar = $('.repo-file-list-topbar'),
|
||||
list_hd = $('.repo-file-list tr:first-child'),
|
||||
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 {
|
||||
file_topbar.removeClass('fixed-hd');
|
||||
list_hd.removeClass('fixed-hd');
|
||||
}
|
||||
|
||||
@@ -1057,7 +1320,6 @@ $(window).scroll(function() {
|
||||
}
|
||||
);
|
||||
opOnDirent(more_dirents);
|
||||
$('#name-down, #name-up, #time-up, #time-down').unbind().click(sortDirent);
|
||||
if (data['dirent_more']) {
|
||||
ele_more.data('start', data['more_start']);
|
||||
} else {
|
||||
|
@@ -27,6 +27,9 @@
|
||||
|
||||
<table class="repo-file-list">
|
||||
<tr>
|
||||
<th class="select">
|
||||
<span class="checkbox"><input type="checkbox" class="checkbox-orig" /></span>
|
||||
</th>
|
||||
<th class="star"></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>
|
||||
|
@@ -1,6 +1,9 @@
|
||||
{% load seahub_tags i18n %}
|
||||
{% 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 %}">
|
||||
<td class="select">
|
||||
<span class="checkbox"><input type="checkbox" class="checkbox-orig" /></span>
|
||||
</td>
|
||||
<td class="star"></td>
|
||||
<td class="dirent-icon"><img src="{{ MEDIA_URL }}img/folder-icon-24.png" alt="{% trans "Directory icon"%}" /></td>
|
||||
<td>
|
||||
@@ -41,6 +44,9 @@
|
||||
{% endfor %}
|
||||
{% 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 %}">
|
||||
<td class="select">
|
||||
<span class="checkbox"><input type="checkbox" class="checkbox-orig" /></span>
|
||||
</td>
|
||||
<td class="star alc">
|
||||
{% if dirent.starred %}
|
||||
<span title="{% trans 'starred' %}" class="icon-star file-star" data-status="starred"></span>
|
||||
|
@@ -103,6 +103,9 @@ urlpatterns = patterns('',
|
||||
|
||||
### Ajax ###
|
||||
(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/my-unenc-repos/$', get_my_unenc_repos, name='get_my_unenc_repos'),
|
||||
url(r'^ajax/contacts/$', get_contacts, name='get_contacts'),
|
||||
|
@@ -469,6 +469,50 @@ def delete_dirent(request, repo_id):
|
||||
return HttpResponse(json.dumps({'error': err_msg}),
|
||||
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):
|
||||
"""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
|
||||
if repo_id == dst_repo_id and path == dst_path:
|
||||
url = reverse('repo', args=[repo_id]) + ('?p=%s' % urlquote(path))
|
||||
return HttpResponseRedirect(url)
|
||||
result['error'] = _('Invalid destination path')
|
||||
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 _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,
|
||||
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
|
||||
def repo_star_file(request, repo_id):
|
||||
if not request.is_ajax():
|
||||
|
Reference in New Issue
Block a user