1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-02 07:27:04 +00:00

update download dir/multi

This commit is contained in:
lian
2016-07-04 15:31:55 +08:00
parent 1e3b088e30
commit 6dca56e9d5
26 changed files with 743 additions and 98 deletions

View File

@@ -0,0 +1,37 @@
import logging
import json
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
from seaserv import seafile_api
logger = logging.getLogger(__name__)
class QueryZipProgressView(APIView):
throttle_classes = (UserRateThrottle, )
def get(self, request, format=None):
""" check progress when download dir/multi.
Permission checking:
"""
token = request.GET.get('token', None)
if not token:
error_msg = 'token invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
progress = seafile_api.query_zip_progress(token)
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(json.loads(progress))

View File

@@ -0,0 +1,120 @@
import logging
import os
import json
import posixpath
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from django.conf import settings
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.utils import api_error
from seahub.views.file import send_file_access_msg
from seahub.share.models import FileShare
from seahub.utils import is_windows_operating_system, \
is_pro_version
import seaserv
from seaserv import seafile_api
logger = logging.getLogger(__name__)
class ShareLinkZipTaskView(APIView):
throttle_classes = (UserRateThrottle,)
def get(self, request, format=None):
""" Only used for download dir when view dir share link from web.
Permission checking:
1. authenticated user OR anonymous user has passed email code check(if necessary);
"""
# permission check
if is_pro_version() and settings.ENABLE_SHARE_LINK_AUDIT:
if not request.user.is_authenticated() and \
not request.session.get('anonymous_email'):
# if anonymous user has passed email code check,
# then his/her email info will be in session.
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# argument check
share_link_token = request.GET.get('share_link_token', None)
if not share_link_token:
error_msg = 'share_link_token invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
req_path = request.GET.get('path', None)
if not req_path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# recourse check
fileshare = FileShare.objects.get_valid_dir_link_by_token(share_link_token)
if not fileshare:
error_msg = 'share_link_token %s not found.' % share_link_token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if req_path[-1] != '/':
req_path += '/'
if req_path == '/':
real_path = fileshare.path
else:
real_path = posixpath.join(fileshare.path, req_path.lstrip('/'))
if real_path[-1] != '/':
real_path += '/'
repo_id = fileshare.repo_id
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
dir_id = seafile_api.get_dir_id_by_path(repo_id, real_path)
if not dir_id:
error_msg = 'Folder %s not found.' % real_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# get file server access token
dir_name = repo.name if real_path == '/' else \
os.path.basename(real_path.rstrip('/'))
dir_size = seafile_api.get_dir_size(
repo.store_id, repo.version, dir_id)
if dir_size > seaserv.MAX_DOWNLOAD_DIR_SIZE:
error_msg = 'Unable to download directory "%s": size is too large.' % dir_name
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
is_windows = 0
if is_windows_operating_system(request):
is_windows = 1
fake_obj_id = {
'obj_id': dir_id,
'dir_name': dir_name,
'is_windows': is_windows
}
username = request.user.username
try:
zip_token = seafile_api.get_fileserver_access_token(
repo_id, json.dumps(fake_obj_id), 'download-dir', username)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if request.session.get('anonymous_email'):
request.user.username = request.session.get('anonymous_email')
send_file_access_msg(request, repo, real_path, 'share-link')
return Response({'zip_token': zip_token})

View File

