From aad993163ea01ce3a4e67de4777240be20e6d1bd Mon Sep 17 00:00:00 2001 From: llj Date: Fri, 30 Aug 2013 13:46:56 +0800 Subject: [PATCH] [repo] added feature 'select multi dirents, and del/mv/cp' and fixed sort bug --- media/css/seahub.css | 45 ++- seahub/templates/base.html | 2 + seahub/templates/repo.html | 296 +++++++++++++++++-- seahub/templates/snippets/repo_dir_data.html | 3 + seahub/templates/snippets/repo_dirents.html | 6 + seahub/urls.py | 3 + seahub/views/ajax.py | 165 ++++++++++- 7 files changed, 494 insertions(+), 26 deletions(-) diff --git a/media/css/seahub.css b/media/css/seahub.css index 6be7e8d7a1..d67a55c1ec 100644 --- a/media/css/seahub.css +++ b/media/css/seahub.css @@ -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 { diff --git a/seahub/templates/base.html b/seahub/templates/base.html index 26c032aa31..e3a7f03734 100644 --- a/seahub/templates/base.html +++ b/seahub/templates/base.html @@ -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) { diff --git a/seahub/templates/repo.html b/seahub/templates/repo.html index f0674eec09..bd68039c22 100644 --- a/seahub/templates/repo.html +++ b/seahub/templates/repo.html @@ -44,6 +44,13 @@ + {% if user_perm == 'rw' %} +
+ + + +
+ {% endif %}

{% trans "Upload Files" %}

{% 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('

' + "{% trans "Delete Items" %}" + '

' + "{% trans "Are you sure you want to delete these selected items?" %}" + '

'); + $('#confirm-yes').unbind().click(del_dirents); +}); +var del_dirents = function() { + $('#confirm-popup').append('

' + "{% trans "Processing..." %}" + '

'); + 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("

{% trans "Move selected directories/files to:" %}

"); + } else { + op = 'cp'; + form.prepend("

{% trans "Copy selected directories/files to:" %}

"); + } + + 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('

' + "{% trans "Processing..." %}" + '

'); + $.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 += ' ' + "{% trans "View" %}" + ''; + 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 { diff --git a/seahub/templates/snippets/repo_dir_data.html b/seahub/templates/snippets/repo_dir_data.html index c1cdf8fa91..c8764c91e1 100644 --- a/seahub/templates/snippets/repo_dir_data.html +++ b/seahub/templates/snippets/repo_dir_data.html @@ -27,6 +27,9 @@ + diff --git a/seahub/templates/snippets/repo_dirents.html b/seahub/templates/snippets/repo_dirents.html index d379f62b48..4ccd44ecf3 100644 --- a/seahub/templates/snippets/repo_dirents.html +++ b/seahub/templates/snippets/repo_dirents.html @@ -1,6 +1,9 @@ {% load seahub_tags i18n %} {% for dirent in dir_list %} + +
+ + {% trans "Name"%}
+ + {% trans @@ -41,6 +44,9 @@ {% endfor %} {% for dirent in file_list %}
+ + {% if dirent.starred %} diff --git a/seahub/urls.py b/seahub/urls.py index 97f2a750dc..e4e72a3600 100644 --- a/seahub/urls.py +++ b/seahub/urls.py @@ -103,6 +103,9 @@ urlpatterns = patterns('', ### Ajax ### (r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/$', get_dirents), + url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/delete/$', delete_dirents, name='delete_dirents'), + url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/move/$', mv_dirents, name='mv_dirents'), + url(r'^ajax/repo/(?P[-0-9a-f]{36})/dirents/copy/$', cp_dirents, name='cp_dirents'), url(r'^ajax/group/(?P\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'), diff --git a/seahub/views/ajax.py b/seahub/views/ajax.py index 237a54ae15..97d0ff6242 100644 --- a/seahub/views/ajax.py +++ b/seahub/views/ajax.py @@ -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():