1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-28 03:31:23 +00:00
seahub/seahub/views/file.py
2023-11-01 17:05:28 +08:00

2254 lines
82 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Copyright (c) 2012-2016 Seafile Ltd.
# -*- coding: utf-8 -*-
"""
File related views, including view_file, view_history_file, view_trash_file,
view_snapshot_file, view_shared_file, etc.
"""
import os
import json
import time
import uuid
import stat
import urllib.request
import urllib.error
import urllib.parse
import chardet
import logging
import posixpath
import re
import mimetypes
from django.core.cache import cache
from django.contrib.sites.shortcuts import get_current_site
from django.contrib import messages
from django.urls import reverse
from django.db.models import F
from django.http import Http404, HttpResponseRedirect, HttpResponseBadRequest, HttpResponseForbidden
from django.shortcuts import render
from urllib.parse import quote
from django.utils.translation import get_language, gettext as _
from django.views.decorators.csrf import ensure_csrf_cookie
from django.template.defaultfilters import filesizeformat
from seaserv import seafile_api, ccnet_api
from seaserv import get_repo, get_commits, \
get_file_id_by_path, get_commit, get_file_size, \
seafserv_threaded_rpc
from seahub.settings import SITE_ROOT
from seahub.tags.models import FileUUIDMap
from seahub.wopi.utils import get_wopi_dict
from seahub.onlyoffice.utils import get_onlyoffice_dict
from seahub.auth.decorators import login_required
from seahub.base.decorators import repo_passwd_set_required
from seahub.base.accounts import ANONYMOUS_EMAIL
from seahub.base.templatetags.seahub_tags import file_icon_filter
from seahub.share.models import FileShare, check_share_link_common
from seahub.share.decorators import share_link_audit, share_link_login_required
from seahub.wiki.utils import get_wiki_dirent
from seahub.wiki.models import Wiki, WikiDoesNotExist, WikiPageMissing
from seahub.utils import render_error, is_org_context, \
get_file_type_and_ext, gen_file_get_url, gen_file_share_link, \
render_permission_error, is_pro_version, is_textual_file, \
EMPTY_SHA1, HtmlDiff, gen_inner_file_get_url, \
get_file_audit_events_by_path, \
generate_file_audit_event_type, FILE_AUDIT_ENABLED, \
get_conf_text_ext, HAS_OFFICE_CONVERTER, PREVIEW_FILEEXT, \
normalize_file_path, get_service_url, OFFICE_PREVIEW_MAX_SIZE, \
normalize_cache_key
from seahub.utils.ip import get_remote_ip
from seahub.utils.timeutils import utc_to_local
from seahub.utils.file_types import (IMAGE, PDF, SVG,
DOCUMENT, SPREADSHEET, AUDIO,
MARKDOWN, TEXT, VIDEO, XMIND, SEADOC)
from seahub.utils.star import is_file_starred
from seahub.utils.http import json_response, \
BadRequestException
from seahub.utils.file_op import check_file_lock, \
ONLINE_OFFICE_LOCK_OWNER, if_locked_by_online_office
from seahub.views import check_folder_permission, \
get_unencry_rw_repos_by_user
from seahub.utils.repo import is_repo_owner, parse_repo_perm
from seahub.group.utils import is_group_member
from seahub.thumbnail.utils import extract_xmind_image, get_thumbnail_src, \
XMIND_IMAGE_SIZE, get_share_link_thumbnail_src, get_thumbnail_image_path
from seahub.drafts.utils import get_file_draft, \
is_draft_file, has_draft_file
from seahub.seadoc.utils import get_seadoc_file_uuid, gen_seadoc_access_token, is_seadoc_revision
from seahub.seadoc.models import SeadocDraft, SeadocRevision
if HAS_OFFICE_CONVERTER:
from seahub.utils import (
query_office_convert_status, get_office_converted_page,
prepare_converted_html,
)
import seahub.settings as settings
from seahub.settings import FILE_ENCODING_LIST, FILE_PREVIEW_MAX_SIZE, \
FILE_ENCODING_TRY_LIST, MEDIA_URL, SEAFILE_COLLAB_SERVER, ENABLE_WATERMARK, \
SHARE_LINK_EXPIRE_DAYS_MIN, SHARE_LINK_EXPIRE_DAYS_MAX, SHARE_LINK_PASSWORD_MIN_LENGTH, \
SHARE_LINK_FORCE_USE_PASSWORD, SHARE_LINK_PASSWORD_STRENGTH_LEVEL, \
SHARE_LINK_EXPIRE_DAYS_DEFAULT, ENABLE_SHARE_LINK_REPORT_ABUSE, SEADOC_SERVER_URL
# wopi
try:
from seahub.settings import ENABLE_OFFICE_WEB_APP
except ImportError:
ENABLE_OFFICE_WEB_APP = False
try:
from seahub.settings import ENABLE_OFFICE_WEB_APP_EDIT
except ImportError:
ENABLE_OFFICE_WEB_APP_EDIT = False
try:
from seahub.settings import OFFICE_WEB_APP_FILE_EXTENSION
except ImportError:
OFFICE_WEB_APP_FILE_EXTENSION = ()
try:
from seahub.settings import OFFICE_WEB_APP_EDIT_FILE_EXTENSION
except ImportError:
OFFICE_WEB_APP_EDIT_FILE_EXTENSION = ()
# onlyoffice
try:
from seahub.settings import ENABLE_ONLYOFFICE
except ImportError:
ENABLE_ONLYOFFICE = False
try:
from seahub.onlyoffice.settings import ONLYOFFICE_FILE_EXTENSION
except ImportError:
ONLYOFFICE_FILE_EXTENSION = ()
try:
from seahub.onlyoffice.settings import ONLYOFFICE_EDIT_FILE_EXTENSION
except ImportError:
ONLYOFFICE_EDIT_FILE_EXTENSION = ()
# bisheng office
from seahub.bisheng_office.utils import get_bisheng_dict, \
get_bisheng_editor_url, get_bisheng_preivew_url
from seahub.bisheng_office.settings import ENABLE_BISHENG_OFFICE
from seahub.bisheng_office.settings import BISHENG_OFFICE_FILE_EXTENSION
from seahub.thirdparty_editor.settings import ENABLE_THIRDPARTY_EDITOR
from seahub.thirdparty_editor.settings import THIRDPARTY_EDITOR_ACTION_URL_DICT
from seahub.thirdparty_editor.settings import THIRDPARTY_EDITOR_ACCESS_TOKEN_EXPIRATION
# Get an instance of a logger
logger = logging.getLogger(__name__)
def gen_path_link(path, repo_name):
"""
Generate navigate paths and links in repo page.
"""
if path and path[-1] != '/':
path += '/'
paths = []
links = []
if path and path != '/':
paths = path[1:-1].split('/')
i = 1
for name in paths:
link = '/' + '/'.join(paths[:i])
i = i + 1
links.append(link)
if repo_name:
paths.insert(0, repo_name)
links.insert(0, '/')
zipped = list(zip(paths, links))
return zipped
def get_file_content(file_type, raw_path, file_enc):
"""Get textual file content, including txt/markdown/seaf.
"""
return repo_file_get(raw_path, file_enc) if is_textual_file(
file_type=file_type) else ('', '', '')
def repo_file_get(raw_path, file_enc):
"""
Get file content and encoding.
"""
err = ''
file_content = ''
encoding = None
if file_enc != 'auto':
encoding = file_enc
try:
file_response = urllib.request.urlopen(raw_path)
content = file_response.read()
except urllib.error.HTTPError as e:
logger.error(e)
err = _('HTTPError: failed to open file online')
return err, '', None
except urllib.error.URLError as e:
logger.error(e)
err = _('URLError: failed to open file online')
return err, '', None
else:
if encoding:
try:
u_content = content.decode(encoding)
except UnicodeDecodeError:
err = _('The encoding you chose is not proper.')
return err, '', encoding
else:
for enc in FILE_ENCODING_TRY_LIST:
try:
u_content = content.decode(enc)
encoding = enc
break
except UnicodeDecodeError:
if enc != FILE_ENCODING_TRY_LIST[-1]:
continue
else:
encoding = chardet.detect(content)['encoding']
if encoding:
try:
u_content = content.decode(encoding)
except UnicodeDecodeError:
err = _('Unknown file encoding')
return err, '', ''
else:
err = _('Unknown file encoding')
return err, '', ''
file_content = u_content
return err, file_content, encoding
def get_file_view_path_and_perm(request, repo_id, obj_id, path,
use_onetime=settings.FILESERVER_TOKEN_ONCE_ONLY):
""" Get path and the permission to view file.
Returns:
outer fileserver file url, inner fileserver file url, permission
"""
username = request.user.username
filename = os.path.basename(path)
user_perm = check_folder_permission(request, repo_id, '/')
if user_perm is None:
return ('', '', user_perm)
else:
# Get a token to visit file
token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'view', username, use_onetime=use_onetime)
if not token:
return ('', '', None)
outer_url = gen_file_get_url(token, filename)
inner_url = gen_inner_file_get_url(token, filename)
return (outer_url, inner_url, user_perm)
def handle_textual_file(request, filetype, raw_path, ret_dict):
# encoding option a user chose
file_enc = request.GET.get('file_enc', 'auto')
if not file_enc in FILE_ENCODING_LIST:
file_enc = 'auto'
err, file_content, encoding = get_file_content(filetype,
raw_path, file_enc)
file_encoding_list = FILE_ENCODING_LIST
if encoding and encoding not in FILE_ENCODING_LIST:
file_encoding_list.append(encoding)
# populate return value dict
ret_dict['err'] = err
ret_dict['file_content'] = file_content
ret_dict['encoding'] = encoding
ret_dict['file_enc'] = file_enc
ret_dict['file_encoding_list'] = file_encoding_list
def handle_document(raw_path, obj_id, fileext, ret_dict):
if HAS_OFFICE_CONVERTER:
err = prepare_converted_html(raw_path, obj_id, fileext, ret_dict)
# populate return value dict
ret_dict['err'] = err
else:
ret_dict['filetype'] = 'Unknown'
def handle_spreadsheet(raw_path, obj_id, fileext, ret_dict):
handle_document(raw_path, obj_id, fileext, ret_dict)
def convert_md_link(file_content, repo_id, username):
def repl(matchobj):
if matchobj.group(2): # return origin string in backquotes
return matchobj.group(2)
link_alias = link_name = matchobj.group(1).strip()
if len(link_name.split('|')) > 1:
link_alias = link_name.split('|')[0]
link_name = link_name.split('|')[1]
filetype, fileext = get_file_type_and_ext(link_name)
if fileext == '':
# convert link_name that extension is missing to a markdown page
try:
dirent = get_wiki_dirent(repo_id, link_name)
path = "/" + dirent.obj_name
href = reverse('view_lib_file', args=[repo_id, path])
a_tag = '''<a href="%s">%s</a>'''
return a_tag % (href, link_alias)
except (WikiDoesNotExist, WikiPageMissing):
a_tag = '''<p class="wiki-page-missing">%s</p>'''
return a_tag % (link_alias)
elif filetype == IMAGE:
# load image to current page
path = "/" + link_name
filename = os.path.basename(path)
obj_id = get_file_id_by_path(repo_id, path)
if not obj_id:
return '''<p class="wiki-page-missing">%s</p>''' % link_name
token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'view', username)
if not token:
return '''<p class="wiki-page-missing">%s</p>''' % link_name
return '<img class="wiki-image" src="%s" alt="%s" />' % (gen_file_get_url(token, filename), filename)
else:
# convert other types of filelinks to clickable links
path = "/" + link_name
icon = file_icon_filter(link_name)
s = reverse('view_lib_file', args=[repo_id, path])
a_tag = '''<img src="%simg/file/%s" alt="%s" class="vam" /> <a href="%s" target="_blank" class="vam">%s</a>'''
return a_tag % (MEDIA_URL, icon, icon, s, link_name)
return re.sub(r'\[\[(.+?)\]\]|(`.+?`)', repl, file_content)
def can_preview_file(file_name, file_size, repo):
"""Check whether Seafile supports view file.
Returns (True, None) if Yes, otherwise (False, error_msg).
"""
filetype, fileext = get_file_type_and_ext(file_name)
# Seafile defines 10 kinds of filetype:
# TEXT, MARKDOWN, IMAGE, DOCUMENT, SPREADSHEET, VIDEO, AUDIO, PDF, SVG
if filetype in (TEXT, MARKDOWN, IMAGE) or fileext in get_conf_text_ext():
if file_size > FILE_PREVIEW_MAX_SIZE:
error_msg = _('File size surpasses %s, can not be opened online.') % \
filesizeformat(FILE_PREVIEW_MAX_SIZE)
return False, error_msg
elif filetype in (DOCUMENT, SPREADSHEET):
if repo.encrypted:
error_msg = _('The library is encrypted, can not open file online.')
return False, error_msg
if not HAS_OFFICE_CONVERTER and \
not ENABLE_OFFICE_WEB_APP and \
not ENABLE_ONLYOFFICE:
error_msg = "File preview unsupported"
return False, error_msg
# priority of view office file is:
# OOS > OnlyOffice > Seafile integrated
if ENABLE_OFFICE_WEB_APP:
if fileext not in OFFICE_WEB_APP_FILE_EXTENSION:
error_msg = "File preview unsupported"
return False, error_msg
elif ENABLE_ONLYOFFICE:
if fileext not in ONLYOFFICE_FILE_EXTENSION:
error_msg = "File preview unsupported"
return False, error_msg
else:
if not HAS_OFFICE_CONVERTER:
error_msg = "File preview unsupported"
return False, error_msg
# HAS_OFFICE_CONVERTER
if file_size > OFFICE_PREVIEW_MAX_SIZE:
error_msg = _('File size surpasses %s, can not be opened online.') % \
filesizeformat(OFFICE_PREVIEW_MAX_SIZE)
return False, error_msg
else:
# NOT depends on Seafile settings
if filetype not in list(PREVIEW_FILEEXT.keys()):
error_msg = "File preview unsupported"
return False, error_msg
return True, ''
def can_edit_file(file_name, file_size, repo):
"""Check whether Seafile supports edit file.
Returns (True, None) if Yes, otherwise (False, error_msg).
"""
can_preview, err_msg = can_preview_file(file_name, file_size, repo)
if not can_preview:
return False, err_msg
file_type, file_ext = get_file_type_and_ext(file_name)
if file_type in (TEXT, MARKDOWN) or file_ext in get_conf_text_ext():
return True, ''
if file_type in (DOCUMENT, SPREADSHEET):
if ENABLE_OFFICE_WEB_APP_EDIT and \
file_ext in OFFICE_WEB_APP_EDIT_FILE_EXTENSION:
return True, ''
if ENABLE_ONLYOFFICE and file_ext in ONLYOFFICE_EDIT_FILE_EXTENSION:
return True, ''
return False, 'File edit unsupported'
@login_required
def view_lib_file_via_smart_link(request, dirent_uuid, dirent_name):
uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(dirent_uuid)
if not uuid_map:
raise Http404
repo_id = uuid_map.repo_id
parent_path = uuid_map.parent_path
dirent_name_from_uuid_map = uuid_map.filename
is_dir = uuid_map.is_dir
repo = seafile_api.get_repo(repo_id)
if not repo:
raise Http404
dirent_path = posixpath.join(parent_path, dirent_name_from_uuid_map.strip('/'))
if not is_dir:
redirect_to = reverse('view_lib_file', args=[repo_id, dirent_path])
if request.GET.get('dl', '') == '1':
redirect_to = redirect_to + '?dl=1'
else:
redirect_to = reverse('lib_view', args=[repo_id, repo.name, dirent_path.strip('/')])
return HttpResponseRedirect(redirect_to)
def convert_repo_path_when_can_not_view_file(request, repo_id, path):
path = normalize_file_path(path)
username = request.user.username
converted_repo_path = seafile_api.convert_repo_path(repo_id, path, username)
if not converted_repo_path:
return render_permission_error(request, _('Unable to view file'))
converted_repo_path = json.loads(converted_repo_path)
repo_id = converted_repo_path['repo_id']
repo = seafile_api.get_repo(repo_id)
if not repo:
raise Http404
path = converted_repo_path['path']
path = normalize_file_path(path)
file_id = seafile_api.get_file_id_by_path(repo_id, path)
if not file_id:
return render_error(request, _('File does not exist'))
group_id = ''
if 'group_id' in converted_repo_path:
group_id = converted_repo_path['group_id']
if not ccnet_api.get_group(group_id):
return render_error(request, _('Group does not exist'))
if not is_group_member(group_id, username):
return render_permission_error(request, _('Unable to view file'))
parent_dir = os.path.dirname(path)
permission = check_folder_permission(request, repo_id, parent_dir)
if not permission:
return render_permission_error(request, _('Unable to view file'))
next_url = reverse('view_lib_file', args=[repo_id, path])
return HttpResponseRedirect(next_url)
@login_required
@repo_passwd_set_required
def view_lib_file(request, repo_id, path):
# resource check
repo = seafile_api.get_repo(repo_id)
if not repo:
raise Http404
path = normalize_file_path(path)
file_id = seafile_api.get_file_id_by_path(repo_id, path)
if not file_id:
return render_error(request, _('File does not exist'))
# permission check
username = request.user.username
parent_dir = os.path.dirname(path)
permission = check_folder_permission(request, repo_id, parent_dir)
if not permission:
return convert_repo_path_when_can_not_view_file(request, repo_id, path)
# download file or view raw file
filename = os.path.basename(path)
dl = request.GET.get('dl', '0') == '1'
raw = request.GET.get('raw', '0') == '1'
if dl or raw:
if parse_repo_perm(permission).can_download is False:
raise Http404
operation = 'download' if dl else 'view'
token = seafile_api.get_fileserver_access_token(
repo_id, file_id, operation, username,
use_onetime=settings.FILESERVER_TOKEN_ONCE_ONLY)
if not token:
return render_permission_error(request, _('Unable to view file'))
dl_or_raw_url = gen_file_get_url(token, filename)
# send stats message
send_file_access_msg(request, repo, path, 'web')
return HttpResponseRedirect(dl_or_raw_url)
if ENABLE_THIRDPARTY_EDITOR:
filename = os.path.basename(path)
filetype, fileext = get_file_type_and_ext(filename)
action_url = THIRDPARTY_EDITOR_ACTION_URL_DICT.get(fileext, '')
if action_url:
user_repo_path_info = {
'request_user': request.user.username,
'repo_id': repo_id,
'file_path': path,
'permission': {
# Only can preview file for now
'can_edit': False
}
}
uid = uuid.uuid4()
access_token = uid.hex
cache.set('thirdparty_editor_access_token_' + access_token,
user_repo_path_info,
THIRDPARTY_EDITOR_ACCESS_TOKEN_EXPIRATION)
editor_dict = {}
editor_dict['action_url'] = action_url
editor_dict['access_token'] = access_token
editor_dict['access_token_ttl'] = int(time.time()) + THIRDPARTY_EDITOR_ACCESS_TOKEN_EXPIRATION
return render(request, 'view_file_thirdparty_editor.html', editor_dict)
org_id = request.user.org.org_id if is_org_context(request) else -1
# basic file info
return_dict = {
'is_pro': is_pro_version(),
'repo': repo,
'file_id': file_id,
'last_commit_id': repo.head_cmmt_id,
'is_repo_owner': is_repo_owner(request, repo_id, username),
'path': path,
'parent_dir': parent_dir,
'filename': filename,
'file_perm': permission,
'highlight_keyword': settings.HIGHLIGHT_KEYWORD,
'enable_file_comment': settings.ENABLE_FILE_COMMENT,
'enable_watermark': ENABLE_WATERMARK,
'share_link_force_use_password': SHARE_LINK_FORCE_USE_PASSWORD,
'share_link_password_min_length': SHARE_LINK_PASSWORD_MIN_LENGTH,
'share_link_password_strength_level': SHARE_LINK_PASSWORD_STRENGTH_LEVEL,
'share_link_expire_days_default': SHARE_LINK_EXPIRE_DAYS_DEFAULT,
'share_link_expire_days_min': SHARE_LINK_EXPIRE_DAYS_MIN,
'share_link_expire_days_max': SHARE_LINK_EXPIRE_DAYS_MAX,
'can_download_file': parse_repo_perm(permission).can_download,
'seafile_collab_server': SEAFILE_COLLAB_SERVER,
}
# check whether file is starred
is_starred = is_file_starred(username, repo_id, path, org_id)
return_dict['is_starred'] = is_starred
# check file lock info
try:
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
except Exception as e:
logger.error(e)
is_locked = False
locked_by_me = False
locked_by_online_office = if_locked_by_online_office(repo_id, path)
if is_pro_version() and permission == 'rw':
can_lock_unlock_file = True
else:
can_lock_unlock_file = False
return_dict['file_locked'] = is_locked
return_dict['locked_by_me'] = locked_by_me
return_dict['can_lock_unlock_file'] = can_lock_unlock_file
# file shared link
l = FileShare.objects.filter(repo_id=repo_id).filter(
username=username).filter(path=path)
fileshare = l[0] if len(l) > 0 else None
file_shared_link = gen_file_share_link(fileshare.token) if fileshare else ''
return_dict['fileshare'] = fileshare,
return_dict['file_shared_link'] = file_shared_link
if parse_repo_perm(permission).can_download and \
request.user.permissions.can_generate_share_link():
return_dict['can_share_file'] = True
else:
return_dict['can_share_file'] = False
# fetch file contributors and latest contributor
try:
# get real path for sub repo
real_path = repo.origin_path + path if repo.origin_path else path
dirent = seafile_api.get_dirent_by_path(repo.store_id, real_path)
if dirent:
latest_contributor, last_modified = dirent.modifier, dirent.mtime
else:
latest_contributor, last_modified = None, 0
except Exception as e:
logger.error(e)
latest_contributor, last_modified = None, 0
return_dict['latest_contributor'] = latest_contributor
return_dict['last_modified'] = last_modified
# get file type and extention
filetype, fileext = get_file_type_and_ext(filename)
return_dict['fileext'] = fileext
return_dict['filetype'] = filetype
# get file raw url
raw_path = ''
inner_path = ''
use_onetime = False if filetype in (VIDEO, AUDIO) else True
token = seafile_api.get_fileserver_access_token(repo_id,
file_id, 'view', username, use_onetime=use_onetime)
if token:
raw_path = gen_file_get_url(token, filename)
inner_path = gen_inner_file_get_url(token, filename)
# handle file preview/edit according to file extention
file_size = seafile_api.get_file_size(repo.store_id, repo.version, file_id)
# template = 'view_file_%s.html' % filetype.lower()
template = '%s_file_view_react.html' % filetype.lower()
if filetype in (IMAGE, VIDEO, AUDIO, PDF, SVG, XMIND, 'Unknown'):
template = 'common_file_view_react.html'
if filetype == SEADOC:
file_uuid = get_seadoc_file_uuid(repo, path)
return_dict['file_uuid'] = file_uuid
return_dict['assets_url'] = '/api/v2.1/seadoc/download-image/' + file_uuid
return_dict['seadoc_server_url'] = SEADOC_SERVER_URL
can_edit_file = True
if parse_repo_perm(permission).can_edit_on_web is False:
can_edit_file = False
elif is_locked and not locked_by_me:
can_edit_file = False
return_dict['can_edit_file'] = can_edit_file
if is_pro_version() and can_edit_file:
try:
if not is_locked:
seafile_api.lock_file(repo_id, path, ONLINE_OFFICE_LOCK_OWNER,
int(time.time()) + 40 * 60)
elif locked_by_online_office:
seafile_api.refresh_file_lock(repo_id, path,
int(time.time()) + 40 * 60)
except Exception as e:
logger.error(e)
seadoc_perm = 'rw' if can_edit_file else 'r'
return_dict['seadoc_access_token'] = gen_seadoc_access_token(file_uuid, filename, username, permission=seadoc_perm)
# draft
is_sdoc_draft = False
sdoc_draft = SeadocDraft.objects.get_by_doc_uuid(file_uuid)
if sdoc_draft:
is_sdoc_draft = True
return_dict['is_sdoc_draft'] = is_sdoc_draft
# revision
revision_info = is_seadoc_revision(file_uuid)
return_dict.update(revision_info)
send_file_access_msg(request, repo, path, 'web')
return render(request, template, return_dict)
if filetype == TEXT or fileext in get_conf_text_ext():
# get file size
if file_size > FILE_PREVIEW_MAX_SIZE:
error_msg = _('File size surpasses %s, can not be opened online.') % \
filesizeformat(FILE_PREVIEW_MAX_SIZE)
return_dict['err'] = error_msg
return render(request, template, return_dict)
file_enc = request.GET.get('file_enc', 'auto')
if file_enc not in FILE_ENCODING_LIST:
file_enc = 'auto'
error_msg, file_content, encoding = get_file_content(filetype, inner_path, file_enc)
if error_msg:
return_dict['err'] = error_msg
return render(request, template, return_dict)
file_encoding_list = FILE_ENCODING_LIST
if encoding and encoding not in FILE_ENCODING_LIST:
file_encoding_list.append(encoding)
return_dict['file_enc'] = file_enc
# return_dict['encoding'] = encoding
# return_dict['file_encoding_list'] = file_encoding_list
return_dict['file_content'] = file_content
can_edit_file = True
if parse_repo_perm(permission).can_edit_on_web is False:
can_edit_file = False
elif is_locked and not locked_by_me:
can_edit_file = False
return_dict['can_edit_file'] = can_edit_file
send_file_access_msg(request, repo, path, 'web')
return render(request, template, return_dict)
if filetype == MARKDOWN:
mode = request.GET.get('mode', '')
is_draft = is_draft_file(repo.id, path)
has_draft = False
if not is_draft:
has_draft = has_draft_file(repo.id, path)
draft = get_file_draft(repo.id, path, is_draft, has_draft)
return_dict['protocol'] = request.is_secure() and 'https' or 'http'
return_dict['domain'] = get_current_site(request).domain
return_dict['serviceUrl'] = get_service_url().rstrip('/')
return_dict['language_code'] = get_language()
return_dict['mode'] = 'edit' if mode == 'edit' else 'viewer'
return_dict['is_draft'] = is_draft
return_dict['has_draft'] = has_draft
return_dict['draft_id'] = draft['draft_id']
return_dict['draft_file_path'] = draft['draft_file_path']
return_dict['draft_origin_file_path'] = draft['draft_origin_file_path']
return_dict['share_link_expire_days_Default'] = SHARE_LINK_EXPIRE_DAYS_DEFAULT
return_dict['share_link_expire_days_min'] = SHARE_LINK_EXPIRE_DAYS_MIN
return_dict['share_link_expire_days_max'] = SHARE_LINK_EXPIRE_DAYS_MAX
can_edit_file = True
if parse_repo_perm(permission).can_edit_on_web is False:
can_edit_file = False
elif is_locked and not locked_by_me:
can_edit_file = False
return_dict['can_edit_file'] = can_edit_file
return render(request, template, return_dict)
elif filetype in (VIDEO, AUDIO, PDF, SVG):
return_dict['raw_path'] = raw_path
send_file_access_msg(request, repo, path, 'web')
if filetype == VIDEO:
return_dict['enable_video_thumbnail'] = settings.ENABLE_VIDEO_THUMBNAIL
return render(request, template, return_dict)
elif filetype == XMIND:
xmind_image_path = get_thumbnail_image_path(file_id, XMIND_IMAGE_SIZE)
if not os.path.exists(xmind_image_path) and not extract_xmind_image(repo_id, path)[0]:
error_msg = _('Unable to view file')
return_dict['err'] = error_msg
else:
return_dict['xmind_image_src'] = quote(get_thumbnail_src(repo_id, XMIND_IMAGE_SIZE, path))
return render(request, template, return_dict)
elif filetype == IMAGE:
if file_size > FILE_PREVIEW_MAX_SIZE:
error_msg = _('File size surpasses %s, can not be opened online.') % \
filesizeformat(FILE_PREVIEW_MAX_SIZE)
return_dict['err'] = error_msg
return render(request, template, return_dict)
img_prev = None
img_next = None
img_list = []
dirs = seafile_api.list_dir_by_path(repo_id, parent_dir)
for dirent in dirs:
if not stat.S_ISDIR(dirent.props.mode):
fltype, flext = get_file_type_and_ext(dirent.obj_name)
if fltype == IMAGE:
img_list.append(dirent.obj_name)
if len(img_list) > 1:
img_list.sort(key=lambda x: x.lower())
cur_img_index = img_list.index(filename)
if cur_img_index != 0:
img_prev = posixpath.join(parent_dir, img_list[cur_img_index - 1])
if cur_img_index != len(img_list) - 1:
img_next = posixpath.join(parent_dir, img_list[cur_img_index + 1])
return_dict['img_prev'] = img_prev
return_dict['img_next'] = img_next
return_dict['raw_path'] = raw_path
send_file_access_msg(request, repo, path, 'web')
return render(request, template, return_dict)
elif filetype in (DOCUMENT, SPREADSHEET):
if repo.encrypted:
return_dict['err'] = _('The library is encrypted, can not open file online.')
return render(request, template, return_dict)
if ENABLE_OFFICE_WEB_APP:
action_name = None
# first check if can view file
if fileext in OFFICE_WEB_APP_FILE_EXTENSION:
action_name = 'view'
# then check if can edit file
if ENABLE_OFFICE_WEB_APP_EDIT and parse_repo_perm(permission).can_edit_on_web and \
fileext in OFFICE_WEB_APP_EDIT_FILE_EXTENSION and \
((not is_locked) or (is_locked and locked_by_online_office)):
action_name = 'edit'
wopi_dict = get_wopi_dict(username, repo_id, path,
action_name=action_name,
language_code=request.LANGUAGE_CODE,
can_download=parse_repo_perm(permission).can_download)
if wopi_dict:
send_file_access_msg(request, repo, path, 'web')
return render(request, 'view_file_wopi.html', wopi_dict)
else:
return_dict['err'] = _('Error when prepare Office Online file preview page.')
if ENABLE_ONLYOFFICE and fileext in ONLYOFFICE_FILE_EXTENSION:
can_edit = False
if parse_repo_perm(permission).can_edit_on_web and \
fileext in ONLYOFFICE_EDIT_FILE_EXTENSION and \
((not is_locked) or (is_locked and locked_by_online_office)):
can_edit = True
onlyoffice_dict = get_onlyoffice_dict(request, username, repo_id, path,
can_edit=can_edit,
can_download=parse_repo_perm(permission).can_download)
if onlyoffice_dict:
if is_pro_version() and can_edit:
try:
if not is_locked:
logger.info('{} lock {} in repo {} when open it via OnlyOffice.'.format(ONLINE_OFFICE_LOCK_OWNER, path, repo_id))
seafile_api.lock_file(repo_id, path, ONLINE_OFFICE_LOCK_OWNER,
int(time.time()) + 40 * 60)
elif locked_by_online_office:
logger.info('{} relock {} in repo {} when open it via OnlyOffice.'.format(ONLINE_OFFICE_LOCK_OWNER, path, repo_id))
seafile_api.refresh_file_lock(repo_id, path,
int(time.time()) + 40 * 60)
except Exception as e:
logger.error(e)
send_file_access_msg(request, repo, path, 'web')
return render(request, 'view_file_onlyoffice.html', onlyoffice_dict)
else:
return_dict['err'] = _('Error when prepare OnlyOffice file preview page.')
if ENABLE_BISHENG_OFFICE and fileext in BISHENG_OFFICE_FILE_EXTENSION:
bisheng_info_dict = get_bisheng_dict(username, repo_id, path)
doc_id = bisheng_info_dict['doc_id']
call_url = bisheng_info_dict['call_url']
sign = bisheng_info_dict['sign']
# openEditor vs openPreview
can_edit = False
if parse_repo_perm(permission).can_edit_on_web and \
((not is_locked) or (is_locked and locked_by_online_office)):
can_edit = True
if can_edit:
editor_url = get_bisheng_editor_url(call_url, sign)
else:
editor_url = get_bisheng_preivew_url(call_url, sign)
# store info to cache
bisheng_info_dict['can_edit'] = can_edit
cache.set('BISHENG_OFFICE_' + doc_id, bisheng_info_dict, None)
return HttpResponseRedirect(editor_url)
if not HAS_OFFICE_CONVERTER:
return_dict['err'] = "File preview unsupported"
return render(request, template, return_dict)
if file_size > OFFICE_PREVIEW_MAX_SIZE:
error_msg = _('File size surpasses %s, can not be opened online.') % \
filesizeformat(OFFICE_PREVIEW_MAX_SIZE)
return_dict['err'] = error_msg
return render(request, template, return_dict)
error_msg = prepare_converted_html(raw_path, file_id, fileext, return_dict)
if error_msg:
return_dict['err'] = error_msg
return render(request, template, return_dict)
send_file_access_msg(request, repo, path, 'web')
return render(request, template, return_dict)
elif getattr(settings, 'ENABLE_CAD', False) and path.endswith('.dwg'):
from seahub.cad.utils import get_cad_dict
cad_dict = get_cad_dict(request, username, repo_id, path)
return_dict.update(cad_dict)
return render(request, 'view_file_cad.html', return_dict)
else:
return_dict['err'] = "File preview unsupported"
return render(request, template, return_dict)
def view_history_file_common(request, repo_id, ret_dict):
ret_dict['err'] = ''
# check arguments
repo = get_repo(repo_id)
if not repo:
raise Http404
path = request.GET.get('p', '/')
path = normalize_file_path(path)
commit_id = request.GET.get('commit_id', '')
if not commit_id:
raise Http404
obj_id = request.GET.get('obj_id', '')
if not obj_id:
raise Http404
# construct some varibles
u_filename = os.path.basename(path)
current_commit = get_commit(repo.id, repo.version, commit_id)
if not current_commit:
raise Http404
# get file type and extension
filetype, fileext = get_file_type_and_ext(u_filename)
# Check whether user has permission to view file and get file raw path,
# render error page if permission deny.
if filetype == VIDEO or filetype == AUDIO:
raw_path, inner_path, user_perm = get_file_view_path_and_perm(
request, repo_id, obj_id, path, use_onetime=False)
else:
raw_path, inner_path, user_perm = get_file_view_path_and_perm(
request, repo_id, obj_id, path)
request.user_perm = user_perm
if user_perm:
if filetype in (DOCUMENT, SPREADSHEET):
username = request.user.username
if ENABLE_OFFICE_WEB_APP and fileext in OFFICE_WEB_APP_FILE_EXTENSION:
# obj_id for view trash/history file
wopi_dict = get_wopi_dict(username, repo_id, path,
language_code=request.LANGUAGE_CODE, obj_id=obj_id,
can_download=parse_repo_perm(user_perm).can_download)
if wopi_dict:
# send file audit message
send_file_access_msg(request, repo, path, 'web')
ret_dict['wopi_dict'] = wopi_dict
else:
ret_dict['err'] = _('Error when prepare Office Online file preview page.')
if ENABLE_ONLYOFFICE and fileext in ONLYOFFICE_FILE_EXTENSION:
onlyoffice_dict = get_onlyoffice_dict(request, username, repo_id, path,
file_id=obj_id, can_download=parse_repo_perm(user_perm).can_download)
if onlyoffice_dict:
# send file audit message
send_file_access_msg(request, repo, path, 'web')
ret_dict['onlyoffice_dict'] = onlyoffice_dict
else:
ret_dict['err'] = _('Error when prepare OnlyOffice file preview page.')
# Check if can preview file
fsize = get_file_size(repo.store_id, repo.version, obj_id)
can_preview, err_msg = can_preview_file(u_filename, fsize, repo)
if can_preview:
# send file audit message
send_file_access_msg(request, repo, path, 'web')
"""Choose different approach when dealing with different type of file."""
if is_textual_file(file_type=filetype):
handle_textual_file(request, filetype, inner_path, ret_dict)
elif filetype == DOCUMENT:
handle_document(raw_path, obj_id, fileext, ret_dict)
elif filetype == SPREADSHEET:
handle_spreadsheet(raw_path, obj_id, fileext, ret_dict)
else:
pass
else:
ret_dict['err'] = err_msg
# populate return value dict
ret_dict['repo'] = repo
ret_dict['obj_id'] = obj_id
ret_dict['file_name'] = u_filename
ret_dict['path'] = path
ret_dict['current_commit'] = current_commit
ret_dict['fileext'] = fileext
ret_dict['raw_path'] = raw_path
ret_dict['enable_watermark'] = ENABLE_WATERMARK
ret_dict['can_download_file'] = parse_repo_perm(user_perm).can_download
if 'filetype' not in ret_dict:
ret_dict['filetype'] = filetype
@repo_passwd_set_required
def view_history_file(request, repo_id):
ret_dict = {}
view_history_file_common(request, repo_id, ret_dict)
if not request.user_perm:
return render_permission_error(request, _('Unable to view file'))
if ret_dict['err']:
return render(request, 'history_file_view_react.html', ret_dict)
if 'wopi_dict' in ret_dict:
wopi_dict = ret_dict['wopi_dict']
return render(request, 'view_file_wopi.html', wopi_dict)
if 'onlyoffice_dict' in ret_dict:
onlyoffice_dict = ret_dict['onlyoffice_dict']
return render(request, 'view_file_onlyoffice.html', onlyoffice_dict)
# generate file path navigator
path = ret_dict['path']
repo = ret_dict['repo']
ret_dict['zipped'] = gen_path_link(path, repo.name)
#return render(request, 'view_history_file.html', ret_dict)
return render(request, 'history_file_view_react.html', ret_dict)
@repo_passwd_set_required
def view_trash_file(request, repo_id):
ret_dict = {}
view_history_file_common(request, repo_id, ret_dict)
if not request.user_perm:
return render_permission_error(request, _('Unable to view file'))
if ret_dict['err']:
return render(request, 'history_file_view_react.html', ret_dict)
if 'wopi_dict' in ret_dict:
wopi_dict = ret_dict['wopi_dict']
return render(request, 'view_file_wopi.html', wopi_dict)
if 'onlyoffice_dict' in ret_dict:
onlyoffice_dict = ret_dict['onlyoffice_dict']
return render(request, 'view_file_onlyoffice.html', onlyoffice_dict)
basedir = request.GET.get('base', '')
if not basedir:
raise Http404
if basedir != '/':
tmp_path = posixpath.join(basedir.rstrip('/'), ret_dict['path'].lstrip('/'))
ret_dict['path'] = tmp_path
#return render(request, 'view_trash_file.html', ret_dict)
ret_dict['from_trash'] = True
return render(request, 'history_file_view_react.html', ret_dict)
@repo_passwd_set_required
def view_snapshot_file(request, repo_id):
ret_dict = {}
view_history_file_common(request, repo_id, ret_dict)
if not request.user_perm:
return render_permission_error(request, _('Unable to view file'))
if ret_dict['err']:
return render(request, 'history_file_view_react.html', ret_dict)
if 'wopi_dict' in ret_dict:
wopi_dict = ret_dict['wopi_dict']
return render(request, 'view_file_wopi.html', wopi_dict)
if 'onlyoffice_dict' in ret_dict:
onlyoffice_dict = ret_dict['onlyoffice_dict']
return render(request, 'view_file_onlyoffice.html', onlyoffice_dict)
# generate file path navigator
path = ret_dict['path']
repo = ret_dict['repo']
ret_dict['zipped'] = gen_path_link(path, repo.name)
#return render(request, 'view_snapshot_file.html', ret_dict)
return render(request, 'history_file_view_react.html', ret_dict)
def _download_file_from_share_link(request, fileshare):
"""Download shared file.
`path` need to be provided by frontend, if missing, use `fileshare.path`
"""
next_page = request.headers.get('referer', settings.SITE_ROOT)
repo = get_repo(fileshare.repo_id)
if not repo:
raise Http404
# Construct real file path if download file in shared dir, otherwise, just
# use path in DB.
if isinstance(fileshare, FileShare) and fileshare.is_dir_share_link():
req_path = request.GET.get('p', '')
if not req_path:
messages.error(request, _('Unable to download file, invalid file path'))
return HttpResponseRedirect(next_page)
real_path = posixpath.join(fileshare.path, req_path.lstrip('/'))
else:
real_path = fileshare.path
filename = os.path.basename(real_path)
obj_id = seafile_api.get_file_id_by_path(repo.id, real_path)
if not obj_id:
messages.error(request, _('Unable to download file, wrong file path'))
return HttpResponseRedirect(next_page)
dl_token = seafile_api.get_fileserver_access_token(repo.id,
obj_id, 'download-link', fileshare.username, use_onetime=False)
if not dl_token:
messages.error(request, _('Unable to download file.'))
return HttpResponseRedirect(gen_file_get_url(dl_token, filename))
@ensure_csrf_cookie
@share_link_audit
@share_link_login_required
def view_shared_file(request, fileshare):
"""
View file via shared link.
Download share file if `dl` in request param.
View raw share file if `raw` in request param.
"""
token = fileshare.token
# check if share link is encrypted
password_check_passed, err_msg = check_share_link_common(request, fileshare)
direct_download = request.GET.get('dl', '') == '1'
if not password_check_passed:
d = {'token': token, 'view_name': 'view_shared_file',
'err_msg': err_msg, 'direct_download': direct_download}
return render(request, 'share_access_validation.html', d)
# recourse check
repo_id = fileshare.repo_id
repo = get_repo(repo_id)
if not repo:
raise Http404
path = normalize_file_path(fileshare.path)
obj_id = seafile_api.get_file_id_by_path(repo_id, path)
if not obj_id:
return render_error(request, _('File does not exist'))
# permission check
shared_by = fileshare.username
if not seafile_api.check_permission_by_path(repo_id, '/', shared_by):
return render_error(request, _('Permission denied'))
# Increase file shared link view_cnt, this operation should be atomic
fileshare.view_cnt = F('view_cnt') + 1
fileshare.save()
if not request.user.is_authenticated:
username = ANONYMOUS_EMAIL
else:
username = request.user.username
# check file lock info
try:
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
except Exception as e:
logger.error(e)
is_locked = False
locked_by_me = False
locked_by_online_office = if_locked_by_online_office(repo_id, path)
# get share link permission
can_download = fileshare.get_permissions()['can_download']
can_edit = fileshare.get_permissions()['can_edit'] and \
(not is_locked or locked_by_online_office)
# download shared file
if request.GET.get('dl', '') == '1':
if can_download is False:
raise Http404
# send file audit message
send_file_access_msg(request, repo, path, 'share-link')
return _download_file_from_share_link(request, fileshare)
# get raw file
access_token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'view', '', use_onetime=False)
if not access_token:
return render_error(request, _('Unable to view file'))
filename = os.path.basename(path)
raw_path = gen_file_get_url(access_token, filename)
if request.GET.get('raw', '') == '1':
if can_download is False:
raise Http404
# send file audit message
send_file_access_msg(request, repo, path, 'share-link')
# view raw shared file, directly show/download file depends on
# browsers
return HttpResponseRedirect(raw_path)
# preview file
filetype, fileext = get_file_type_and_ext(filename)
ret_dict = {'err': '', 'file_content': '', 'encoding': '', 'file_enc': '',
'file_encoding_list': [], 'filetype': filetype}
if filetype == SEADOC:
file_uuid = get_seadoc_file_uuid(repo, path)
ret_dict['file_uuid'] = file_uuid
ret_dict['assets_url'] = '/api/v2.1/seadoc/download-image/' + file_uuid
ret_dict['seadoc_server_url'] = SEADOC_SERVER_URL
ret_dict['can_edit_file'] = can_edit
seadoc_perm = 'rw' if can_edit else 'r'
ret_dict['file_perm'] = seadoc_perm
ret_dict['seadoc_access_token'] = gen_seadoc_access_token(file_uuid, filename, username, permission=seadoc_perm)
send_file_access_msg(request, repo, path, 'web')
request.session['seadoc_share_session'] = {
'file_uuid': file_uuid,
'permission': seadoc_perm,
'seadoc_access_token': ret_dict['seadoc_access_token'],
}
if filetype in (DOCUMENT, SPREADSHEET):
def online_office_lock_or_refresh_lock(repo_id, path, username):
try:
if not is_locked:
seafile_api.lock_file(repo_id, path, ONLINE_OFFICE_LOCK_OWNER,
int(time.time()) + 40 * 60)
elif locked_by_online_office:
seafile_api.refresh_file_lock(repo_id, path,
int(time.time()) + 40 * 60)
except Exception as e:
logger.error(e)
if ENABLE_OFFICE_WEB_APP and fileext in OFFICE_WEB_APP_FILE_EXTENSION:
action_name = 'edit' if can_edit else 'view'
wopi_dict = get_wopi_dict(username, repo_id, path,
action_name=action_name, can_download=can_download,
language_code=request.LANGUAGE_CODE)
if wopi_dict:
wopi_dict['share_link_token'] = token
# send file audit message
send_file_access_msg(request, repo, path, 'share-link')
return render(request, 'view_file_wopi.html', wopi_dict)
else:
ret_dict['err'] = _('Error when prepare Office Online file preview page.')
if ENABLE_ONLYOFFICE and fileext in ONLYOFFICE_FILE_EXTENSION:
onlyoffice_dict = get_onlyoffice_dict(request, username, repo_id, path,
can_edit=can_edit, can_download=can_download)
if onlyoffice_dict:
if is_pro_version() and can_edit:
online_office_lock_or_refresh_lock(repo_id, path, username)
onlyoffice_dict['share_link_token'] = token
# send file audit message
send_file_access_msg(request, repo, path, 'share-link')
return render(request, 'view_file_onlyoffice.html',
onlyoffice_dict)
else:
ret_dict['err'] = _('Error when prepare OnlyOffice file preview page.')
file_size = seafile_api.get_file_size(repo.store_id, repo.version, obj_id)
can_preview, err_msg = can_preview_file(filename, file_size, repo)
if can_preview:
# send file audit message
send_file_access_msg(request, repo, path, 'share-link')
"""Choose different approach when dealing with different type of file."""
inner_path = gen_inner_file_get_url(access_token, filename)
if is_textual_file(file_type=filetype):
handle_textual_file(request, filetype, inner_path, ret_dict)
elif filetype == DOCUMENT:
handle_document(raw_path, obj_id, fileext, ret_dict)
elif filetype == SPREADSHEET:
handle_spreadsheet(raw_path, obj_id, fileext, ret_dict)
elif filetype == XMIND:
xmind_image_path = get_thumbnail_image_path(obj_id, XMIND_IMAGE_SIZE)
if not os.path.exists(xmind_image_path) and not extract_xmind_image(repo_id, path)[0]:
error_msg = _('Unable to view file')
ret_dict['err'] = error_msg
else:
raw_path = quote(SITE_ROOT + get_share_link_thumbnail_src(token, XMIND_IMAGE_SIZE, path))
else:
ret_dict['err'] = err_msg
accessible_repos = get_unencry_rw_repos_by_user(request)
save_to_link = reverse('save_shared_link') + '?t=' + token
permissions = fileshare.get_permissions()
#template = 'shared_file_view.html'
template = 'shared_file_view_react.html'
file_share_link = request.path
desc_for_ogp = _('Share link for %s.') % filename
icon_path_for_ogp = file_icon_filter(filename, size=192)
data = {'repo': repo,
'obj_id': obj_id,
'path': path,
'file_name': filename,
'file_size': file_size,
'shared_token': token,
'access_token': access_token,
'fileext': fileext,
'raw_path': raw_path,
'shared_by': shared_by,
'err': ret_dict['err'],
'file_content': ret_dict['file_content'],
'encoding': ret_dict['encoding'],
'file_encoding_list': ret_dict['file_encoding_list'],
'filetype': ret_dict['filetype'],
'accessible_repos': accessible_repos,
'save_to_link': save_to_link,
'traffic_over_limit': False,
'permissions': permissions,
'enable_watermark': ENABLE_WATERMARK,
'file_share_link': file_share_link,
'desc_for_ogp': desc_for_ogp,
'icon_path_for_ogp': icon_path_for_ogp,
'enable_share_link_report_abuse': ENABLE_SHARE_LINK_REPORT_ABUSE,
}
if filetype == SEADOC:
data['file_uuid'] = ret_dict['file_uuid']
data['assets_url'] = ret_dict['assets_url']
data['seadoc_server_url'] = ret_dict['seadoc_server_url']
data['seadoc_access_token'] = ret_dict['seadoc_access_token']
data['can_edit_file'] = ret_dict['can_edit_file']
data['file_perm'] = ret_dict['file_perm']
return render(request, template, data)
@share_link_audit
@share_link_login_required
def view_file_via_shared_dir(request, fileshare):
token = fileshare.token
# argument check
req_path = request.GET.get('p', '')
if not req_path:
return HttpResponseRedirect(reverse('view_shared_dir', args=[token]))
req_path = normalize_file_path(req_path)
# check if share link is encrypted
password_check_passed, err_msg = check_share_link_common(request, fileshare)
if not password_check_passed:
d = {'token': token,
'view_name': 'view_file_via_shared_dir',
'path': req_path,
'err_msg': err_msg,
}
return render(request, 'share_access_validation.html', d)
# recourse check
repo_id = fileshare.repo_id
repo = get_repo(repo_id)
if not repo:
raise Http404
# recourse check
# Get file path from frontend, and construct request file path
# with fileshare.path to real path, used to fetch file content by RPC.
real_path = posixpath.join(fileshare.path, req_path.lstrip('/'))
obj_id = seafile_api.get_file_id_by_path(repo_id, real_path)
if not obj_id:
return render_error(request, _('File does not exist'))
# permission check
shared_by = fileshare.username
if not seafile_api.check_permission_by_path(repo_id, '/', shared_by):
return render_error(request, _('Permission denied'))
if not request.user.is_authenticated:
username = ANONYMOUS_EMAIL
else:
username = request.user.username
# check file lock info
try:
is_locked, locked_by_me = check_file_lock(repo_id, real_path, username)
except Exception as e:
logger.error(e)
is_locked = False
locked_by_me = False
locked_by_online_office = if_locked_by_online_office(repo_id, real_path)
# get share link permission
can_download = fileshare.get_permissions()['can_download']
can_edit = fileshare.get_permissions()['can_edit'] and \
(not is_locked or locked_by_online_office)
# download shared file
if request.GET.get('dl', '') == '1':
if fileshare.get_permissions()['can_download'] is False:
raise Http404
# send file audit message
send_file_access_msg(request, repo, real_path, 'share-link')
return _download_file_from_share_link(request, fileshare)
# get raw file
access_token = seafile_api.get_fileserver_access_token(repo.id,
obj_id, 'view', '', use_onetime=False)
if not access_token:
return render_error(request, _('Unable to view file'))
filename = os.path.basename(req_path)
raw_path = gen_file_get_url(access_token, filename)
if request.GET.get('raw', '0') == '1':
if fileshare.get_permissions()['can_download'] is False:
raise Http404
# send file audit message
send_file_access_msg(request, repo, real_path, 'share-link')
# view raw shared file, directly show/download file depends on browsers
return HttpResponseRedirect(raw_path)
filetype, fileext = get_file_type_and_ext(filename)
ret_dict = {'err': '', 'file_content': '', 'encoding': '', 'file_enc': '',
'file_encoding_list': [], 'filetype': filetype}
if filetype == SEADOC:
file_uuid = get_seadoc_file_uuid(repo, real_path)
ret_dict['file_uuid'] = file_uuid
ret_dict['assets_url'] = '/api/v2.1/seadoc/download-image/' + file_uuid
ret_dict['seadoc_server_url'] = SEADOC_SERVER_URL
ret_dict['can_edit_file'] = can_edit
seadoc_perm = 'rw' if can_edit else 'r'
ret_dict['file_perm'] = seadoc_perm
ret_dict['seadoc_access_token'] = gen_seadoc_access_token(file_uuid, filename, username, permission=seadoc_perm)
send_file_access_msg(request, repo, real_path, 'web')
request.session['seadoc_share_session'] = {
'file_uuid': file_uuid,
'permission': seadoc_perm,
'seadoc_access_token': ret_dict['seadoc_access_token'],
}
if filetype in (DOCUMENT, SPREADSHEET):
if not request.user.is_authenticated:
username = ANONYMOUS_EMAIL
else:
username = request.user.username
if ENABLE_OFFICE_WEB_APP and fileext in OFFICE_WEB_APP_FILE_EXTENSION:
wopi_dict = get_wopi_dict(username, repo_id, real_path,
language_code=request.LANGUAGE_CODE)
if wopi_dict:
# send file audit message
send_file_access_msg(request, repo, real_path, 'share-link')
return render(request, 'view_file_wopi.html', wopi_dict)
else:
ret_dict['err'] = _('Error when prepare Office Online file preview page.')
if ENABLE_ONLYOFFICE and fileext in ONLYOFFICE_FILE_EXTENSION:
onlyoffice_dict = get_onlyoffice_dict(request, username,
repo_id, real_path,
can_edit=can_edit, can_download=can_download)
if onlyoffice_dict:
# send file audit message
send_file_access_msg(request, repo, real_path, 'share-link')
return render(request, 'view_file_onlyoffice.html',
onlyoffice_dict)
else:
ret_dict['err'] = _('Error when prepare OnlyOffice file preview page.')
img_prev = None
img_next = None
inner_path = gen_inner_file_get_url(access_token, filename)
file_size = seafile_api.get_file_size(repo.store_id, repo.version, obj_id)
can_preview, err_msg = can_preview_file(filename, file_size, repo)
if can_preview:
# send file audit message
send_file_access_msg(request, repo, real_path, 'share-link')
"""Choose different approach when dealing with different type of file."""
if is_textual_file(file_type=filetype):
handle_textual_file(request, filetype, inner_path, ret_dict)
elif filetype == DOCUMENT:
handle_document(raw_path, obj_id, fileext, ret_dict)
elif filetype == SPREADSHEET:
handle_spreadsheet(raw_path, obj_id, fileext, ret_dict)
elif filetype == IMAGE:
current_commit = get_commits(repo_id, 0, 1)[0]
real_parent_dir = os.path.dirname(real_path)
parent_dir = os.path.dirname(req_path)
dirs = seafile_api.list_dir_by_commit_and_path(current_commit.repo_id,
current_commit.id, real_parent_dir)
if not dirs:
raise Http404
img_list = []
for dirent in dirs:
if not stat.S_ISDIR(dirent.props.mode):
fltype, flext = get_file_type_and_ext(dirent.obj_name)
if fltype == 'Image':
img_list.append(dirent.obj_name)
if len(img_list) > 1:
img_list.sort(key=lambda x: x.lower())
cur_img_index = img_list.index(filename)
if cur_img_index != 0:
img_prev = posixpath.join(parent_dir, img_list[cur_img_index - 1])
if cur_img_index != len(img_list) - 1:
img_next = posixpath.join(parent_dir, img_list[cur_img_index + 1])
elif filetype == XMIND:
xmind_image_path = get_thumbnail_image_path(obj_id, XMIND_IMAGE_SIZE)
if not os.path.exists(xmind_image_path) and not extract_xmind_image(repo_id, real_path)[0]:
error_msg = _('Unable to view file')
ret_dict['err'] = error_msg
else:
raw_path = quote(SITE_ROOT + get_share_link_thumbnail_src(token, XMIND_IMAGE_SIZE, req_path))
else:
ret_dict['err'] = err_msg
permissions = fileshare.get_permissions()
# generate dir navigator
if fileshare.path == '/':
zipped = gen_path_link(req_path, repo.name)
else:
zipped = gen_path_link(req_path, os.path.basename(fileshare.path[:-1]))
#template = 'shared_file_view.html'
template = 'shared_file_view_react.html'
file_share_link = request.path
desc_for_ogp = _('Share link for %s.') % filename
icon_path_for_ogp = file_icon_filter(filename, size=192)
data = {'repo': repo,
'obj_id': obj_id,
'from_shared_dir': True,
'path': req_path,
'file_name': filename,
'file_size': file_size,
'shared_token': token,
'access_token': access_token,
'fileext': fileext,
'raw_path': raw_path,
'shared_by': shared_by,
'token': token,
'err': ret_dict['err'],
'file_content': ret_dict['file_content'],
'encoding': ret_dict['encoding'],
'file_encoding_list': ret_dict['file_encoding_list'],
'filetype': ret_dict['filetype'],
'zipped': zipped,
'img_prev': img_prev,
'img_next': img_next,
'traffic_over_limit': False,
'permissions': permissions,
'enable_watermark': ENABLE_WATERMARK,
'file_share_link': file_share_link,
'desc_for_ogp': desc_for_ogp,
'icon_path_for_ogp': icon_path_for_ogp,
'enable_share_link_report_abuse': ENABLE_SHARE_LINK_REPORT_ABUSE,
}
if filetype == SEADOC:
data['file_uuid'] = ret_dict['file_uuid']
data['assets_url'] = ret_dict['assets_url']
data['seadoc_server_url'] = ret_dict['seadoc_server_url']
data['seadoc_access_token'] = ret_dict['seadoc_access_token']
data['can_edit_file'] = ret_dict['can_edit_file']
data['file_perm'] = ret_dict['file_perm']
return render(request, template, data)
@login_required
def view_raw_file(request, repo_id, file_path):
"""Returns raw content of a file.
Used when use viewer.js to preview opendocument file.
Arguments:
- `request`:
- `repo_id`:
"""
repo = get_repo(repo_id)
if not repo:
raise Http404
file_path = file_path.rstrip('/')
if file_path[0] != '/':
file_path = '/' + file_path
obj_id = get_file_id_by_path(repo_id, file_path)
if not obj_id:
raise Http404
raw_path, inner_path, user_perm = get_file_view_path_and_perm(
request, repo.id, obj_id, file_path)
if user_perm is None:
raise Http404
send_file_access_msg(request, repo, file_path, 'web')
return HttpResponseRedirect(raw_path)
def send_file_access_msg(request, repo, path, access_from):
"""Send file downlaod msg for audit.
Arguments:
- `request`:
- `repo`:
- `obj_id`:
- `access_from`: web or api or share-link
"""
access_from = access_from.lower()
if access_from not in ['web', 'api', 'share-link']:
logger.warn('Invalid access_from in file access msg: %s' % access_from)
return
username = request.user.username
ip = get_remote_ip(request)
user_agent = request.headers.get("user-agent")
msg = 'file-download-%s\t%s\t%s\t%s\t%s\t%s' % \
(access_from, username, ip, user_agent, repo.id, path)
try:
seafile_api.publish_event('seahub.audit', msg)
except Exception as e:
logger.error("Error when sending file-download-%s message: %s" %
(access_from, str(e)))
@login_required
def download_file(request, repo_id, obj_id):
"""Download file in repo/file history page.
Arguments:
- `request`:
- `repo_id`:
- `obj_id`:
"""
username = request.user.username
repo = get_repo(repo_id)
if not repo:
raise Http404
if repo.encrypted and not seafile_api.is_password_set(repo_id, username):
reverse_url = reverse('lib_view', args=[repo_id, repo.name, ''])
return HttpResponseRedirect(reverse_url)
# only check the permissions at the repo level
# to prevent file can not be downloaded on the history page
# if it has been renamed
if parse_repo_perm(check_folder_permission(
request, repo_id, '/')).can_download is True:
# Get a token to access file
token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'download', username)
if not token:
messages.error(request, _('Unable to download file'))
next_page = request.headers.get('referer', settings.SITE_ROOT)
return HttpResponseRedirect(next_page)
else:
messages.error(request, _('Unable to download file'))
next_page = request.headers.get('referer', settings.SITE_ROOT)
return HttpResponseRedirect(next_page)
path = request.GET.get('p', '')
send_file_access_msg(request, repo, path, 'web') # send stats message
file_name = os.path.basename(path.rstrip('/'))
redirect_url = gen_file_get_url(token, file_name) # generate download link
return HttpResponseRedirect(redirect_url)
########## text diff
def get_file_content_by_commit_and_path(request, repo_id, commit_id, path, file_enc):
try:
obj_id = seafserv_threaded_rpc.get_file_id_by_commit_and_path( \
repo_id, commit_id, path)
except:
return None, 'bad path'
if not obj_id or obj_id == EMPTY_SHA1:
return '', None
else:
if parse_repo_perm(check_folder_permission(
request, repo_id, '/')).can_preview is True:
# Get a token to visit file
token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'view', request.user.username)
if not token:
return None, 'FileServer access token invalid'
else:
return None, 'permission denied'
filename = os.path.basename(path)
inner_path = gen_inner_file_get_url(token, filename)
try:
err, file_content, encoding = repo_file_get(inner_path, file_enc)
except Exception as e:
return None, 'error when read file from fileserver: %s' % e
return file_content, err
@login_required
def text_diff(request, repo_id):
commit_id = request.GET.get('commit', '')
path = request.GET.get('p', '')
u_filename = os.path.basename(path)
file_enc = request.GET.get('file_enc', 'auto')
if not file_enc in FILE_ENCODING_LIST:
file_enc = 'auto'
if not (commit_id and path):
return render_error(request, 'bad params')
repo = get_repo(repo_id)
if not repo:
return render_error(request, 'bad repo')
current_commit = seafserv_threaded_rpc.get_commit(repo.id, repo.version, commit_id)
if not current_commit:
return render_error(request, 'bad commit id')
prev_commit = seafserv_threaded_rpc.get_commit(repo.id, repo.version, current_commit.parent_id)
if not prev_commit:
return render_error('bad commit id')
current_content, err = get_file_content_by_commit_and_path(request, \
repo_id, current_commit.id, path, file_enc)
if err:
return render_error(request, err)
prev_content, err = get_file_content_by_commit_and_path(request, \
repo_id, prev_commit.id, path, file_enc)
if err:
return render_error(request, err)
is_new_file = False
diff_result_table = ''
if prev_content == '' and current_content == '':
is_new_file = True
else:
diff = HtmlDiff()
diff_result_table = diff.make_table(prev_content.splitlines(),
current_content.splitlines(), True)
zipped = gen_path_link(path, repo.name)
referer = request.GET.get('referer', '')
return render(request, 'text_diff.html', {
'u_filename': u_filename,
'repo': repo,
'path': path,
'zipped': zipped,
'current_commit': current_commit,
'prev_commit': prev_commit,
'diff_result_table': diff_result_table,
'is_new_file': is_new_file,
'referer': referer,
})
########## office related
def _check_office_convert_perm(request, repo_id, path, ret):
token = request.GET.get('token', '')
if not token:
# Work around for the images embedded in excel files
referer = request.headers.get('referer', '')
if referer:
token = urllib.parse.parse_qs(
urllib.parse.urlparse(referer).query).get('token', [''])[0]
if token:
fileshare = FileShare.objects.get_valid_file_link_by_token(token)
if not fileshare or fileshare.repo_id != repo_id:
return False
if fileshare.is_file_share_link() and fileshare.path == path:
return True
if fileshare.is_dir_share_link():
ret['dir_share_path'] = fileshare.path
return True
return False
else:
return request.user.is_authenticated and \
check_folder_permission(request, repo_id, '/') is not None
def _office_convert_get_file_id(request, repo_id=None, commit_id=None, path=None):
repo_id = repo_id or request.GET.get('repo_id', '')
commit_id = commit_id or request.GET.get('commit_id', '')
path = path or request.GET.get('path', '')
if not (repo_id and path and commit_id):
raise BadRequestException()
if '../' in path:
raise BadRequestException()
ret = {'dir_share_path': None}
if not _check_office_convert_perm(request, repo_id, path, ret):
raise BadRequestException()
if ret['dir_share_path']:
path = posixpath.join(ret['dir_share_path'], path.lstrip('/'))
return seafserv_threaded_rpc.get_file_id_by_commit_and_path(repo_id, commit_id, path)
@json_response
def office_convert_query_status(request):
if not request.headers.get('x-requested-with') == 'XMLHttpRequest':
raise Http404
doctype = request.GET.get('doctype', None)
file_id = _office_convert_get_file_id(request)
ret = {'success': False}
try:
ret = query_office_convert_status(file_id, doctype)
except Exception as e:
logging.exception('failed to call query_office_convert_status')
ret['error'] = str(e)
return ret
_OFFICE_PAGE_PATTERN = re.compile(r'^file\.css|file\.outline|index.html|index_html_.*.png|[a-z0-9]+\.pdf$')
def office_convert_get_page(request, repo_id, commit_id, path, filename):
"""Valid static file path inclueds:
- index.html for spreadsheets and index_html_xxx.png for images embedded in spreadsheets
- 77e168722458356507a1f373714aa9b575491f09.pdf
"""
if not HAS_OFFICE_CONVERTER:
raise Http404
if not _OFFICE_PAGE_PATTERN.match(filename):
return HttpResponseForbidden()
path = '/' + path
file_id = _office_convert_get_file_id(request, repo_id, commit_id, path)
if filename.endswith('.pdf'):
filename = "{0}.pdf".format(file_id)
resp = get_office_converted_page(path, filename, file_id)
if filename.endswith('.page'):
content_type = 'text/html'
else:
content_type = mimetypes.guess_type(filename)[0] or 'text/html'
resp['Content-Type'] = content_type
return resp
@login_required
def file_access(request, repo_id):
"""List file access log.
"""
if not is_pro_version() or not FILE_AUDIT_ENABLED:
raise Http404
referer = request.headers.get('referer', None)
next_page = settings.SITE_ROOT if referer is None else referer
repo = get_repo(repo_id)
if not repo:
messages.error(request, _("Library does not exist"))
return HttpResponseRedirect(next_page)
path = request.GET.get('p', None)
if not path:
messages.error(request, _("Argument missing"))
return HttpResponseRedirect(next_page)
if not seafile_api.get_file_id_by_path(repo_id, path):
messages.error(request, _("File does not exist"))
return HttpResponseRedirect(next_page)
# perm check
if check_folder_permission(request, repo_id, path) != 'rw':
messages.error(request, _("Permission denied"))
return HttpResponseRedirect(next_page)
# Make sure page request is an int. If not, deliver first page.
try:
current_page = int(request.GET.get('page', '1'))
per_page = int(request.GET.get('per_page', '100'))
except ValueError:
current_page = 1
per_page = 100
start = per_page * (current_page - 1)
limit = per_page + 1
if is_org_context(request):
org_id = request.user.org.org_id
events = get_file_audit_events_by_path(None, org_id, repo_id, path, start, limit)
else:
events = get_file_audit_events_by_path(None, 0, repo_id, path, start, limit)
events = events if events else []
if len(events) == per_page + 1:
page_next = True
else:
page_next = False
events = events[:per_page]
for ev in events:
ev.repo = get_repo(ev.repo_id)
ev.filename = os.path.basename(ev.file_path)
ev.time = utc_to_local(ev.timestamp)
ev.event_type, ev.show_device = generate_file_audit_event_type(ev)
filename = os.path.basename(path)
zipped = gen_path_link(path, repo.name)
extra_href = "&p=%s" % quote(path)
return render(request, 'file_access.html', {
'repo': repo,
'path': path,
'filename': filename,
'zipped': zipped,
'events': events,
'extra_href': extra_href,
'current_page': current_page,
'prev_page': current_page-1,
'next_page': current_page+1,
'per_page': per_page,
'page_next': page_next,
})
def view_media_file_via_share_link(request):
image_path = request.GET.get('path', '')
token = request.GET.get('token', '')
if not image_path or not token:
return HttpResponseBadRequest('invalid params')
file_share = FileShare.objects.get_valid_file_link_by_token(token)
if not file_share:
err_msg = 'Share link does not exist'
return render_error(request, err_msg)
if file_share.is_expired():
err_msg = 'Share link has expired'
return render_error(request, err_msg)
shared_file_name = os.path.basename(file_share.path)
file_type, file_ext = get_file_type_and_ext(shared_file_name)
if file_type != MARKDOWN:
err_msg = 'Invalid file type'
return render_error(request, err_msg)
# recourse check
repo_id = file_share.repo_id
repo = get_repo(repo_id)
if not repo:
return render_error(request, 'Repo does not exist')
file_id = seafile_api.get_file_id_by_path(repo_id, file_share.path)
if not file_id:
return render_error(request, 'File does not exist')
# read file from cache, if hit
err_msg, file_content = get_file_content_from_cache(file_id, repo_id, shared_file_name)
if err_msg:
return render_error(request, err_msg)
# If the image does not exist in markdown
serviceURL = get_service_url().rstrip('/')
image_file_name = os.path.basename(image_path)
# Translation ( ')'
image_file_name = image_file_name.replace('(', '\(')
image_file_name = image_file_name.replace(')', '\)')
encoded_image_file_name = urllib.parse.quote(image_file_name)
p = re.compile('(%s)/lib/(%s)/file(.*?)%s\?raw=1' % (serviceURL, repo_id, encoded_image_file_name))
result = re.search(p, file_content)
if not result:
return render_error(request, 'Image does not exist')
# get image
obj_id = seafile_api.get_file_id_by_path(repo_id, image_path)
if not obj_id:
return render_error(request, 'Image does not exist')
access_token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'view', '', use_onetime=False)
dl_or_raw_url = gen_file_get_url(access_token, image_file_name)
return HttpResponseRedirect(dl_or_raw_url)
def view_media_file_via_public_wiki(request):
image_path = request.GET.get('path', '')
slug = request.GET.get('slug', '')
if not image_path or not slug:
return HttpResponseBadRequest('invalid params')
# check file type
image_file_name = os.path.basename(image_path)
file_type, file_ext = get_file_type_and_ext(image_file_name)
if file_type != IMAGE:
err_msg = 'Invalid file type'
return render_error(request, err_msg)
# get wiki object or 404
try:
wiki = Wiki.objects.get(slug=slug)
except Wiki.DoesNotExist:
err_msg = "Wiki not found."
return render_error(request, err_msg)
if wiki.permission != 'public':
return render_permission_error(request, 'Permission denied')
# recourse check
repo_id = wiki.repo_id
repo = seafile_api.get_repo(repo_id)
if not repo:
return render_error(request, 'Repo does not exist')
# get image
obj_id = seafile_api.get_file_id_by_path(repo_id, image_path)
if not obj_id:
return render_error(request, 'Image does not exist')
access_token = seafile_api.get_fileserver_access_token(repo_id,
obj_id, 'view', '', use_onetime=False)
dl_or_raw_url = gen_file_get_url(access_token, image_file_name)
return HttpResponseRedirect(dl_or_raw_url)
def get_file_content_from_cache(file_id, repo_id, file_name):
err_msg = ''
file_content = ''
cache_key = normalize_cache_key(file_id)
# read file from cache, if hit
file_content = cache.get(cache_key)
if not file_content:
# otherwise, read file from database and update cache
access_token = seafile_api.get_fileserver_access_token(repo_id,
file_id, 'view', '', use_onetime=False)
if not access_token:
err_msg = 'Unable to view file'
return err_msg, file_content
file_raw_path = gen_inner_file_get_url(access_token, file_name)
err_msg, file_content, encode = repo_file_get(file_raw_path, 'auto')
cache.set(cache_key, file_content, 24 * 60 * 60)
return err_msg, file_content
@login_required
@repo_passwd_set_required
def view_sdoc_revision(request, repo_id, revision_id):
username = request.user.username
# resource check
repo = seafile_api.get_repo(repo_id)
if not repo:
raise Http404
revision = SeadocRevision.objects.get_by_revision_id(repo_id, revision_id)
if not revision:
return render_error(request, 'revision not found')
is_published = revision.is_published
if is_published:
origin_file_uuid = revision.origin_doc_uuid
origin_uuid_map = FileUUIDMap.objects.get_fileuuidmap_by_uuid(origin_file_uuid)
if not origin_uuid_map:
return render_error(request, _('Origin file does not exist'))
parent_dir = origin_uuid_map.parent_path
filename = origin_uuid_map.filename
path = posixpath.join(parent_dir, filename)
file_id = seafile_api.get_file_id_by_path(repo_id, path)
if not file_id:
return render_error(request, _('File does not exist'))
else:
uuid_map = FileUUIDMap.objects.filter(uuid=revision.doc_uuid).first()
if not uuid_map:
return render_error(request, _('File does not exist'))
parent_dir = uuid_map.parent_path
filename = uuid_map.filename
path = posixpath.join(parent_dir, filename)
file_id = seafile_api.get_file_id_by_path(repo_id, path)
if not file_id:
return render_error(request, _('File does not exist'))
# permission check
permission = check_folder_permission(request, repo_id, parent_dir)
if not permission:
return convert_repo_path_when_can_not_view_file(request, repo_id, path)
org_id = request.user.org.org_id if is_org_context(request) else -1
# basic file info
return_dict = {
'is_pro': is_pro_version(),
'repo': repo,
'file_id': file_id,
'last_commit_id': repo.head_cmmt_id,
'is_repo_owner': is_repo_owner(request, repo_id, username),
'path': path,
'parent_dir': parent_dir,
'filename': filename,
'file_perm': permission,
'highlight_keyword': settings.HIGHLIGHT_KEYWORD,
'enable_file_comment': settings.ENABLE_FILE_COMMENT,
'enable_watermark': ENABLE_WATERMARK,
'share_link_force_use_password': SHARE_LINK_FORCE_USE_PASSWORD,
'share_link_password_min_length': SHARE_LINK_PASSWORD_MIN_LENGTH,
'share_link_password_strength_level': SHARE_LINK_PASSWORD_STRENGTH_LEVEL,
'share_link_expire_days_default': SHARE_LINK_EXPIRE_DAYS_DEFAULT,
'share_link_expire_days_min': SHARE_LINK_EXPIRE_DAYS_MIN,
'share_link_expire_days_max': SHARE_LINK_EXPIRE_DAYS_MAX,
'can_download_file': parse_repo_perm(permission).can_download,
'seafile_collab_server': SEAFILE_COLLAB_SERVER,
}
is_locked = False
locked_by_me = False
# check whether file is starred
if not is_published:
is_starred = is_file_starred(username, repo_id, path, org_id)
return_dict['is_starred'] = is_starred
# check file lock info
try:
is_locked, locked_by_me = check_file_lock(repo_id, path, username)
except Exception as e:
logger.error(e)
is_locked = False
locked_by_me = False
if is_pro_version() and permission == 'rw':
can_lock_unlock_file = True
else:
can_lock_unlock_file = False
return_dict['file_locked'] = is_locked
return_dict['locked_by_me'] = locked_by_me
return_dict['can_lock_unlock_file'] = can_lock_unlock_file
return_dict['can_share_file'] = False
return_dict['filetype'] = 'SDoc'
file_uuid = revision.doc_uuid
return_dict['file_uuid'] = file_uuid
return_dict['seadoc_server_url'] = SEADOC_SERVER_URL
return_dict['assets_url'] = '/api/v2.1/seadoc/download-image/' + (origin_file_uuid if is_published else file_uuid)
can_edit_file = not is_published
if parse_repo_perm(permission).can_edit_on_web is False:
can_edit_file = False
elif is_locked and not locked_by_me:
can_edit_file = False
seadoc_perm = 'rw' if can_edit_file else 'r'
return_dict['can_edit_file'] = can_edit_file
return_dict['seadoc_access_token'] = gen_seadoc_access_token(file_uuid, filename, username, permission=seadoc_perm)
# revision
revision_info = is_seadoc_revision(file_uuid, revision)
return_dict.update(revision_info)
send_file_access_msg(request, repo, path, 'web')
if is_published:
return render(request, 'sdoc_published_revision_file_view.html', return_dict)
return render(request, 'sdoc_file_view_react.html', return_dict)