@@ -0,0 +1,144 @@
import logging
import json
import stat
import posixpath
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
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
from seahub.views import check_folder_permission
from seahub.views.file import send_file_access_msg
from seahub.utils import is_windows_operating_system
import seaserv
from seaserv import seafile_api
logger = logging.getLogger(__name__)
class ZipTaskView(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
def get(self, request, repo_id, format=None):
""" Get file server token for download-dir and download-multi.
Permission checking:
1. user with 'r' or 'rw' permission;
"""
# argument check
parent_dir = request.GET.get('parent_dir', None)
if not parent_dir:
error_msg = 'parent_dir invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
dirent_name_list = request.GET.getlist('dirents', None)
if not dirent_name_list:
error_msg = 'dirents invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if len(dirent_name_list) == 1:
download_type = 'download-dir'
elif len(dirent_name_list) > 1:
download_type = 'download-multi'
else:
error_msg = 'dirents invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# recourse check
repo = seafile_api.get_repo(repo_id)
if not repo:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_dir_id_by_path(repo_id, parent_dir):
error_msg = 'Folder %s not found.' % parent_dir
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
if not check_folder_permission(request, repo_id, parent_dir):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# get file server access token
is_windows = 0
if is_windows_operating_system(request):
is_windows = 1
if download_type == 'download-dir':
dir_name = dirent_name_list[0].strip('/')
full_dir_path = posixpath.join(parent_dir, dir_name)
dir_id = seafile_api.get_dir_id_by_path(repo_id, full_dir_path)
if not dir_id:
error_msg = 'Folder %s not found.' % full_dir_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
dir_size = seafile_api.get_dir_size(
repo.store_id, repo.version, dir_id)
if dir_size > seaserv.MAX_DOWNLOAD_DIR_SIZE:
error_msg = 'Unable to download directory "%s": size is too large.' % dir_name
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
fake_obj_id = {
'obj_id': dir_id,
'dir_name': dir_name,
'is_windows': is_windows
}
if download_type == 'download-multi':
dirent_list = []
total_size = 0
for dirent_name in dirent_name_list:
dirent_name = dirent_name.strip('/')
dirent_list.append(dirent_name)
full_dirent_path = posixpath.join(parent_dir, dirent_name)
current_dirent = seafile_api.get_dirent_by_path(repo_id, full_dirent_path)
if not current_dirent:
continue
if stat.S_ISDIR(current_dirent.mode):
total_size += seafile_api.get_dir_size(repo.store_id,
repo.version, current_dirent.obj_id)
else:
total_size += current_dirent.size
if total_size > seaserv.MAX_DOWNLOAD_DIR_SIZE:
error_msg = _('Total size exceeds limit.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
fake_obj_id = {
'parent_dir': parent_dir,
'file_list': dirent_list,
'is_windows': is_windows
}
username = request.user.username
try:
zip_token = seafile_api.get_fileserver_access_token(
repo_id, json.dumps(fake_obj_id), download_type, username)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if len(dirent_name_list) > 10:
send_file_access_msg(request, repo, parent_dir, 'web')
else:
for dirent_name in dirent_name_list:
full_dirent_path = posixpath.join(parent_dir, dirent_name)
send_file_access_msg(request, repo, full_dirent_path, 'web')
return Response({'zip_token': zip_token})

View File

@@ -61,6 +61,9 @@ def base(request):
repo_id_patt = r".*/([a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12})/.*"
m = re.match(repo_id_patt, request.get_full_path())
search_repo_id = m.group(1) if m is not None else None
file_server_root = config.FILE_SERVER_ROOT
if not file_server_root.endswith('/'):
file_server_root += '/'
return {
'seafile_version': SEAFILE_VERSION,
@@ -90,4 +93,5 @@ def base(request):
'search_repo_id': search_repo_id,
'SITE_ROOT': SITE_ROOT,
'constance_enabled': dj_settings.CONSTANCE_ENABLED,
'FILE_SERVER_ROOT': file_server_root,
}

View File

@@ -112,7 +112,8 @@
var app = {
config: {
mediaUrl: '{{ MEDIA_URL }}',
siteRoot: '{{ SITE_ROOT }}'
siteRoot: '{{ SITE_ROOT }}',
fileServerRoot: '{{ FILE_SERVER_ROOT }}'
}
};
</script>

View File

@@ -105,6 +105,7 @@
<% if (!encrypted && user_perm == 'r') { %>
<div id="multi-dirents-op" class="hide">
<button id="cp-dirents" title="{% trans 'Copy' %}" class="op-btn sf2-icon-copy op-icon"></button>
<button id="download-dirents" title="{% trans 'Download' %}" class="op-btn sf2-icon-download op-icon"></button>
</div>
<% } %>
@@ -206,7 +207,9 @@
<td class="dirent-op">
<div class="op-container">
<div class="displayed-op">
<a class="op-icon download sf2-icon-download sf2-x vh" href="<%= download_url %>" title="{% trans "Download" %}" aria-label="{% trans "Download" %}"></a>
<a class="op-icon download sf2-icon-download sf2-x vh download-dir" href="#" title="{% trans "Download" %}" aria-label="{% trans "Download" %}"></a>
<% if (!repo_encrypted && can_generate_shared_link) { %>
<a href="#" class="op-icon share sf2-icon-share sf2-x vh" title="{% trans "Share" %}" aria-label="{% trans "Share" %}"></a>
<% } %>

View File

@@ -42,7 +42,7 @@
{% endif %}
</a>
{% if not traffic_over_limit %}
<a href="?p={{ path|urlencode }}&dl=1" class="obv-btn vam shared-dir-zip">{% trans "ZIP"%}</a>
<a href="#" class="obv-btn vam shared-dir-zip" data-name="{{ dirent.obj_name }}">{% trans "ZIP"%}</a>
{% endif %}
</div>
</div>
@@ -60,7 +60,7 @@
<tbody>
{% for dirent in dir_list %}
<tr>
<tr data-name="{{dirent.obj_name}}" >
<td class="alc"><img src="{{ MEDIA_URL }}img/folder-24.png" alt="{% trans "Directory icon"%}" /></td>
<td>
<a href="?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&mode={{mode}}" class="normal">{{ dirent.obj_name }}</a>
@@ -69,7 +69,7 @@
<td>{{ dirent.last_modified|translate_seahub_time }}</td>
<td>
{% if not traffic_over_limit %}
<a class="op-icon vh" href="{% url "view_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&dl=1" title="{% trans 'Download' %}" aria-label="{% trans 'Download' %}">
<a class="op-icon vh download-dir" data-name="{{ dirent.obj_name }}" href="#" aria-label="{% trans 'Download' %}">
<img src="{{MEDIA_URL}}img/download.png" alt="{% trans 'Download' %}" />
</a>
{% endif %}
@@ -119,7 +119,7 @@
</a>
<a href="?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&mode={{mode}}" class="normal text-link ellipsis">{{ dirent.obj_name }}</a>
{% if not traffic_over_limit %}
<a class="op-icon vh" href="{% url "view_shared_dir" token %}?p={{ path|urlencode }}{{ dirent.obj_name|urlencode }}&dl=1" title="{% trans 'Download' %}">
<a class="op-icon vh download-dir" data-name="{{ dirent.obj_name }}" href="#" title="{% trans 'Download' %}">
<img src="{{MEDIA_URL}}img/download.png" alt="" />
</a>
{% endif %}
@@ -185,9 +185,73 @@ var magnificOptions = {
$('.img-name-link').magnificPopup(magnificOptions);
$('.img-img-link').magnificPopup(magnificOptions);
var cur_path = '{{path|escapejs}}';
var share_link_token = '{{token}}';
var file_server_root = '{{FILE_SERVER_ROOT}}';
$('.shared-dir-zip, .download-dir').click(function (e) {
var cur_download_dir_path,
cur_download_dir_name,
zip_token;
var target = e.target || event.srcElement;
if ($(target).hasClass("shared-dir-zip")) {
cur_download_dir_path = cur_path;
} else {
cur_download_dir_name = $(target).closest('a').attr('data-name');
if (cur_path.lastIndexOf('/') != cur_path.length - 1) {
// cur_path NOT end with '/'
cur_download_dir_path = cur_path.substring(0, cur_path.lastIndexOf('/')) + '/' + cur_download_dir_name;
} else {
cur_download_dir_path = cur_path + cur_download_dir_name;
}
}
var queryZipProgress = function() {
$.ajax({
url: '{% url 'api-v2.1-query-zip-progress' %}' + '?token=' + zip_token,
dataType: 'json',
cache: false,
success: function (data) {
if (data['total'] == data['zipped']) {
clearInterval(interval);
location.href = file_server_root + 'zip/' + zip_token;
}
},
error: function (xhr) {
if (xhr.responseText) {
feedback($.parseJSON(xhr.responseText).error_msg, 'error');
} else {
feedback("{% trans "Failed. Please check the network." %}", 'error');
}
clearInterval(interval);
}
});
};
$.ajax({
url: '{% url 'api-v2.1-share-link-zip-task' %}' + '?share_link_token=' + share_link_token + '&path=' + encodeURIComponent(cur_download_dir_path),
dataType: 'json',
cache: false,
success: function(data) {
zip_token = data['zip_token'];
queryZipProgress();
interval = setInterval(queryZipProgress, 1000);
},
error: function (xhr) {
if (xhr.responseText) {
feedback($.parseJSON(xhr.responseText).error_msg, 'error');
} else {
feedback("{% trans "Failed. Please check the network." %}", 'error');
}
}
});
});
{% if not repo.encrypted and ENABLE_THUMBNAIL %}
// get thumbnails for image files
var cur_path = "{{path|escapejs}}";
$(function() {
var img_icons = $('.not-thumbnail');
if (img_icons.length == 0) {

View File

@@ -27,6 +27,9 @@ from seahub.api2.endpoints.file import FileView
from seahub.api2.endpoints.dir import DirView
from seahub.api2.endpoints.repo_set_password import RepoSetPassword
from seahub.api2.endpoints.dirents_download_link import DirentsDownloadLinkView
from seahub.api2.endpoints.zip_task import ZipTaskView
from seahub.api2.endpoints.share_link_zip_task import ShareLinkZipTaskView
from seahub.api2.endpoints.query_zip_progress import QueryZipProgressView
from seahub.api2.endpoints.admin.login import Login
from seahub.api2.endpoints.admin.file_audit import FileAudit
from seahub.api2.endpoints.admin.file_update import FileUpdate
@@ -187,6 +190,9 @@ urlpatterns = patterns(
url(r'^api/v2.1/upload-links/(?P<token>[a-f0-9]{10})/$', UploadLink.as_view(), name='api-v2.1-upload-link'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/file/$', FileView.as_view(), name='api-v2.1-file-view'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/dirents/download-link/$', DirentsDownloadLinkView.as_view(), name='api-v2.1-dirents-download-link-view'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/zip-task/$', ZipTaskView.as_view(), name='api-v2.1-zip-task'),
url(r'^api/v2.1/share-link-zip-task/$', ShareLinkZipTaskView.as_view(), name='api-v2.1-share-link-zip-task'),
url(r'^api/v2.1/query-zip-progress/$', QueryZipProgressView.as_view(), name='api-v2.1-query-zip-progress'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/dir/$', DirView.as_view(), name='api-v2.1-dir-view'),
url(r'^api/v2.1/repos/(?P<repo_id>[-0-9a-f]{36})/set-password/$', RepoSetPassword.as_view(), name="api-v2.1-repo-set-password"),
url(r'^api/v2.1/admin/sysinfo/$', SysInfo.as_view(), name='api-v2.1-sysinfo'),

View File

@@ -1298,3 +1298,12 @@ def get_system_admins():
admins.append(user)
return admins
def is_windows_operating_system(request):
if not request.META.has_key('HTTP_USER_AGENT'):
return False
if 'windows' in request.META['HTTP_USER_AGENT'].lower():
return True
else:
return False

View File

@@ -21,7 +21,6 @@ from seahub.share.models import FileShare, UploadLinkShare, \
check_share_link_common
from seahub.views import gen_path_link, get_repo_dirents, \
check_folder_permission
from seahub.views.file import send_file_access_msg
from seahub.utils import gen_file_upload_url, gen_dir_share_link, \
gen_shared_upload_link, user_traffic_over_limit, render_error, \
@@ -29,7 +28,6 @@ from seahub.utils import gen_file_upload_url, gen_dir_share_link, \
from seahub.settings import ENABLE_UPLOAD_FOLDER, \
ENABLE_RESUMABLE_FILEUPLOAD, ENABLE_THUMBNAIL, \
THUMBNAIL_ROOT, THUMBNAIL_DEFAULT_SIZE, THUMBNAIL_SIZE_FOR_GRID
from seahub.utils import gen_file_get_url
from seahub.utils.file_types import IMAGE
from seahub.thumbnail.utils import get_share_link_thumbnail_src
@@ -158,47 +156,6 @@ def repo_history_view(request, repo_id):
}, context_instance=RequestContext(request))
########## shared dir/uploadlink
def _download_dir_from_share_link(request, fileshare, repo, real_path):
# check whether owner's traffic over the limit
if user_traffic_over_limit(fileshare.username):
return render_error(
request, _(u'Unable to access file: share link traffic is used up.'))
shared_by = fileshare.username
if real_path == '/':
dirname = repo.name
else:
dirname = os.path.basename(real_path.rstrip('/'))
dir_id = seafile_api.get_dir_id_by_path(repo.id, real_path)
if not dir_id:
return render_error(
request, _(u'Unable to download: folder not found.'))
try:
total_size = seaserv.seafserv_threaded_rpc.get_dir_size(
repo.store_id, repo.version, dir_id)
except Exception as e:
logger.error(str(e))
return render_error(request, _(u'Internal Error'))
if total_size > seaserv.MAX_DOWNLOAD_DIR_SIZE:
return render_error(request, _(u'Unable to download directory "%s": size is too large.') % dirname)
token = seafile_api.get_fileserver_access_token(repo.id,
dir_id,
'download-dir',
request.user.username)
try:
seaserv.send_message('seahub.stats', 'dir-download\t%s\t%s\t%s\t%s' %
(repo.id, shared_by, dir_id, total_size))
send_file_access_msg(request, repo, real_path, 'web')
except Exception as e:
logger.error('Error when sending dir-download message: %s' % str(e))
return HttpResponseRedirect(gen_file_get_url(token, dirname))
@share_link_audit
def view_shared_dir(request, fileshare):
token = fileshare.token
@@ -233,11 +190,6 @@ def view_shared_dir(request, fileshare):
if not seafile_api.get_dir_id_by_path(repo.id, fileshare.path):
return render_error(request, _('"%s" does not exist.') % fileshare.path)
# download shared dir
if request.GET.get('dl', '') == '1':
return _download_dir_from_share_link(request, fileshare, repo,
real_path)
if fileshare.path == '/':
# use repo name as dir name if share whole library
dir_name = repo.name

View File

@@ -67,7 +67,7 @@ define([
_this.remove();
var msg = gettext("Successfully unlink %(name)s.")
.replace('%(name)s', Common.HTMLescape(device_name));
.replace('%(name)s', device_name);
Common.feedback(msg, 'success');
},
error: function(xhr) {
@@ -87,7 +87,7 @@ define([
_this.remove();
var msg = gettext("Successfully unlink %(name)s.")
.replace('%(name)s', Common.HTMLescape(device_name));
.replace('%(name)s', device_name);
Common.feedback(msg, 'success');
},
error: function(xhr) {

View File

@@ -197,10 +197,10 @@ define([
if (_this.op_type == 'mv') {
msg = gettext("Successfully moved %(name)s")
.replace('%(name)s', Common.HTMLescape(obj_name));
.replace('%(name)s', obj_name);
} else {
msg = gettext("Successfully copied %(name)s")
.replace('%(name)s', Common.HTMLescape(obj_name));
.replace('%(name)s', obj_name);
}
if (!data['task_id']) { // no progress

View File

@@ -807,22 +807,53 @@ define([
},
download: function () {
var dirents = this.dir,
selected_dirents = dirents.where({'selected':true}),
selected_names = '';
var dirents = this.dir;
var parent_dir = dirents.path;
var selected_dirents = dirents.where({'selected':true});
var selected_names = [];
var interval;
var zip_token;
var queryZipProgress = function() {
$.ajax({
url: Common.getUrl({name: 'query_zip_progress'}) + '?token=' + zip_token,
dataType: 'json',
cache: false,
success: function (data) {
if (data['total'] == data['zipped']) {
clearInterval(interval);
location.href = Common.getUrl({name: 'download_dir_zip_url', zip_token: zip_token});
}
},
error: function (xhr) {
Common.ajaxErrorHandler(xhr);
clearInterval(interval);
}
});
};
if (selected_dirents.length == 1 && selected_dirents[0].get('is_file')) {
// only select one file
var file_path = parent_dir + '/' + selected_dirents[0].get('obj_name');
location.href = Common.getUrl({name: 'get_file_download_url', repo_id: dirents.repo_id, file_path: encodeURIComponent(file_path)});
return false
}
$(selected_dirents).each(function() {
selected_names += this.get('obj_name') + ',';
selected_names.push(this.get('obj_name'));
});
$.ajax({
url: Common.getUrl({
name: 'download_dirents',
repo_id: dirents.repo_id
}) + '?parent_dir=' + encodeURIComponent(dirents.path) + '&dirents=' + encodeURIComponent(selected_names),
url: Common.getUrl({name: 'zip_task', repo_id: dirents.repo_id}),
data: {
'parent_dir': parent_dir,
'dirents': selected_names
},
dataType: 'json',
traditional: true,
success: function(data) {
location.href = data['url'];
zip_token = data['zip_token'];
queryZipProgress();
interval = setInterval(queryZipProgress, 1000);
},
error: function (xhr) {
Common.ajaxErrorHandler(xhr);
@@ -877,7 +908,7 @@ define([
} else {
msg_s = gettext("Successfully deleted %(name)s and %(amount)s other items.");
}
msg_s = msg_s.replace('%(name)s', Common.HTMLescape(data['deleted'][0])).replace('%(amount)s', del_len - 1);
msg_s = msg_s.replace('%(name)s', data['deleted'][0]).replace('%(amount)s', del_len - 1);
Common.feedback(msg_s, 'success');
}
if (not_del_len > 0) {
@@ -888,7 +919,7 @@ define([
} else {
msg_f = gettext("Failed to delete %(name)s and %(amount)s other items.");
}
msg_f = msg_f.replace('%(name)s', Common.HTMLescape(data['undeleted'][0])).replace('%(amount)s', not_del_len - 1);
msg_f = msg_f.replace('%(name)s', data['undeleted'][0]).replace('%(amount)s', not_del_len - 1);
Common.feedback(msg_f, 'error');
}
$.modal.close();
@@ -1033,7 +1064,7 @@ define([
}
}
msg_s = msg_s.replace('%(name)s', Common.HTMLescape(data['success'][0])).replace('%(amount)s', success_len - 1);
msg_s = msg_s.replace('%(name)s', data['success'][0]).replace('%(amount)s', success_len - 1);
//msg_s += ' <a href="' + view_url + '">' + "View" + '</a>';
Common.feedback(msg_s, 'success');
}
@@ -1052,7 +1083,7 @@ define([
msg_f = gettext("Internal error. Failed to copy %(name)s.");
}
}
msg_f = msg_f.replace('%(name)s', Common.HTMLescape(data['failed'][0])).replace('%(amount)s', data['failed'].length - 1);
msg_f = msg_f.replace('%(name)s', data['failed'][0]).replace('%(amount)s', data['failed'].length - 1);
Common.feedback(msg_f, 'error');
}
},

View File

@@ -152,7 +152,7 @@ define([
this.model.deleteFromServer({
success: function(data) {
var msg = gettext("Successfully deleted %(name)s")
.replace('%(name)s', Common.HTMLescape(dirent_name));
.replace('%(name)s', dirent_name);
Common.feedback(msg, 'success');
},
error: function(xhr) {

View File

@@ -69,6 +69,7 @@ define([
events: {
'click .select': 'select',
'click .file-star': 'starFile',
'click .download-dir': 'downloadDir',
'click .share': 'share',
'click .delete': 'del', // 'delete' is a preserve word
'click .rename': 'rename',
@@ -118,6 +119,48 @@ define([
}
},
downloadDir: function() {
var dir = this.dirView.dir;
var obj_name = this.model.get('obj_name');
var interval;
var zip_token;
var queryZipProgress = function() {
$.ajax({
url: Common.getUrl({name: 'query_zip_progress'}) + '?token=' + zip_token,
dataType: 'json',
cache: false,
success: function (data) {
if (data['total'] == data['zipped']) {
clearInterval(interval);
location.href = Common.getUrl({name: 'download_dir_zip_url', zip_token: zip_token});
}
},
error: function (xhr) {
Common.ajaxErrorHandler(xhr);
clearInterval(interval);
}
});
};
$.ajax({
url: Common.getUrl({
name: 'zip_task',
repo_id: dir.repo_id
}) + '?parent_dir=' + encodeURIComponent(dir.path) + '&dirents=' + encodeURIComponent(obj_name),
dataType: 'json',
success: function(data) {
zip_token = data['zip_token'];
queryZipProgress();
interval = setInterval(queryZipProgress, 1000);
},
error: function (xhr) {
Common.ajaxErrorHandler(xhr);
}
});
return false;
},
starFile: function() {
var _this = this;
var dir = this.dirView.dir;
@@ -185,7 +228,7 @@ define([
this.model.deleteFromServer({
success: function(data) {
var msg = gettext("Successfully deleted %(name)s")
.replace('%(name)s', Common.HTMLescape(dirent_name));
.replace('%(name)s', dirent_name);
Common.feedback(msg, 'success');
},
error: function(xhr) {

View File

@@ -54,7 +54,7 @@ define([
this.model.destroy({
wait: true,
success: function() {
var msg = gettext('Successfully unshared {placeholder}').replace('{placeholder}', '<span class="op-target">' + Common.HTMLescape(lib_name) + '</span>');
var msg = gettext('Successfully unshared 1 item.');
Common.feedback(msg, 'success', Common.SUCCESS_TIMOUT);
},
error: function(model, response) {

View File

@@ -45,7 +45,7 @@ define([
dataType: 'json',
success: function () {
el.remove();
var msg = gettext('Successfully unshared {placeholder}').replace('{placeholder}', '<span class="op-target">' + Common.HTMLescape(lib_name) + '</span>');
var msg = gettext('Successfully unshared 1 item.');
Common.feedback(msg, 'success', Common.SUCCESS_TIMOUT);
},
error: function(xhr) {

View File

@@ -328,11 +328,11 @@ define([
var after_op_success = function(data) {
$.modal.close();
var msg = gettext("Successfully sent to {placeholder}")
.replace('{placeholder}', Common.HTMLescape(data['send_success'].join(', ')));
.replace('{placeholder}', data['send_success'].join(', '));
Common.feedback(msg, 'success');
if (data['send_failed'].length > 0) {
msg += '<br />' + gettext("Failed to send to {placeholder}")
.replace('{placeholder}', Common.HTMLescape(data['send_failed'].join(', ')));
.replace('{placeholder}', data['send_failed'].join(', '));
Common.feedback(msg, 'info');
}
};

View File

@@ -42,7 +42,7 @@ define([
beforeSend: Common.prepareCSRFToken,
success: function() {
_this.remove();
Common.feedback(gettext("Successfully unstared {placeholder}").replace('{placeholder}', Common.HTMLescape(file_name)), 'success');
Common.feedback(gettext("Successfully unstared {placeholder}").replace('{placeholder}', file_name), 'success');
},
error: function (xhr) {
Common.ajaxErrorHandler(xhr);

View File

@@ -73,12 +73,15 @@ define([
getUrl: function(options) {
var siteRoot = app.config.siteRoot;
var fileServerRoot = app.config.fileServerRoot;
switch (options.name) {
// File Operations
case 'list_lib_dir': return siteRoot + 'ajax/lib/' + options.repo_id + '/dir/';
case 'del_dir': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/dir/';
case 'del_file': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/';
case 'download_dirents': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/dirents/download-link/';
case 'download_dir_zip_url': return fileServerRoot + 'zip/' + options.zip_token;
case 'zip_task': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/zip-task/';
case 'query_zip_progress': return siteRoot + 'api/v2.1/query-zip-progress/';
case 'rename_dir': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/dir/';
case 'rename_file': return siteRoot + 'api/v2.1/repos/' + options.repo_id + '/file/';
case 'mv_dir': return siteRoot + 'ajax/repo/' + options.repo_id + '/dir/mv/';
@@ -94,6 +97,7 @@ define([
case 'get_cp_progress': return siteRoot + 'ajax/cp_progress/';
case 'cancel_cp': return siteRoot + 'ajax/cancel_cp/';
case 'get_file_op_url': return siteRoot + 'ajax/repo/' + options.repo_id + '/file_op_url/';
case 'get_file_download_url': return siteRoot + 'lib/' + options.repo_id + '/file' + options.file_path + '?dl=1';
case 'get_file_uploaded_bytes': return siteRoot + 'ajax/repo/' + options.repo_id + '/get-file-uploaded-bytes/';
case 'get_dirents': return siteRoot + 'ajax/repo/' + options.repo_id + '/dirents/';
@@ -327,9 +331,9 @@ define([
feedback: function(con, type, time) {
var time = time || 5000;
if ($('.messages').length > 0) {
$('.messages').html('<li class="' + type + '">' + con + '</li>');
$('.messages').html('<li class="' + type + '">' + this.HTMLescape(con) + '</li>');
} else {
var html = '<ul class="messages"><li class="' + type + '">' + con + '</li></ul>';
var html = '<ul class="messages"><li class="' + type + '">' + this.HTMLescape(con) + '</li></ul>';
$('#main').append(html);
}
$('.messages').css({'left':($(window).width() - $('.messages').width())/2, 'top':10}).removeClass('hide');

View File

@@ -42,7 +42,7 @@ define([
_this.remove();
var msg = gettext("Successfully unlink %(name)s.")
.replace('%(name)s', Common.HTMLescape(device_name));
.replace('%(name)s', device_name);
Common.feedback(msg, 'success');
},
error: function(xhr) {

View File

@@ -6,11 +6,6 @@ from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
try:
from seahub.settings import LOCAL_PRO_DEV_ENV
except ImportError:
LOCAL_PRO_DEV_ENV = False
class DirentsDownloadLinkViewTest(BaseTestCase):

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
import json
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
class QueryZipProgressViewTest(BaseTestCase):
def _get_zip_token(self):
self.login_as(self.user)
parent_dir = '/'
folder_name = self.folder
args = '?parent_dir=%s&dirents=%s' % (parent_dir, folder_name)
url = reverse('api-v2.1-zip-task', args=[self.repo.id]) + args
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
return json_resp['zip_token']
def setUp(self):
self.url = reverse('api-v2.1-query-zip-progress')
def tearDown(self):
self.remove_repo()
def test_can_get_progress(self):
token = self._get_zip_token()
url = self.url + '?token=%s' % token
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert json_resp['total'] is not None
assert json_resp['zipped'] is not None

View File

@@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
import json
from mock import patch
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
from seahub.share.models import FileShare
class ShareLinkZipTaskViewTest(BaseTestCase):
def _add_dir_share_link(self):
fs = FileShare.objects.create_dir_link(self.user.username,
self.repo.id, self.folder, None, None)
return fs.token
def setUp(self):
self.url = reverse('api-v2.1-share-link-zip-task')
def tearDown(self):
self.remove_repo()
def test_can_get_share_link_zip_task(self):
share_link_token = self._add_dir_share_link()
url = self.url + '?share_link_token=%s&path=/' % share_link_token
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36
@patch('seahub.api2.endpoints.share_link_zip_task.settings.ENABLE_SHARE_LINK_AUDIT')
@patch('seahub.api2.endpoints.share_link_zip_task.is_pro_version')
def test_get_zip_token_with_unauthenticated_user(self,
mock_is_pro_version, mock_enable_share_link_audit):
mock_is_pro_version.return_value = True
mock_enable_share_link_audit = True
share_link_token = self._add_dir_share_link()
url = self.url + '?share_link_token=%s&path=/' % share_link_token
# user neither login in nor passed code check
resp = self.client.get(url)
self.assertEqual(403, resp.status_code)
@patch('seahub.api2.endpoints.share_link_zip_task.settings.ENABLE_SHARE_LINK_AUDIT')
@patch('seahub.api2.endpoints.share_link_zip_task.is_pro_version')
def test_get_zip_token_with_authenticated_user(self,
mock_is_pro_version, mock_enable_share_link_audit):
mock_is_pro_version.return_value = True
mock_enable_share_link_audit = True
share_link_token = self._add_dir_share_link()
# user login in
self.login_as(self.admin)
url = self.url + '?share_link_token=%s&path=/' % share_link_token
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36
@patch('seahub.api2.endpoints.share_link_zip_task.settings.ENABLE_SHARE_LINK_AUDIT')
@patch('seahub.api2.endpoints.share_link_zip_task.is_pro_version')
def test_get_zip_token_with_anonymous_user_passed_code_check(self,
mock_is_pro_version, mock_enable_share_link_audit):
mock_is_pro_version.return_value = True
mock_enable_share_link_audit = True
share_link_token = self._add_dir_share_link()
url = self.url + '?share_link_token=%s&path=/' % share_link_token
# user pass code check
session = self.client.session
session['anonymous_email'] = 'anonymous@email.com'
session.save()
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36

View File

@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
import os
import json
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
from seaserv import seafile_api
try:
from seahub.settings import LOCAL_PRO_DEV_ENV
except ImportError:
LOCAL_PRO_DEV_ENV = False
class ZipTaskViewTest(BaseTestCase):
def setUp(self):
self.repo_id = self.repo.id
self.folder_path = self.folder
self.folder_name = os.path.basename(self.folder_path)
self.url = reverse('api-v2.1-zip-task', args=[self.repo_id])
def tearDown(self):
self.remove_repo()
def test_can_get_download_dir_zip_token(self):
self.login_as(self.user)
parent_dir = '/'
folder_name = self.folder_name
url = self.url + '?parent_dir=%s&dirents=%s' % (parent_dir, folder_name)
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36
def test_can_get_download_multi_zip_token(self):
# create another folder for download multi
another_folder_name = 'another_folder_name'
seafile_api.post_dir(repo_id=self.repo.id,
parent_dir='/', dirname=another_folder_name,
username=self.user.username)
self.login_as(self.user)
parent_dir = '/'
folder_name = self.folder_name
url = self.url + '?parent_dir=%s&dirents=%s&dirents=%s' % (parent_dir,
folder_name, another_folder_name)
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36
def test_can_get_zip_token_with_invalid_repo_permission(self):
self.login_as(self.admin)
parent_dir = '/'
folder_name = self.folder_name
url = self.url + '?parent_dir=%s&dirents=%s' % (parent_dir, folder_name)
resp = self.client.get(url)
self.assertEqual(403, resp.status_code)
def test_can_get_zip_token_for_r_permission_folder(self):
if not LOCAL_PRO_DEV_ENV:
return
self.set_user_folder_r_permission_to_admin()
self.login_as(self.admin)
parent_dir = '/'
folder_name = self.folder_name
url = self.url + '?parent_dir=%s&dirents=%s' % (parent_dir, folder_name)
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36
def test_can_get_zip_token_for_rw_permission_folder(self):
if not LOCAL_PRO_DEV_ENV:
return
self.set_user_folder_rw_permission_to_admin()
self.login_as(self.admin)
parent_dir = '/'
folder_name = self.folder_name
url = self.url + '?parent_dir=%s&dirents=%s' % (parent_dir, folder_name)
resp = self.client.get(url)
self.assertEqual(200, resp.status_code)
json_resp = json.loads(resp.content)
assert len(json_resp['zip_token']) == 36

View File

@@ -28,16 +28,6 @@ class SharedDirTest(TestCase, Fixtures):
self.assertContains(resp, '<h2>%s</h2>' % self.repo.name)
zip_url = 'href="?p=/&dl=1"'
self.assertContains(resp, zip_url)
def test_can_download(self):
dl_url = reverse('view_shared_dir', args=[self.fs.token]) + \
'?p=/&dl=1'
resp = self.client.get(dl_url)
self.assertEqual(302, resp.status_code)
assert '8082/files/' in resp.get('location')
def test_view_raw_file_via_shared_dir(self):
resp = self.client.get(
reverse('view_file_via_shared_dir', args=[self.fs.token]) + '?p=' + self.file + '&raw=1'