mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-28 19:51:34 +00:00
Merge branch '6.3'
This commit is contained in:
commit
d772dba34b
@ -502,7 +502,6 @@ class DirDetailView(APIView):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
dir_obj = seafile_api.get_dirent_by_path(repo_id, path)
|
dir_obj = seafile_api.get_dirent_by_path(repo_id, path)
|
||||||
count_info = seafile_api.get_file_count_info_by_path(repo_id, path)
|
|
||||||
except SearpcError as e:
|
except SearpcError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
error_msg = 'Internal Server Error'
|
error_msg = 'Internal Server Error'
|
||||||
@ -512,9 +511,6 @@ class DirDetailView(APIView):
|
|||||||
'repo_id': repo_id,
|
'repo_id': repo_id,
|
||||||
'path': path,
|
'path': path,
|
||||||
'name': dir_obj.obj_name,
|
'name': dir_obj.obj_name,
|
||||||
'file_count': count_info.file_count,
|
|
||||||
'dir_count': count_info.dir_count,
|
|
||||||
'size': count_info.size,
|
|
||||||
'mtime': timestamp_to_isoformat_timestr(dir_obj.mtime),
|
'mtime': timestamp_to_isoformat_timestr(dir_obj.mtime),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,25 +77,7 @@ def get_share_link_info(fileshare):
|
|||||||
data['permissions'] = fileshare.get_permissions()
|
data['permissions'] = fileshare.get_permissions()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class ShareLinks(APIView):
|
def check_permissions_arg(request):
|
||||||
|
|
||||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
|
||||||
permission_classes = (IsAuthenticated, CanGenerateShareLink)
|
|
||||||
throttle_classes = (UserRateThrottle,)
|
|
||||||
|
|
||||||
def _generate_obj_id_and_type_by_path(self, repo_id, path):
|
|
||||||
|
|
||||||
file_id = seafile_api.get_file_id_by_path(repo_id, path)
|
|
||||||
if file_id:
|
|
||||||
return (file_id, 'f')
|
|
||||||
|
|
||||||
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
|
|
||||||
if dir_id:
|
|
||||||
return (dir_id, 'd')
|
|
||||||
|
|
||||||
return (None, None)
|
|
||||||
|
|
||||||
def _check_permissions_arg(self, request):
|
|
||||||
permissions = request.data.get('permissions', None)
|
permissions = request.data.get('permissions', None)
|
||||||
if permissions is not None:
|
if permissions is not None:
|
||||||
if isinstance(permissions, dict):
|
if isinstance(permissions, dict):
|
||||||
@ -134,6 +116,24 @@ class ShareLinks(APIView):
|
|||||||
|
|
||||||
return perm
|
return perm
|
||||||
|
|
||||||
|
class ShareLinks(APIView):
|
||||||
|
|
||||||
|
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||||
|
permission_classes = (IsAuthenticated, CanGenerateShareLink)
|
||||||
|
throttle_classes = (UserRateThrottle,)
|
||||||
|
|
||||||
|
def _generate_obj_id_and_type_by_path(self, repo_id, path):
|
||||||
|
|
||||||
|
file_id = seafile_api.get_file_id_by_path(repo_id, path)
|
||||||
|
if file_id:
|
||||||
|
return (file_id, 'f')
|
||||||
|
|
||||||
|
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
|
||||||
|
if dir_id:
|
||||||
|
return (dir_id, 'd')
|
||||||
|
|
||||||
|
return (None, None)
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
""" Get all share links of a user.
|
""" Get all share links of a user.
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ class ShareLinks(APIView):
|
|||||||
else:
|
else:
|
||||||
expire_date = timezone.now() + relativedelta(days=expire_days)
|
expire_date = timezone.now() + relativedelta(days=expire_days)
|
||||||
|
|
||||||
perm = self._check_permissions_arg(request)
|
perm = check_permissions_arg(request)
|
||||||
|
|
||||||
# resource check
|
# resource check
|
||||||
repo = seafile_api.get_repo(repo_id)
|
repo = seafile_api.get_repo(repo_id)
|
||||||
@ -317,6 +317,33 @@ class ShareLink(APIView):
|
|||||||
link_info = get_share_link_info(fs)
|
link_info = get_share_link_info(fs)
|
||||||
return Response(link_info)
|
return Response(link_info)
|
||||||
|
|
||||||
|
def put(self, request, token):
|
||||||
|
""" Update share link, currently only available for permission.
|
||||||
|
|
||||||
|
Permission checking:
|
||||||
|
share link creater
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
fs = FileShare.objects.get(token=token)
|
||||||
|
except FileShare.DoesNotExist:
|
||||||
|
error_msg = 'token %s not found.' % token
|
||||||
|
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||||
|
|
||||||
|
username = request.user.username
|
||||||
|
if not fs.is_owner(username):
|
||||||
|
error_msg = 'Permission denied.'
|
||||||
|
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||||
|
|
||||||
|
permissions = request.data.get('permissions', None)
|
||||||
|
if permissions:
|
||||||
|
perm = check_permissions_arg(request)
|
||||||
|
fs.permission = perm
|
||||||
|
fs.save()
|
||||||
|
|
||||||
|
link_info = get_share_link_info(fs)
|
||||||
|
return Response(link_info)
|
||||||
|
|
||||||
def delete(self, request, token):
|
def delete(self, request, token):
|
||||||
""" Delete share link.
|
""" Delete share link.
|
||||||
|
|
||||||
|
@ -427,6 +427,8 @@ class Search(APIView):
|
|||||||
try:
|
try:
|
||||||
current_page = int(request.GET.get('page', '1'))
|
current_page = int(request.GET.get('page', '1'))
|
||||||
per_page = int(request.GET.get('per_page', '10'))
|
per_page = int(request.GET.get('per_page', '10'))
|
||||||
|
if per_page > 100:
|
||||||
|
per_page = 100
|
||||||
except ValueError:
|
except ValueError:
|
||||||
current_page = 1
|
current_page = 1
|
||||||
per_page = 10
|
per_page = 10
|
||||||
@ -1050,7 +1052,6 @@ class PubRepos(APIView):
|
|||||||
repo = {
|
repo = {
|
||||||
"id": r.repo_id,
|
"id": r.repo_id,
|
||||||
"name": r.repo_name,
|
"name": r.repo_name,
|
||||||
"desc": r.repo_desc,
|
|
||||||
"owner": r.user,
|
"owner": r.user,
|
||||||
"owner_nickname": email2nickname(r.user),
|
"owner_nickname": email2nickname(r.user),
|
||||||
"owner_name": email2nickname(r.user),
|
"owner_name": email2nickname(r.user),
|
||||||
@ -1060,12 +1061,7 @@ class PubRepos(APIView):
|
|||||||
"size_formatted": filesizeformat(r.size),
|
"size_formatted": filesizeformat(r.size),
|
||||||
"encrypted": r.encrypted,
|
"encrypted": r.encrypted,
|
||||||
"permission": r.permission,
|
"permission": r.permission,
|
||||||
"root": r.root,
|
|
||||||
}
|
}
|
||||||
if r.encrypted:
|
|
||||||
repo["enc_version"] = r.enc_version
|
|
||||||
repo["magic"] = r.magic
|
|
||||||
repo["random_key"] = r.random_key
|
|
||||||
repos_json.append(repo)
|
repos_json.append(repo)
|
||||||
|
|
||||||
return Response(repos_json)
|
return Response(repos_json)
|
||||||
@ -4714,7 +4710,6 @@ class GroupRepos(APIView):
|
|||||||
repo = {
|
repo = {
|
||||||
"id": r.id,
|
"id": r.id,
|
||||||
"name": r.name,
|
"name": r.name,
|
||||||
"desc": r.desc,
|
|
||||||
"size": r.size,
|
"size": r.size,
|
||||||
"size_formatted": filesizeformat(r.size),
|
"size_formatted": filesizeformat(r.size),
|
||||||
"mtime": r.last_modified,
|
"mtime": r.last_modified,
|
||||||
|
@ -301,6 +301,7 @@ ENABLE_UPLOAD_FOLDER = True
|
|||||||
|
|
||||||
# enable resumable fileupload or not
|
# enable resumable fileupload or not
|
||||||
ENABLE_RESUMABLE_FILEUPLOAD = False
|
ENABLE_RESUMABLE_FILEUPLOAD = False
|
||||||
|
RESUMABLE_UPLOAD_FILE_BLOCK_SIZE = 8
|
||||||
|
|
||||||
## maxNumberOfFiles for fileupload
|
## maxNumberOfFiles for fileupload
|
||||||
MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD = 1000
|
MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD = 1000
|
||||||
|
@ -1148,19 +1148,6 @@
|
|||||||
<th width="35%"></th>
|
<th width="35%"></th>
|
||||||
<td width="65%"></td>
|
<td width="65%"></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<th>{% trans "Folders" %}</th>
|
|
||||||
<td class="dir-folder-counts"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>{% trans "Files" %}</th>
|
|
||||||
<td class="dir-file-counts"></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th>{% trans "Size" %}</th>
|
|
||||||
<td class="dir-size"></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Location" %}</th>
|
<th>{% trans "Location" %}</th>
|
||||||
<td><%- path %></td>
|
<td><%- path %></td>
|
||||||
|
@ -285,6 +285,7 @@ app["pageOptions"] = {
|
|||||||
enable_wiki: {% if enable_wiki %} true {% else %} false {% endif %},
|
enable_wiki: {% if enable_wiki %} true {% else %} false {% endif %},
|
||||||
enable_upload_folder: {% if enable_upload_folder %} true {% else %} false {% endif %},
|
enable_upload_folder: {% if enable_upload_folder %} true {% else %} false {% endif %},
|
||||||
enable_resumable_fileupload: {% if enable_resumable_fileupload %} true {% else %} false {% endif %},
|
enable_resumable_fileupload: {% if enable_resumable_fileupload %} true {% else %} false {% endif %},
|
||||||
|
resumable_upload_file_block_size: {{ resumable_upload_file_block_size }},
|
||||||
max_number_of_files_for_fileupload: {{ max_number_of_files_for_fileupload }},
|
max_number_of_files_for_fileupload: {{ max_number_of_files_for_fileupload }},
|
||||||
enable_thumbnail: {% if enable_thumbnail %} true {% else %} false {% endif %},
|
enable_thumbnail: {% if enable_thumbnail %} true {% else %} false {% endif %},
|
||||||
thumbnail_default_size: {{ thumbnail_default_size }},
|
thumbnail_default_size: {{ thumbnail_default_size }},
|
||||||
|
@ -353,7 +353,6 @@ def get_user_repos(username, org_id=None):
|
|||||||
# repos.
|
# repos.
|
||||||
r.id = r.repo_id
|
r.id = r.repo_id
|
||||||
r.name = r.repo_name
|
r.name = r.repo_name
|
||||||
r.desc = r.repo_desc
|
|
||||||
r.last_modify = r.last_modified
|
r.last_modify = r.last_modified
|
||||||
else:
|
else:
|
||||||
owned_repos = seafile_api.get_org_owned_repo_list(org_id,
|
owned_repos = seafile_api.get_org_owned_repo_list(org_id,
|
||||||
@ -369,7 +368,6 @@ def get_user_repos(username, org_id=None):
|
|||||||
# repos.
|
# repos.
|
||||||
r.id = r.repo_id
|
r.id = r.repo_id
|
||||||
r.name = r.repo_name
|
r.name = r.repo_name
|
||||||
r.desc = r.repo_desc
|
|
||||||
r.last_modify = r.last_modified
|
r.last_modify = r.last_modified
|
||||||
|
|
||||||
return (owned_repos, shared_repos, groups_repos, public_repos)
|
return (owned_repos, shared_repos, groups_repos, public_repos)
|
||||||
|
@ -720,6 +720,7 @@ def libraries(request):
|
|||||||
'enable_wiki': request.user.permissions.can_use_wiki(),
|
'enable_wiki': request.user.permissions.can_use_wiki(),
|
||||||
'enable_upload_folder': settings.ENABLE_UPLOAD_FOLDER,
|
'enable_upload_folder': settings.ENABLE_UPLOAD_FOLDER,
|
||||||
'enable_resumable_fileupload': settings.ENABLE_RESUMABLE_FILEUPLOAD,
|
'enable_resumable_fileupload': settings.ENABLE_RESUMABLE_FILEUPLOAD,
|
||||||
|
'resumable_upload_file_block_size': settings.RESUMABLE_UPLOAD_FILE_BLOCK_SIZE,
|
||||||
'max_number_of_files_for_fileupload': settings.MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD,
|
'max_number_of_files_for_fileupload': settings.MAX_NUMBER_OF_FILES_FOR_FILEUPLOAD,
|
||||||
'enable_thumbnail': settings.ENABLE_THUMBNAIL,
|
'enable_thumbnail': settings.ENABLE_THUMBNAIL,
|
||||||
'enable_repo_snapshot_label': settings.ENABLE_REPO_SNAPSHOT_LABEL,
|
'enable_repo_snapshot_label': settings.ENABLE_REPO_SNAPSHOT_LABEL,
|
||||||
|
@ -49,9 +49,6 @@ define([
|
|||||||
if (part_data.error_msg) {
|
if (part_data.error_msg) {
|
||||||
$('.error', $container).html(part_data.error_msg).show();
|
$('.error', $container).html(part_data.error_msg).show();
|
||||||
} else {
|
} else {
|
||||||
this.$('.dir-folder-counts').html(part_data.dir_count);
|
|
||||||
this.$('.dir-file-counts').html(part_data.file_count);
|
|
||||||
this.$('.dir-size').html(part_data.size);
|
|
||||||
$('table', $container).show();
|
$('table', $container).show();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -192,7 +192,7 @@ define([
|
|||||||
data.jqXHR = popup.fileupload('send', data);
|
data.jqXHR = popup.fileupload('send', data);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
var block_size = 1024 * 1024;
|
var block_size = app.pageOptions.resumable_upload_file_block_size * 1024 * 1024;
|
||||||
if (enable_resumable_fileupload &&
|
if (enable_resumable_fileupload &&
|
||||||
file.size && file.size > block_size) {
|
file.size && file.size > block_size) {
|
||||||
popup.fileupload('option', 'maxChunkSize', block_size);
|
popup.fileupload('option', 'maxChunkSize', block_size);
|
||||||
|
@ -616,8 +616,6 @@ class DirDetailViewTest(BaseTestCase):
|
|||||||
json_resp = json.loads(resp.content)
|
json_resp = json.loads(resp.content)
|
||||||
|
|
||||||
assert json_resp['name'] == self.folder_name
|
assert json_resp['name'] == self.folder_name
|
||||||
assert json_resp['file_count'] == 1
|
|
||||||
assert json_resp['dir_count'] == 1
|
|
||||||
|
|
||||||
def test_get_dir_detail_with_invalid_perm(self):
|
def test_get_dir_detail_with_invalid_perm(self):
|
||||||
|
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from mock import patch
|
from mock import patch
|
||||||
from seaserv import seafile_api
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from seahub.test_utils import BaseTestCase
|
from seahub.test_utils import BaseTestCase
|
||||||
from seahub.share.models import FileShare
|
from seahub.share.models import FileShare
|
||||||
from seahub.api2.permissions import CanGenerateShareLink
|
from seahub.api2.permissions import CanGenerateShareLink
|
||||||
|
from seahub.utils import gen_token
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from seahub.settings import LOCAL_PRO_DEV_ENV
|
from seahub.settings import LOCAL_PRO_DEV_ENV
|
||||||
@ -41,7 +41,7 @@ class ShareLinksTest(BaseTestCase):
|
|||||||
link = FileShare.objects.get(token=token)
|
link = FileShare.objects.get(token=token)
|
||||||
link.delete()
|
link.delete()
|
||||||
|
|
||||||
def test_get_file_share_link(self):
|
def test_get_file_share_links(self):
|
||||||
self.login_as(self.user)
|
self.login_as(self.user)
|
||||||
token = self._add_file_share_link()
|
token = self._add_file_share_link()
|
||||||
|
|
||||||
@ -309,3 +309,139 @@ class ShareLinksTest(BaseTestCase):
|
|||||||
url = reverse('api-v2.1-share-link', args=[token])
|
url = reverse('api-v2.1-share-link', args=[token])
|
||||||
resp = self.client.delete(url, {}, 'application/x-www-form-urlencoded')
|
resp = self.client.delete(url, {}, 'application/x-www-form-urlencoded')
|
||||||
self.assertEqual(403, resp.status_code)
|
self.assertEqual(403, resp.status_code)
|
||||||
|
|
||||||
|
|
||||||
|
class ShareLinkTest(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.repo_id = self.repo.id
|
||||||
|
self.file_path= self.file
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.remove_repo()
|
||||||
|
|
||||||
|
def _remove_share_link(self, token):
|
||||||
|
link = FileShare.objects.get(token=token)
|
||||||
|
link.delete()
|
||||||
|
|
||||||
|
def test_get_file_share_link(self):
|
||||||
|
|
||||||
|
self.login_as(self.user)
|
||||||
|
|
||||||
|
# create a share link first
|
||||||
|
json_str = json.dumps({'path': self.file_path, 'repo_id': self.repo_id})
|
||||||
|
url = reverse('api-v2.1-share-links')
|
||||||
|
resp = self.client.post(url, json_str,
|
||||||
|
content_type="application/json")
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
token = json_resp['token']
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-share-link', args=[token])
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert json_resp['token'] == token
|
||||||
|
|
||||||
|
self._remove_share_link(token)
|
||||||
|
|
||||||
|
def test_get_file_share_link_with_invalid_token(self):
|
||||||
|
|
||||||
|
self.login_as(self.user)
|
||||||
|
invalid_token = gen_token(max_length=20)
|
||||||
|
url = reverse('api-v2.1-share-link', args=[invalid_token])
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(404, resp.status_code)
|
||||||
|
|
||||||
|
def test_update_file_share_link_permissions(self):
|
||||||
|
|
||||||
|
self.login_as(self.user)
|
||||||
|
|
||||||
|
# create a share link first
|
||||||
|
# all True
|
||||||
|
json_str = json.dumps({'path': self.file_path, 'repo_id': self.repo_id,
|
||||||
|
'permissions': {
|
||||||
|
'can_edit': True,
|
||||||
|
'can_download': True
|
||||||
|
}})
|
||||||
|
url = reverse('api-v2.1-share-links')
|
||||||
|
resp = self.client.post(url, json_str,
|
||||||
|
content_type="application/json")
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert json_resp['permissions']['can_edit'] == True
|
||||||
|
assert json_resp['permissions']['can_download'] == True
|
||||||
|
|
||||||
|
token = json_resp['token']
|
||||||
|
|
||||||
|
# update share link permission
|
||||||
|
# all False
|
||||||
|
json_str = json.dumps({'permissions': {'can_edit': False, 'can_download': False}})
|
||||||
|
url = reverse('api-v2.1-share-link', args=[token])
|
||||||
|
resp = self.client.put(url, json_str,
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert json_resp['token'] == token
|
||||||
|
assert json_resp['permissions']['can_edit'] == False
|
||||||
|
assert json_resp['permissions']['can_download'] == False
|
||||||
|
|
||||||
|
self._remove_share_link(token)
|
||||||
|
|
||||||
|
def test_update_file_share_link_permission_if_not_creator(self):
|
||||||
|
|
||||||
|
self.login_as(self.user)
|
||||||
|
|
||||||
|
# create a share link first
|
||||||
|
# all True
|
||||||
|
json_str = json.dumps({'path': self.file_path, 'repo_id': self.repo_id,
|
||||||
|
'permissions': {
|
||||||
|
'can_edit': True,
|
||||||
|
'can_download': True
|
||||||
|
}})
|
||||||
|
url = reverse('api-v2.1-share-links')
|
||||||
|
resp = self.client.post(url, json_str,
|
||||||
|
content_type="application/json")
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
assert json_resp['permissions']['can_edit'] == True
|
||||||
|
assert json_resp['permissions']['can_download'] == True
|
||||||
|
|
||||||
|
token = json_resp['token']
|
||||||
|
|
||||||
|
self.logout()
|
||||||
|
self.login_as(self.admin)
|
||||||
|
|
||||||
|
# update share link permission
|
||||||
|
# all False
|
||||||
|
json_str = json.dumps({'permissions': {'can_edit': False, 'can_download': False}})
|
||||||
|
url = reverse('api-v2.1-share-link', args=[token])
|
||||||
|
resp = self.client.put(url, json_str,
|
||||||
|
content_type="application/json")
|
||||||
|
|
||||||
|
self.assertEqual(403, resp.status_code)
|
||||||
|
self._remove_share_link(token)
|
||||||
|
|
||||||
|
def test_delete_file_share_link(self):
|
||||||
|
|
||||||
|
self.login_as(self.user)
|
||||||
|
|
||||||
|
# create a share link first
|
||||||
|
json_str = json.dumps({'path': self.file_path, 'repo_id': self.repo_id})
|
||||||
|
url = reverse('api-v2.1-share-links')
|
||||||
|
resp = self.client.post(url, json_str,
|
||||||
|
content_type="application/json")
|
||||||
|
json_resp = json.loads(resp.content)
|
||||||
|
token = json_resp['token']
|
||||||
|
|
||||||
|
url = reverse('api-v2.1-share-link', args=[token])
|
||||||
|
resp = self.client.get(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
# delete share link
|
||||||
|
url = reverse('api-v2.1-share-link', args=[token])
|
||||||
|
resp = self.client.delete(url)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
assert not FileShare.objects.filter(token=token)
|
||||||
|
Loading…
Reference in New Issue
Block a user