1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-07-01 09:24:35 +00:00
seahub/seahub/api2/endpoints/share_links.py
2024-12-06 21:42:04 +08:00

1532 lines
59 KiB
Python

# Copyright (c) 2012-2016 Seafile Ltd.
import os
import stat
import json
import time
import logging
import posixpath
from constance import config
from dateutil.relativedelta import relativedelta
import dateutil.parser
from collections import defaultdict
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 import timezone
from django.utils.timezone import get_current_timezone
from django.utils.translation import gettext as _
from urllib.parse import quote
from seaserv import seafile_api
from pysearpc import SearpcError
from seahub.api2.utils import api_error, send_share_link_emails
from seahub.api2.authentication import TokenAuthentication
from seahub.api2.throttling import UserRateThrottle
from seahub.api2.permissions import CanGenerateShareLink, IsProVersion
from seahub.base.accounts import User
from seahub.base.templatetags.seahub_tags import email2nickname
from seahub.constants import PERMISSION_READ_WRITE, PERMISSION_READ, \
PERMISSION_PREVIEW_EDIT, PERMISSION_PREVIEW
from seahub.share.models import FileShare, UploadLinkShare, check_share_link_access, check_share_link_access_by_scope
from seahub.share.decorators import check_share_link_count
from seahub.share.utils import VALID_SHARE_LINK_SCOPE, SCOPE_SPECIFIC_USERS, SCOPE_SPECIFIC_EMAILS
from seahub.utils import gen_shared_link, is_org_context, normalize_file_path, \
normalize_dir_path, is_pro_version, get_file_type_and_ext, \
check_filename_with_rename, gen_file_upload_url, \
get_password_strength_level, is_valid_password, is_valid_email, string2list, gen_file_get_url_by_sharelink
from seahub.utils.file_op import if_locked_by_online_office
from seahub.utils.file_types import IMAGE, VIDEO, XMIND
from seahub.utils.file_tags import get_tagged_files, get_files_tags_in_dir
from seahub.utils.timeutils import datetime_to_isoformat_timestr, \
timestamp_to_isoformat_timestr
from seahub.utils.repo import parse_repo_perm
from seahub.thumbnail.utils import get_share_link_thumbnail_src
from seahub.settings import SHARE_LINK_EXPIRE_DAYS_MAX, \
SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_LOGIN_REQUIRED, \
SHARE_LINK_EXPIRE_DAYS_DEFAULT, THUMBNAIL_DEFAULT_SIZE, \
ENABLE_VIDEO_THUMBNAIL, \
THUMBNAIL_ROOT, ENABLE_UPLOAD_LINK_VIRUS_CHECK
from seahub.wiki.models import Wiki
from seahub.views.file import can_edit_file
from seahub.views import check_folder_permission
from seahub.signals import upload_file_successful, upload_folder_successful
from seahub.repo_tags.models import RepoTags
logger = logging.getLogger(__name__)
def get_share_link_info(fileshare):
data = {}
token = fileshare.token
repo_id = fileshare.repo_id
try:
repo = seafile_api.get_repo(repo_id)
except Exception as e:
logger.error(e)
repo = None
path = fileshare.path
if path:
obj_name = '/' if path == '/' else os.path.basename(path.rstrip('/'))
else:
obj_name = ''
if repo:
if fileshare.s_type == 'd':
folder_path = normalize_dir_path(fileshare.path)
obj_id = seafile_api.get_dir_id_by_path(repo_id, folder_path)
else:
file_path = normalize_file_path(fileshare.path)
obj_id = seafile_api.get_file_id_by_path(repo_id, file_path)
else:
obj_id = ''
if fileshare.expire_date:
expire_date = datetime_to_isoformat_timestr(fileshare.expire_date)
else:
expire_date = ''
if fileshare.ctime:
ctime = datetime_to_isoformat_timestr(fileshare.ctime)
else:
ctime = ''
data['username'] = fileshare.username
data['repo_id'] = repo_id
data['repo_name'] = repo.repo_name if repo else ''
data['path'] = path
data['obj_name'] = obj_name
data['obj_id'] = obj_id or ""
data['is_dir'] = True if fileshare.s_type == 'd' else False
data['token'] = token
data['link'] = gen_shared_link(token, fileshare.s_type)
data['download_link'] = gen_file_get_url_by_sharelink(token)
data['show_download_link'] = fileshare.show_download_link
data['view_cnt'] = fileshare.view_cnt
data['ctime'] = ctime
data['expire_date'] = expire_date
data['is_expired'] = fileshare.is_expired()
data['permissions'] = fileshare.get_permissions()
data['password'] = fileshare.get_password()
data['user_scope'] = fileshare.user_scope
data['can_edit'] = False
if repo and path != '/' and not data['is_dir']:
try:
dirent = seafile_api.get_dirent_by_path(repo_id, path)
except Exception as e:
logger.error(e)
dirent = None
if dirent:
try:
can_edit, error_msg = can_edit_file(obj_name, dirent.size, repo)
data['can_edit'] = can_edit
except Exception as e:
logger.error(e)
else:
data['can_edit'] = False
return data
def check_permissions_arg(request):
permissions = request.data.get('permissions', '')
if not permissions:
return FileShare.PERM_VIEW_DL
if isinstance(permissions, dict):
perm_dict = permissions
elif isinstance(permissions, str):
perm_dict = json.loads(str(permissions))
can_edit = perm_dict.get('can_edit', False)
can_download = perm_dict.get('can_download', True)
can_upload = perm_dict.get('can_upload', False)
if not can_edit and can_download:
perm = FileShare.PERM_VIEW_DL
if not can_edit and not can_download:
perm = FileShare.PERM_VIEW_ONLY
if can_edit and can_download:
perm = FileShare.PERM_EDIT_DL
if can_edit and not can_download:
perm = FileShare.PERM_EDIT_ONLY
if not can_edit and can_download and can_upload:
perm = FileShare.PERM_VIEW_DL_UPLOAD
return perm
class ShareLinks(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, CanGenerateShareLink)
throttle_classes = (UserRateThrottle,)
def get(self, request):
""" Get all share links of a user.
Permission checking:
1. default(NOT guest) user;
"""
try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '25'))
except ValueError:
current_page = 1
per_page = 25
offset = per_page * (current_page - 1)
username = request.user.username
repo_id = request.GET.get('repo_id', '')
path = request.GET.get('path', '')
fileshares = []
# get all share links of current user
if not repo_id and not path:
fileshares = FileShare.objects.filter(username=username)[offset:offset + per_page]
# share links in repo
if repo_id and not path:
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)
fileshares = FileShare.objects.filter(username=username) \
.filter(repo_id=repo_id)[offset:offset + per_page]
# share links by repo and path
if repo_id and path:
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 path != '/':
dirent = seafile_api.get_dirent_by_path(repo_id, path)
if not dirent:
error_msg = 'Dirent %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if stat.S_ISDIR(dirent.mode):
path = normalize_dir_path(path)
else:
path = normalize_file_path(path)
fileshares = FileShare.objects.filter(username=username) \
.filter(repo_id=repo_id) \
.filter(path=path)[offset:offset + per_page]
repo_object_dict = {}
repo_folder_permission_dict = {}
for fileshare in fileshares:
repo_id = fileshare.repo_id
path = fileshare.path
if repo_id not in repo_object_dict:
repo = seafile_api.get_repo(repo_id)
repo_object_dict[repo_id] = repo
tmp_key = f"{repo_id}_{path}"
if tmp_key not in repo_folder_permission_dict:
try:
permission = seafile_api.check_permission_by_path(repo_id,
path,
username)
repo_folder_permission_dict[tmp_key] = permission
except Exception:
repo_folder_permission_dict[tmp_key] = ''
links_info = []
for fs in fileshares:
link_info = {}
token = fs.token
repo_id = fs.repo_id
path = fs.path
s_type = fs.s_type
link_info['username'] = username
link_info['repo_id'] = repo_id
repo_object = repo_object_dict.get(repo_id, '')
if repo_object:
repo_name = repo_object.repo_name
else:
repo_name = ''
if path:
obj_name = '/' if path == '/' else os.path.basename(path.rstrip('/'))
else:
obj_name = ''
link_info['repo_name'] = repo_name
link_info['path'] = path
link_info['obj_name'] = obj_name
link_info['is_dir'] = True if s_type == 'd' else False
link_info['token'] = token
link_info['link'] = gen_shared_link(token, s_type)
link_info['download_link'] = gen_file_get_url_by_sharelink(token)
link_info['show_download_link'] = fs.show_download_link
link_info['view_cnt'] = fs.view_cnt
link_info['ctime'] = datetime_to_isoformat_timestr(fs.ctime) if fs.ctime else ''
link_info['expire_date'] = datetime_to_isoformat_timestr(fs.expire_date) if fs.expire_date else ''
link_info['is_expired'] = fs.is_expired()
link_info['permissions'] = fs.get_permissions()
link_info['password'] = fs.get_password()
link_info['user_scope'] = fs.user_scope
tmp_key = f"{repo_id}_{path}"
link_info['repo_folder_permission'] = repo_folder_permission_dict.get(tmp_key, "")
links_info.append(link_info)
if len(links_info) == 1:
result = links_info
else:
dir_list = [x for x in links_info if x['is_dir']]
file_list = [x for x in links_info if not x['is_dir']]
dir_list.sort(key=lambda x: x['obj_name'])
file_list.sort(key=lambda x: x['obj_name'])
result = dir_list + file_list
return Response(result)
@check_share_link_count
def post(self, request):
""" Create share link.
Permission checking:
1. default(NOT guest) user;
"""
# argument check
repo_id = request.data.get('repo_id', None)
if not repo_id:
error_msg = 'repo_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
path = request.data.get('path', None)
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
password = request.data.get('password', None)
if config.SHARE_LINK_FORCE_USE_PASSWORD and not password:
error_msg = _('Password is required.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if password:
if len(password) < config.SHARE_LINK_PASSWORD_MIN_LENGTH:
error_msg = _('Password is too short.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if get_password_strength_level(password) < config.SHARE_LINK_PASSWORD_STRENGTH_LEVEL:
error_msg = _('Password is too weak.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if not is_valid_password(password):
error_msg = _('Password can only contain number, upper letter, lower letter and other symbols.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
expire_days = request.data.get('expire_days', '')
expiration_time = request.data.get('expiration_time', '')
if expire_days and expiration_time:
error_msg = 'Can not pass expire_days and expiration_time at the same time.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
expire_date = None
if expire_days:
try:
expire_days = int(expire_days)
except ValueError:
error_msg = 'expire_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if expire_days <= 0:
error_msg = 'expire_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_EXPIRE_DAYS_MIN > 0:
if expire_days < SHARE_LINK_EXPIRE_DAYS_MIN:
error_msg = _('Expire days should be greater or equal to %s') % \
SHARE_LINK_EXPIRE_DAYS_MIN
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_EXPIRE_DAYS_MAX > 0:
if expire_days > SHARE_LINK_EXPIRE_DAYS_MAX:
error_msg = _('Expire days should be less than or equal to %s') % \
SHARE_LINK_EXPIRE_DAYS_MAX
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
expire_date = timezone.now() + relativedelta(days=expire_days)
elif expiration_time:
try:
expire_date = dateutil.parser.isoparse(expiration_time)
except Exception as e:
logger.error(e)
error_msg = 'expiration_time invalid, should be iso format, for example: 2020-05-17T10:26:22+08:00'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
expire_date = expire_date.astimezone(get_current_timezone()).replace(tzinfo=None)
if SHARE_LINK_EXPIRE_DAYS_MIN > 0:
expire_date_min_limit = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_MIN)
expire_date_min_limit = expire_date_min_limit.replace(hour=0).replace(minute=0).replace(second=0)
if expire_date < expire_date_min_limit:
error_msg = _('Expiration time should be later than %s.') % \
expire_date_min_limit.strftime("%Y-%m-%d %H:%M:%S")
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_EXPIRE_DAYS_MAX > 0:
expire_date_max_limit = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_MAX)
expire_date_max_limit = expire_date_max_limit.replace(hour=23).replace(minute=59).replace(second=59)
if expire_date > expire_date_max_limit:
error_msg = _('Expiration time should be earlier than %s.') % \
expire_date_max_limit.strftime("%Y-%m-%d %H:%M:%S")
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
else:
if SHARE_LINK_EXPIRE_DAYS_DEFAULT > 0:
expire_date = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_DEFAULT)
try:
perm = check_permissions_arg(request)
except Exception:
error_msg = 'permissions invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource 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 path != '/':
dirent = seafile_api.get_dirent_by_path(repo_id, path)
if not dirent:
error_msg = 'Dirent %s not found.' % path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
if repo.encrypted:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
username = request.user.username
repo_folder_permission = seafile_api.check_permission_by_path(repo_id, path, username)
if parse_repo_perm(repo_folder_permission).can_generate_share_link is False:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if repo_folder_permission in (PERMISSION_PREVIEW_EDIT, PERMISSION_PREVIEW) \
and perm != FileShare.PERM_VIEW_ONLY:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if repo_folder_permission in (PERMISSION_READ) \
and perm not in (FileShare.PERM_VIEW_DL, FileShare.PERM_VIEW_ONLY):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# can_upload requires rw repo permission
if perm == FileShare.PERM_VIEW_DL_UPLOAD and \
repo_folder_permission != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if path != '/':
s_type = 'd' if stat.S_ISDIR(dirent.mode) else 'f'
if s_type == 'f':
file_name = os.path.basename(path.rstrip('/'))
can_edit, error_msg = can_edit_file(file_name, dirent.size, repo)
if not can_edit and perm in (FileShare.PERM_EDIT_DL, FileShare.PERM_EDIT_ONLY):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
else:
s_type = 'd'
# create share link
org_id = request.user.org.org_id if is_org_context(request) else None
if s_type == 'f':
fs = FileShare.objects.get_file_link_by_path(username, repo_id, path)
if fs:
error_msg = _('Share link %s already exists.' % fs.token)
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
fs = FileShare.objects.create_file_link(username, repo_id, path,
password, expire_date,
permission=perm, org_id=org_id)
elif s_type == 'd':
fs = FileShare.objects.get_dir_link_by_path(username, repo_id, path)
if fs:
error_msg = _('Share link %s already exists.' % fs.token)
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
fs = FileShare.objects.create_dir_link(username, repo_id, path,
password, expire_date,
permission=perm, org_id=org_id)
user_scope = request.data.get('user_scope', '')
emails_list = []
if user_scope and user_scope in VALID_SHARE_LINK_SCOPE:
if user_scope == SCOPE_SPECIFIC_USERS:
emails = request.data.get('emails', [])
emails_to_add = []
for username in emails:
try:
User.objects.get(email=username)
except User.DoesNotExist:
continue
emails_to_add.append(username)
fs.authed_details = json.dumps(
{'authed_users': emails_to_add}
)
elif user_scope == SCOPE_SPECIFIC_EMAILS:
emails_str = request.data.get('emails', '')
emails_list = string2list(emails_str)
emails_list = [e for e in emails_list if is_valid_email(e)]
fs.authed_details = json.dumps(
{'authed_emails': emails_list}
)
fs.user_scope = user_scope
fs.save()
if emails_list:
shared_from = email2nickname(username)
send_share_link_emails(emails_list, fs, shared_from)
link_info = get_share_link_info(fs)
return Response(link_info)
def delete(self, request):
""" Delete share links.
Permission checking:
1. default(NOT guest) user;
2. link owner;
"""
token_list = request.data.get('tokens')
if not token_list:
error_msg = 'token invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
result = {}
result['failed'] = []
result['success'] = []
username = request.user.username
for token in token_list:
try:
fs = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
result['success'].append({
'token': token,
})
continue
has_published_library = False
if fs.path == '/':
try:
Wiki.objects.get(repo_id=fs.repo_id)
has_published_library = True
except Wiki.DoesNotExist:
pass
if not fs.is_owner(username):
result['failed'].append({
'token': token,
'error_msg': _('Permission denied.')
})
continue
if has_published_library:
result['failed'].append({
'token': token,
'error_msg': _('There is an associated published library.')
})
continue
try:
fs.delete()
result['success'].append({
'token': token,
})
except Exception as e:
logger.error(e)
result['failed'].append({
'token': token,
'error_msg': _('Internal Server Error')
})
continue
return Response(result)
class ShareLink(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, CanGenerateShareLink)
throttle_classes = (UserRateThrottle,)
def get(self, request, token):
""" Get a special share link info.
Permission checking:
1. default(NOT guest) user;
"""
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)
link_info = get_share_link_info(fs)
return Response(link_info)
def put(self, request, token):
""" Update share link's permission and expiration.
Permission checking:
share link creater
"""
# resource check
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)
repo_id = fs.repo_id
repo = seafile_api.get_repo(repo_id)
if not repo_id:
error_msg = 'Library %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if fs.path != '/':
dirent = seafile_api.get_dirent_by_path(repo_id, fs.path)
if not dirent:
error_msg = 'Dirent %s not found.' % repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
username = request.user.username
if not fs.is_owner(username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# get permission of origin repo/folder
if fs.s_type == 'd':
folder_path = normalize_dir_path(fs.path)
else:
file_path = normalize_file_path(fs.path)
folder_path = os.path.dirname(file_path)
username = request.user.username
repo_folder_permission = seafile_api.check_permission_by_path(repo_id,
folder_path,
username)
if not repo_folder_permission:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# argument check
permissions = request.data.get('permissions', '')
if permissions:
try:
perm = check_permissions_arg(request)
except Exception:
error_msg = 'permissions invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if repo_folder_permission in (PERMISSION_PREVIEW_EDIT, PERMISSION_PREVIEW) \
and perm != FileShare.PERM_VIEW_ONLY:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if repo_folder_permission in (PERMISSION_READ) \
and perm not in (FileShare.PERM_VIEW_DL, FileShare.PERM_VIEW_ONLY):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if fs.s_type == 'f':
file_name = os.path.basename(fs.path.rstrip('/'))
can_edit, error_msg = can_edit_file(file_name, dirent.size, repo)
if not can_edit and perm in (FileShare.PERM_EDIT_DL, FileShare.PERM_EDIT_ONLY):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# update share link permission
fs.permission = perm
fs.save()
expire_days = request.data.get('expire_days', '')
expiration_time = request.data.get('expiration_time', '')
if expire_days and expiration_time:
error_msg = 'Can not pass expire_days and expiration_time at the same time.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if expire_days:
try:
expire_days = int(expire_days)
except ValueError:
error_msg = 'expire_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if expire_days <= 0:
error_msg = 'expire_days invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_EXPIRE_DAYS_MIN > 0:
if expire_days < SHARE_LINK_EXPIRE_DAYS_MIN:
error_msg = _('Expire days should be greater or equal to %s') % \
SHARE_LINK_EXPIRE_DAYS_MIN
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_EXPIRE_DAYS_MAX > 0:
if expire_days > SHARE_LINK_EXPIRE_DAYS_MAX:
error_msg = _('Expire days should be less than or equal to %s') % \
SHARE_LINK_EXPIRE_DAYS_MAX
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
expire_date = timezone.now() + relativedelta(days=expire_days)
fs.expire_date = expire_date
fs.save()
if expiration_time:
try:
expire_date = dateutil.parser.isoparse(expiration_time)
except Exception as e:
logger.error(e)
error_msg = 'expiration_time invalid, should be iso format, for example: 2020-05-17T10:26:22+08:00'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
expire_date = expire_date.astimezone(get_current_timezone()).replace(tzinfo=None)
if SHARE_LINK_EXPIRE_DAYS_MIN > 0:
expire_date_min_limit = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_MIN)
expire_date_min_limit = expire_date_min_limit.replace(hour=0).replace(minute=0).replace(second=0)
if expire_date < expire_date_min_limit:
error_msg = _('Expiration time should be later than %s.') % \
expire_date_min_limit.strftime("%Y-%m-%d %H:%M:%S")
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_EXPIRE_DAYS_MAX > 0:
expire_date_max_limit = timezone.now() + relativedelta(days=SHARE_LINK_EXPIRE_DAYS_MAX)
expire_date_max_limit = expire_date_max_limit.replace(hour=23).replace(minute=59).replace(second=59)
if expire_date > expire_date_max_limit:
error_msg = _('Expiration time should be earlier than %s.') % \
expire_date_max_limit.strftime("%Y-%m-%d %H:%M:%S")
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
fs.expire_date = expire_date
fs.save()
user_scope = request.data.get('user_scope', '')
if user_scope and user_scope in VALID_SHARE_LINK_SCOPE:
fs.user_scope = user_scope
fs.authed_details = None
fs.save()
link_info = get_share_link_info(fs)
return Response(link_info)
def delete(self, request, token):
""" Delete share link.
Permission checking:
1. default(NOT guest) user;
2. link owner;
"""
try:
fs = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
return Response({'success': True})
has_published_library = False
if fs.path == '/':
try:
Wiki.objects.get(repo_id=fs.repo_id)
has_published_library = True
except Wiki.DoesNotExist:
pass
username = request.user.username
if not fs.is_owner(username):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if has_published_library:
error_msg = _('There is an associated published library.')
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
try:
fs.delete()
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({'success': True})
class ShareLinkOnlineOfficeLock(APIView):
permission_classes = (IsProVersion,)
throttle_classes = (UserRateThrottle,)
def put(self, request, token):
""" This api only used for refresh OnlineOffice lock
when user edit office file via share link.
Permission checking:
1, If enable SHARE_LINK_LOGIN_REQUIRED, user must have been authenticated.
2, Share link should have can_edit permission.
3, File must have been locked by OnlineOffice.
"""
expire = request.data.get('expire', 0)
try:
expire = int(expire)
except ValueError:
error_msg = 'expire invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if expire < 0:
error_msg = 'expire invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
if SHARE_LINK_LOGIN_REQUIRED and \
not request.user.is_authenticated:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
try:
share_link = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
error_msg = 'Share link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if share_link.is_expired():
error_msg = 'Share link %s is expired.' % token
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
shared_by = share_link.username
repo_id = share_link.repo_id
path = normalize_file_path(share_link.path)
parent_dir = os.path.dirname(path)
if seafile_api.check_permission_by_path(repo_id,
parent_dir,
shared_by) != PERMISSION_READ_WRITE:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
permissions = share_link.get_permissions()
can_edit = permissions['can_edit']
if not can_edit:
error_msg = 'Share link %s has no edit permission.' % token
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
locked_by_online_office = if_locked_by_online_office(repo_id, path)
if locked_by_online_office:
# refresh lock file
try:
seafile_api.refresh_file_lock(repo_id, path,
int(time.time()) + expire)
except SearpcError as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
else:
error_msg = _("You can not refresh this file's lock.")
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
return Response({'success': True})
class ShareLinkDirents(APIView):
throttle_classes = (UserRateThrottle,)
def get(self, request, token):
""" Only used for get dirents in a folder share link.
Permission checking:
1, If enable SHARE_LINK_LOGIN_REQUIRED, user must have been authenticated.
2, If enable SHARE_LINK_SCOPE, user must have been authenticated by specific scope.
3, If share link is encrypted, share link password must have been checked.
"""
# argument check
thumbnail_size = request.GET.get('thumbnail_size', THUMBNAIL_DEFAULT_SIZE)
try:
thumbnail_size = int(thumbnail_size)
except ValueError:
error_msg = 'thumbnail_size invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# permission check
# check if login required
if SHARE_LINK_LOGIN_REQUIRED and \
not request.user.is_authenticated:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# resource check
try:
share_link = FileShare.objects.get_valid_dir_link_by_token(token=token)
except FileShare.DoesNotExist:
error_msg = 'Share link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# check share link password
if share_link.is_encrypted() and not check_share_link_access(request, token):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not check_share_link_access_by_scope(request, share_link):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if share_link.s_type != 'd':
error_msg = 'Share link %s is not a folder share link.' % token
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repo_id = share_link.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)
share_link_path = share_link.path
request_path = request.GET.get('path', '/')
if request_path == '/':
path = share_link_path
else:
path = posixpath.join(share_link_path, request_path.strip('/'))
path = normalize_dir_path(path)
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
if not dir_id:
error_msg = 'Folder %s not found.' % request_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
try:
current_commit = seafile_api.get_commit_list(repo_id, 0, 1)[0]
dirent_list = seafile_api.list_dir_by_commit_and_path(repo_id,
current_commit.id,
path, -1, -1)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
try:
files_tags_in_dir = get_files_tags_in_dir(repo_id, path)
except Exception as e:
logger.error(e)
files_tags_in_dir = {}
result = []
for dirent in dirent_list:
# don't return parent folder(share link path) info to user
# so use request_path here
dirent_path = posixpath.join(request_path, dirent.obj_name)
dirent_info = {}
dirent_info['size'] = dirent.size
dirent_info['last_modified'] = timestamp_to_isoformat_timestr(dirent.mtime)
if stat.S_ISDIR(dirent.mode):
dirent_info['is_dir'] = True
dirent_info['folder_path'] = normalize_dir_path(dirent_path)
dirent_info['folder_name'] = dirent.obj_name
else:
dirent_info['is_dir'] = False
dirent_info['file_path'] = normalize_file_path(dirent_path)
dirent_info['file_name'] = dirent.obj_name
file_type, file_ext = get_file_type_and_ext(dirent.obj_name)
if file_type in (IMAGE, XMIND) or \
(file_type == VIDEO and ENABLE_VIDEO_THUMBNAIL):
if os.path.exists(os.path.join(THUMBNAIL_ROOT, str(thumbnail_size), dirent.obj_id)):
req_image_path = posixpath.join(request_path, dirent.obj_name)
src = get_share_link_thumbnail_src(token, thumbnail_size, req_image_path)
dirent_info['encoded_thumbnail_src'] = quote(src)
# get tag info
file_tags = files_tags_in_dir.get(dirent.obj_name, [])
if file_tags:
dirent_info['file_tags'] = []
for file_tag in file_tags:
dirent_info['file_tags'].append(file_tag)
result.append(dirent_info)
return Response({'dirent_list': result})
class ShareLinkUpload(APIView):
throttle_classes = (UserRateThrottle, )
def get(self, request, token):
""" Only used for get seafhttp upload link for a folder share link.
Permission checking:
1, If enable SHARE_LINK_LOGIN_REQUIRED, user must have been authenticated.
2, If enable SHARE_LINK_SCOPE, user must have been authenticated by specific scope.
3, If share link is encrypted, share link password must have been checked.
4, Share link must be a folder share link and has can_upload permission.
"""
# check if login required
if SHARE_LINK_LOGIN_REQUIRED and \
not request.user.is_authenticated:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# resource check
try:
share_link = FileShare.objects.get_valid_dir_link_by_token(token=token)
except FileShare.DoesNotExist:
error_msg = 'Share link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not share_link.is_dir_share_link():
error_msg = 'Share link %s is not a folder share link.' % token
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
repo_id = share_link.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)
share_link_path = share_link.path
request_path = request.GET.get('path', '/')
if request_path == '/':
path = share_link_path
else:
path = posixpath.join(share_link_path, request_path.strip('/'))
path = normalize_dir_path(path)
dir_id = seafile_api.get_dir_id_by_path(repo_id, path)
if not dir_id:
error_msg = 'Folder %s not found.' % request_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if share_link.is_encrypted() and not check_share_link_access(request, token):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not check_share_link_access_by_scope(request, share_link):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not share_link.get_permissions()['can_upload']:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# generate token
obj_id = json.dumps({'parent_dir': path})
check_virus = False
if is_pro_version() and ENABLE_UPLOAD_LINK_VIRUS_CHECK:
check_virus = True
if check_virus:
token = seafile_api.get_fileserver_access_token(repo_id,
obj_id,
'upload-link',
share_link.username,
use_onetime=False,
check_virus=check_virus)
else:
token = seafile_api.get_fileserver_access_token(repo_id,
obj_id,
'upload-link',
share_link.username,
use_onetime=False)
if not token:
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
result = {}
result['upload_link'] = gen_file_upload_url(token, 'upload-api')
return Response(result)
class ShareLinkUploadDone(APIView):
throttle_classes = (UserRateThrottle, )
def post(self, request, token):
""" Only used for saving notification after user upload file via folder share link and upload link.
Permission checking:
1, If enable SHARE_LINK_LOGIN_REQUIRED, user must have been authenticated.
2, If enable SHARE_LINK_SCOPE, user must have been authenticated by specific scope.
3, If share link is encrypted, share link password must have been checked.
4, Share link must be a folder share link and has can_upload permission.
"""
# resource check
share_link = None
upload_link = None
try:
share_link = FileShare.objects.get_valid_dir_link_by_token(token=token)
except FileShare.DoesNotExist:
upload_link = UploadLinkShare.objects.get(token=token)
except UploadLinkShare.DoesNotExist:
error_msg = 'token %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if share_link:
# check if login required
if SHARE_LINK_LOGIN_REQUIRED and \
not request.user.is_authenticated:
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# check share link validation
if share_link.is_encrypted() and not check_share_link_access(request, token):
error_msg = 'Share link is encrypted.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not check_share_link_access_by_scope(request, share_link):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not share_link.get_permissions()['can_upload']:
error_msg = 'Share link has no can_upload permission'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if share_link.is_expired():
error_msg = 'Share link is expired'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if not share_link.is_dir_share_link():
error_msg = 'Share link %s is not a folder share link.' % token
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# recourse check
repo_id = share_link.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)
parent_dir = share_link.path
link_owner = share_link.username
if seafile_api.check_permission_by_path(repo_id,
parent_dir,
link_owner) != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if upload_link:
if upload_link.is_encrypted() and not \
check_share_link_access(request, token, is_upload_link=True):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if upload_link.is_expired():
error_msg = 'Upload link is expired'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
repo_id = upload_link.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)
parent_dir = upload_link.path
link_owner = upload_link.username
if seafile_api.check_permission_by_path(repo_id,
parent_dir,
link_owner) != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# send signal
dirent_path = request.data.get('file_path')
if not dirent_path:
error_msg = 'file_path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
is_dir = request.data.get('is_dir', 'false')
if is_dir.lower() == 'true':
dir_id = seafile_api.get_dir_id_by_path(repo_id, dirent_path)
if not dir_id:
error_msg = 'Folder %s not found.' % dirent_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# send singal
upload_folder_successful.send(sender=None,
repo_id=repo_id,
folder_path=dirent_path,
owner=link_owner)
else:
file_id = seafile_api.get_file_id_by_path(repo_id, dirent_path)
if not file_id:
error_msg = 'File %s not found.' % dirent_path
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# send singal
upload_file_successful.send(sender=None,
repo_id=repo_id,
file_path=dirent_path,
owner=link_owner)
return Response({'success': True})
class ShareLinkSaveFileToRepo(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
def post(self, request, token):
# argument check
dst_repo_id = request.POST.get('dst_repo_id', '')
if not dst_repo_id:
error_msg = 'dst_repo_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
dst_parent_dir = request.POST.get('dst_parent_dir', '')
if not dst_parent_dir:
error_msg = 'dst_parent_dir invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check
try:
share_link = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
error_msg = 'Share link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_repo(dst_repo_id):
error_msg = 'Library %s not found.' % dst_repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_dir_id_by_path(dst_repo_id, dst_parent_dir):
error_msg = 'Folder %s not found.' % dst_parent_dir
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
share_link_permission = share_link.get_permissions()
if not share_link_permission.get('can_download', False):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if check_folder_permission(request,
dst_repo_id,
dst_parent_dir) != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# copy file
if share_link.s_type == 'f':
src_dirent_path = share_link.path
else:
path = request.POST.get('path', '')
if not path:
error_msg = 'path invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
src_dirent_path = posixpath.join(share_link.path,
path.strip('/'))
src_repo_id = share_link.repo_id
src_parent_dir = os.path.dirname(src_dirent_path)
src_dirent_name = os.path.basename(src_dirent_path)
dst_dirent_name = check_filename_with_rename(dst_repo_id,
dst_parent_dir,
src_dirent_name)
username = request.user.username
seafile_api.copy_file(src_repo_id, src_parent_dir,
json.dumps([src_dirent_name]),
dst_repo_id, dst_parent_dir,
json.dumps([dst_dirent_name]),
username, need_progress=0)
return Response({'success': True})
class ShareLinkSaveItemsToRepo(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated,)
throttle_classes = (UserRateThrottle,)
def post(self, request, token):
# argument check
dst_repo_id = request.POST.get('dst_repo_id', '')
if not dst_repo_id:
error_msg = 'dst_repo_id invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
dst_parent_dir = request.POST.get('dst_parent_dir', '')
if not dst_parent_dir:
error_msg = 'dst_parent_dir invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
# resource check
try:
share_link = FileShare.objects.get(token=token)
except FileShare.DoesNotExist:
error_msg = 'Share link %s not found.' % token
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_repo(dst_repo_id):
error_msg = 'Library %s not found.' % dst_repo_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
if not seafile_api.get_dir_id_by_path(dst_repo_id, dst_parent_dir):
error_msg = 'Folder %s not found.' % dst_parent_dir
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
# permission check
share_link_permission = share_link.get_permissions()
if not share_link_permission.get('can_download', False):
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
if check_folder_permission(request,
dst_repo_id,
dst_parent_dir) != 'rw':
error_msg = 'Permission denied.'
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
# save items
username = request.user.username
src_repo_id = share_link.repo_id
# save file in file share link
if share_link.s_type == 'f':
src_dirent_path = share_link.path
src_parent_dir = os.path.dirname(src_dirent_path)
src_dirent_name = os.path.basename(src_dirent_path)
check_filename_with_rename(dst_repo_id,
dst_parent_dir,
src_dirent_name)
else:
# save items in folder share link
src_parent_dir = request.POST.get('src_parent_dir', '')
if not src_parent_dir:
error_msg = 'src_parent_dir invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
src_dirents = request.POST.getlist('src_dirents', [])
if not src_dirents:
error_msg = 'src_dirents invalid.'
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
src_parent_dir = posixpath.join(share_link.path, src_parent_dir.strip('/'))
try:
res = seafile_api.copy_file(src_repo_id, src_parent_dir,
json.dumps(src_dirents),
dst_repo_id, dst_parent_dir,
json.dumps(src_dirents),
username, need_progress=1, synchronous=0)
except Exception as e:
logger.error(e)
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
if not res:
error_msg = 'Internal Server Error'
return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
result = {}
if res.background:
result['task_id'] = res.task_id
return Response(result)
class ShareLinkRepoTags(APIView):
throttle_classes = (UserRateThrottle,)
def get(self, request, token):
"""get all repo_tags by share link token.
"""
try:
share_link = 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)
repo_id = share_link.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)
# get all tags in repo
repo_tags = RepoTags.objects.filter(repo_id=repo_id)
# get tagged files by tag id
tag_id_file_list_dict = defaultdict(list)
for repo_tag in repo_tags:
tagged_files = get_tagged_files(repo, repo_tag.pk)['tagged_files']
tagged_files = [
item for item in tagged_files
if item.get('parent_path') and item.get('parent_path').startswith(share_link.path.rstrip('/'))
]
tag_id_file_list_dict[repo_tag.pk] = tagged_files
# generate response
result = {
"repo_tags": []
}
for repo_tag in repo_tags:
repo_tag_info = repo_tag.to_dict()
repo_tag_id = repo_tag_info["repo_tag_id"]
repo_tag_info["files_count"] = len(tag_id_file_list_dict.get(repo_tag_id, []))
result['repo_tags'].append(repo_tag_info)
return Response(result)
class ShareLinkRepoTagsTaggedFiles(APIView):
throttle_classes = (UserRateThrottle,)
def get(self, request, token, tag_id):
"""get tagged files by share link token and tag id.
"""
try:
share_link = 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)
repo_id = share_link.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)
repo_tag = RepoTags.objects.get_repo_tag_by_id(tag_id)
if not repo_tag:
error_msg = 'repo_tag %s not found.' % tag_id
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
share_link_path = share_link.path.rstrip('/') if share_link.path != '/' else '/'
filtered_tagged_files = []
tagged_files = get_tagged_files(repo, tag_id)
for tagged_file in tagged_files.get('tagged_files', []):
if tagged_file.get('file_deleted', False):
continue
tagged_file_parent_path = tagged_file.get('parent_path', '')
if share_link_path == '/':
filtered_tagged_files.append(tagged_file)
elif tagged_file_parent_path.startswith(share_link_path):
tagged_file['parent_path'] = '/' + tagged_file_parent_path.lstrip(share_link_path)
filtered_tagged_files.append(tagged_file)
return Response({'tagged_files': filtered_tagged_files})
class ShareLinksCleanInvalid(APIView):
authentication_classes = (TokenAuthentication, SessionAuthentication)
permission_classes = (IsAuthenticated, CanGenerateShareLink)
throttle_classes = (UserRateThrottle,)
def delete(self, request):
""" Clean invalid share links.
"""
username = request.user.username
share_links = FileShare.objects.filter(username=username)
for share_link in share_links.iterator(chunk_size=1000):
if share_link.is_expired():
share_link.delete()
continue
repo_id = share_link.repo_id
if not seafile_api.get_repo(repo_id):
share_link.delete()
continue
if share_link.s_type == 'd':
folder_path = normalize_dir_path(share_link.path)
obj_id = seafile_api.get_dir_id_by_path(repo_id, folder_path)
else:
file_path = normalize_file_path(share_link.path)
obj_id = seafile_api.get_file_id_by_path(repo_id, file_path)
if not obj_id:
share_link.delete()
continue
return Response({'success': True})