From ff1311339107e422492389f7165d4c7462593cfd Mon Sep 17 00:00:00 2001 From: lian Date: Tue, 2 Mar 2021 15:53:44 +0800 Subject: [PATCH 1/3] send to contact email when user forget password on login page (#4822) Co-authored-by: lian --- seahub/auth/forms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/seahub/auth/forms.py b/seahub/auth/forms.py index 3c639da8b0..52817b1757 100644 --- a/seahub/auth/forms.py +++ b/seahub/auth/forms.py @@ -7,6 +7,7 @@ from django.utils.http import int_to_base36 from seaserv import ccnet_api from seahub.base.accounts import User +from seahub.base.templatetags.seahub_tags import email2contact_email from seahub.auth import authenticate from seahub.auth.tokens import default_token_generator from seahub.options.models import UserOptions @@ -152,7 +153,8 @@ class PasswordResetForm(forms.Form): } send_html_email(_("Reset Password on %s") % site_name, - email_template_name, c, None, [user.username]) + email_template_name, c, None, + [email2contact_email(user.username)]) class SetPasswordForm(forms.Form): """ From ae8e1ac84988ab59f3e6748340040fae8922cb57 Mon Sep 17 00:00:00 2001 From: lian Date: Tue, 2 Mar 2021 21:24:50 +0800 Subject: [PATCH 2/3] search user by login id (#4823) Co-authored-by: lian --- seahub/api2/endpoints/search_user.py | 44 ++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/seahub/api2/endpoints/search_user.py b/seahub/api2/endpoints/search_user.py index 32314af6df..b904617cb4 100644 --- a/seahub/api2/endpoints/search_user.py +++ b/seahub/api2/endpoints/search_user.py @@ -117,7 +117,7 @@ class SearchUser(APIView): # search user from user's contacts email_list += search_user_when_global_address_book_disabled(request, q) - ## search finished, now filter out some users + # search finished, now filter out some users # remove duplicate emails # get_emailusers_in_list can only accept 20 users at most @@ -128,7 +128,8 @@ class SearchUser(APIView): # remove nonexistent or inactive user email_list_json = json.dumps(email_list) user_obj_list = ccnet_api.get_emailusers_in_list('DB', email_list_json) + \ - ccnet_api.get_emailusers_in_list('LDAP', email_list_json) + ccnet_api.get_emailusers_in_list('LDAP', email_list_json) + for user_obj in user_obj_list: if user_obj.is_active: email_result.append(user_obj.email) @@ -136,7 +137,7 @@ class SearchUser(APIView): if django_settings.ENABLE_ADDRESSBOOK_OPT_IN: # get users who has setted to show in address book listed_users = Profile.objects.filter(list_in_address_book=True).values('user') - listed_user_list = [ u['user'] for u in listed_users ] + listed_user_list = [u['user'] for u in listed_users] email_result = list(set(email_result) & set(listed_user_list)) @@ -164,6 +165,7 @@ class SearchUser(APIView): return Response({"users": formated_result}) + def format_searched_user_result(request, users, size): results = [] @@ -178,6 +180,7 @@ def format_searched_user_result(request, users, size): return results + def search_user_from_ccnet(q): """ Return 10 items at most. """ @@ -204,13 +207,15 @@ def search_user_from_ccnet(q): return email_list + def search_user_from_profile(q): """ Return 10 items at most. """ # 'nickname__icontains' for search by nickname # 'contact_email__icontains' for search by contact email users = Profile.objects.filter(Q(nickname__icontains=q) | \ - Q(contact_email__icontains=q)).values('user')[:10] + Q(contact_email__icontains=q) | \ + Q(login_id__icontains=q)).values('user')[:10] email_list = [] for user in users: @@ -218,12 +223,14 @@ def search_user_from_profile(q): return email_list + def search_user_from_profile_with_limits(q, limited_emails): """ Return 10 items at most. """ # search within limited_emails - users = Profile.objects.filter(Q(user__in=limited_emails) & - (Q(nickname__icontains=q) | Q(contact_email__icontains=q))).values('user')[:10] + users = Profile.objects.filter(Q(user__in=limited_emails) & (Q(nickname__icontains=q) | \ + Q(contact_email__icontains=q) | \ + Q(login_id__icontains=q))).values('user')[:10] email_list = [] for user in users: @@ -231,7 +238,9 @@ def search_user_from_profile_with_limits(q, limited_emails): return email_list + def search_user_when_global_address_book_disabled(request, q): + """ Return 10 items at most. """ @@ -242,6 +251,7 @@ def search_user_when_global_address_book_disabled(request, q): # get user's contact list contacts = Contact.objects.get_contacts_by_user(username) for contact in contacts: + # search user from contact list if q in contact.contact_email: email_list.append(contact.contact_email) @@ -254,13 +264,21 @@ def search_user_when_global_address_book_disabled(request, q): email_list += search_user_from_profile_with_limits(q, limited_emails) current_user = User.objects.get(email=username) - if is_valid_email(q) and current_user.role.lower() != 'guest': - # if `q` is a valid email and current is not a guest user - email_list.append(q) + if current_user.role.lower() != 'guest': - # get user whose `contact_email` is `q` - users = Profile.objects.filter(contact_email=q).values('user') - for user in users: - email_list.append(user['user']) + if is_valid_email(q): + + # if `q` is a valid email + email_list.append(q) + + # get user whose `contact_email` is `q` + users = Profile.objects.filter(contact_email=q).values('user') + for user in users: + email_list.append(user['user']) + + # get user whose `login_id` is `q` + username_by_login_id = Profile.objects.get_username_by_login_id(q) + if username_by_login_id: + email_list.append(username_by_login_id) return email_list From 504c53f1c2a49abce36a005b3e51dcf1742de308 Mon Sep 17 00:00:00 2001 From: lian Date: Wed, 17 Mar 2021 12:23:42 +0800 Subject: [PATCH 3/3] add ONLINE_OFFICE_LOCK_REFRESH_INTERVAL (#4826) * unlock file when saving file via onlyoffice; (#4757) * unlock file when saving file via onlyoffice * add log when no doc_key in cache and locked by online office Co-authored-by: lian * update lock file logic when edit file via OnlyOffice * update lock file logic when edit file via OOS Co-authored-by: lian --- seahub/api2/endpoints/file.py | 41 ++++++- seahub/api2/endpoints/share_links.py | 40 +++++-- seahub/onlyoffice/utils.py | 8 ++ seahub/onlyoffice/views.py | 74 +++++++----- .../snippets/wopi_onlyoffice_js.html | 59 ---------- seahub/templates/view_file_onlyoffice.html | 63 ++++++++++- seahub/templates/view_file_wopi.html | 3 +- seahub/views/file.py | 13 ++- seahub/wopi/views.py | 105 +++++++++++++----- 9 files changed, 271 insertions(+), 135 deletions(-) delete mode 100644 seahub/templates/snippets/wopi_onlyoffice_js.html diff --git a/seahub/api2/endpoints/file.py b/seahub/api2/endpoints/file.py index b223b8c204..c7a59f4798 100644 --- a/seahub/api2/endpoints/file.py +++ b/seahub/api2/endpoints/file.py @@ -1,5 +1,6 @@ # Copyright (c) 2012-2016 Seafile Ltd. import os +import time import logging import posixpath import requests @@ -27,8 +28,7 @@ from seahub.constants import PERMISSION_READ_WRITE from seahub.utils.repo import parse_repo_perm, is_repo_admin, is_repo_owner from seahub.utils.file_types import MARKDOWN, TEXT -from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, \ - FILE_LOCK_EXPIRATION_DAYS, OFFICE_TEMPLATE_ROOT +from seahub.settings import MAX_UPLOAD_FILE_NAME_LEN, OFFICE_TEMPLATE_ROOT from seahub.drafts.models import Draft from seahub.drafts.utils import is_draft_file, get_file_draft @@ -569,10 +569,24 @@ class FileView(APIView): error_msg = _("File is locked") return api_error(status.HTTP_400_BAD_REQUEST, error_msg) - # lock file - expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS) + expire = request.data.get('expire', 0) try: - seafile_api.lock_file(repo_id, path, username, expire) + 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) + + # lock file + try: + if expire > 0: + seafile_api.lock_file(repo_id, path, username, + int(time.time()) + expire) + else: + seafile_api.lock_file(repo_id, path, username) except SearpcError as e: logger.error(e) error_msg = 'Internal Server Error' @@ -604,10 +618,25 @@ class FileView(APIView): error_msg = _("File is not locked.") return api_error(status.HTTP_400_BAD_REQUEST, error_msg) + 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 locked_by_me or locked_by_online_office: # refresh lock file try: - seafile_api.refresh_file_lock(repo_id, path) + if expire > 0: + seafile_api.refresh_file_lock(repo_id, path, + int(time.time()) + expire) + else: + seafile_api.refresh_file_lock(repo_id, path) except SearpcError as e: logger.error(e) error_msg = 'Internal Server Error' diff --git a/seahub/api2/endpoints/share_links.py b/seahub/api2/endpoints/share_links.py index aab97a53d7..3fa939ab82 100644 --- a/seahub/api2/endpoints/share_links.py +++ b/seahub/api2/endpoints/share_links.py @@ -2,6 +2,7 @@ import os import stat import json +import time import logging import posixpath from constance import config @@ -112,6 +113,7 @@ def get_share_link_info(fileshare): return data + def check_permissions_arg(request): permissions = request.data.get('permissions', '') @@ -140,6 +142,7 @@ def check_permissions_arg(request): return perm + class ShareLinks(APIView): authentication_classes = (TokenAuthentication, SessionAuthentication) @@ -209,7 +212,8 @@ class ShareLinks(APIView): try: permission = seafile_api.check_permission_by_path(repo_id, - folder_path, fileshare.username) + folder_path, + fileshare.username) except Exception as e: logger.error(e) permission = '' @@ -219,8 +223,7 @@ class ShareLinks(APIView): links_info = [] for fs in fileshares: link_info = get_share_link_info(fs) - link_info['repo_folder_permission'] = \ - repo_folder_permission_dict.get(link_info['repo_id'], '') + link_info['repo_folder_permission'] = repo_folder_permission_dict.get(link_info['repo_id'], '') links_info.append(link_info) if len(links_info) == 1: @@ -391,12 +394,13 @@ class ShareLinks(APIView): 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) + password, expire_date, + permission=perm, org_id=org_id) link_info = get_share_link_info(fs) return Response(link_info) + class ShareLink(APIView): authentication_classes = (TokenAuthentication, SessionAuthentication) @@ -467,7 +471,8 @@ class ShareLink(APIView): username = request.user.username repo_folder_permission = seafile_api.check_permission_by_path(repo_id, - folder_path, username) + folder_path, + username) if not repo_folder_permission: error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) @@ -551,6 +556,17 @@ class ShareLinkOnlineOfficeLock(APIView): 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.' @@ -571,7 +587,8 @@ class ShareLinkOnlineOfficeLock(APIView): 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: + parent_dir, + shared_by) != PERMISSION_READ_WRITE: error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) @@ -583,9 +600,11 @@ class ShareLinkOnlineOfficeLock(APIView): 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) + seafile_api.refresh_file_lock(repo_id, path, + int(time.time()) + expire) except SearpcError as e: logger.error(e) error_msg = 'Internal Server Error' @@ -635,7 +654,7 @@ class ShareLinkDirents(APIView): # resource check try: - share_link= FileShare.objects.get(token=token) + 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) @@ -671,7 +690,8 @@ class ShareLinkDirents(APIView): 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) + current_commit.id, + path, -1, -1) except Exception as e: logger.error(e) error_msg = 'Internal Server Error' diff --git a/seahub/onlyoffice/utils.py b/seahub/onlyoffice/utils.py index 708c78feae..9717a2df07 100644 --- a/seahub/onlyoffice/utils.py +++ b/seahub/onlyoffice/utils.py @@ -1,6 +1,7 @@ import os import json import hashlib +import logging import urllib.parse import posixpath @@ -13,11 +14,15 @@ from seaserv import seafile_api from seahub.base.templatetags.seahub_tags import email2nickname from seahub.utils import get_file_type_and_ext, gen_file_get_url, \ get_site_scheme_and_netloc, normalize_cache_key +from seahub.utils.file_op import if_locked_by_online_office from seahub.settings import ENABLE_WATERMARK from seahub.onlyoffice.settings import ONLYOFFICE_APIJS_URL, \ ONLYOFFICE_FORCE_SAVE, ONLYOFFICE_JWT_SECRET +# Get an instance of a logger +logger = logging.getLogger(__name__) + def generate_onlyoffice_cache_key(repo_id, file_path): prefix = "ONLYOFFICE_" @@ -68,6 +73,9 @@ def get_onlyoffice_dict(request, username, repo_id, file_path, file_id='', if not doc_key: doc_key = cache.get(cache_key) + if not doc_key and if_locked_by_online_office(repo_id, file_path): + logger.error('no doc_key in cache and locked by online office') + if not doc_key: info_bytes = force_bytes(origin_repo_id + origin_file_path + file_id) doc_key = hashlib.md5(info_bytes).hexdigest()[:20] diff --git a/seahub/onlyoffice/views.py b/seahub/onlyoffice/views.py index 25309769cb..3c5510579d 100644 --- a/seahub/onlyoffice/views.py +++ b/seahub/onlyoffice/views.py @@ -11,11 +11,14 @@ from seaserv import seafile_api from seahub.onlyoffice.settings import VERIFY_ONLYOFFICE_CERTIFICATE from seahub.onlyoffice.utils import generate_onlyoffice_cache_key -from seahub.utils import gen_inner_file_upload_url +from seahub.utils import gen_inner_file_upload_url, is_pro_version +from seahub.utils.file_op import if_locked_by_online_office + # Get an instance of a logger logger = logging.getLogger(__name__) + @csrf_exempt def onlyoffice_editor_callback(request): """ Callback func of OnlyOffice. @@ -31,13 +34,13 @@ def onlyoffice_editor_callback(request): # otherwise the document editor will display an error message. return HttpResponse('{"error": 0}') - #### body info of POST rquest when open file on browser + # body info of POST rquest when open file on browser # {u'actions': [{u'type': 1, u'userid': u'uid-1527736776860'}], # u'key': u'8062bdccf9b4cf809ae3', # u'status': 1, # u'users': [u'uid-1527736776860']} - #### body info of POST rquest when close file's web page (save file) + # body info of POST rquest when close file's web page (save file) # {u'actions': [{u'type': 0, u'userid': u'uid-1527736951523'}], # u'changesurl': u'...', # u'history': {u'changes': [{u'created': u'2018-05-31 03:17:17', @@ -74,9 +77,21 @@ def onlyoffice_editor_callback(request): post_data = json.loads(request.body) status = int(post_data.get('status', -1)) - # When forcesave is initiated, document editing service performs request to - # the callback handler with the link to the document as the url parameter and - # with the 6 value for the status parameter. + if status not in (1, 2, 4, 6): + logger.error('onlyoffice status invalid: {}'.format(status)) + return HttpResponse('{"error": 0}') + + # get file basic info + doc_key = post_data.get('key') + doc_info = json.loads(cache.get("ONLYOFFICE_%s" % doc_key)) + + repo_id = doc_info['repo_id'] + file_path = doc_info['file_path'] + username = doc_info['username'] + + cache_key = generate_onlyoffice_cache_key(repo_id, file_path) + + # save file if status in (2, 6): # Defines the link to the edited document to be saved with the document storage service. @@ -87,27 +102,11 @@ def onlyoffice_editor_callback(request): logger.error('[OnlyOffice] No response from file content url.') return HttpResponse('{"error": 0}') - # get file basic info - doc_key = post_data.get('key') - doc_info = json.loads(cache.get("ONLYOFFICE_%s" % doc_key)) - - repo_id = doc_info['repo_id'] - file_path = doc_info['file_path'] - username = doc_info['username'] - - cache_key = generate_onlyoffice_cache_key(repo_id, file_path) - # cache document key when forcesave - if status == 6: - cache.set(cache_key, doc_key) - - # remove document key from cache when document is ready for saving - # no one is editting - if status == 2: - cache.delete(cache_key) - - fake_obj_id = {'online_office_update': True,} + fake_obj_id = {'online_office_update': True} update_token = seafile_api.get_fileserver_access_token(repo_id, - json.dumps(fake_obj_id), 'update', username) + json.dumps(fake_obj_id), + 'update', + username) if not update_token: logger.error('[OnlyOffice] No fileserver access token.') @@ -124,4 +123,27 @@ def onlyoffice_editor_callback(request): update_url = gen_inner_file_upload_url('update-api', update_token) requests.post(update_url, files=files) + # 2 - document is ready for saving, + if status == 2: + + cache.delete(cache_key) + cache.delete("ONLYOFFICE_%s" % doc_key) + + if is_pro_version() and if_locked_by_online_office(repo_id, file_path): + seafile_api.unlock_file(repo_id, file_path) + + # 6 - document is being edited, but the current document state is saved, + if status == 6: + # cache document key when forcesave + cache.set(cache_key, doc_key) + + # 4 - document is closed with no changes, + if status == 4: + + cache.delete(cache_key) + cache.delete("ONLYOFFICE_%s" % doc_key) + + if is_pro_version() and if_locked_by_online_office(repo_id, file_path): + seafile_api.unlock_file(repo_id, file_path) + return HttpResponse('{"error": 0}') diff --git a/seahub/templates/snippets/wopi_onlyoffice_js.html b/seahub/templates/snippets/wopi_onlyoffice_js.html deleted file mode 100644 index 95330ed847..0000000000 --- a/seahub/templates/snippets/wopi_onlyoffice_js.html +++ /dev/null @@ -1,59 +0,0 @@ -{% load i18n %} - -{% if enable_watermark %} - - -{% endif %} diff --git a/seahub/templates/view_file_onlyoffice.html b/seahub/templates/view_file_onlyoffice.html index d90f20ea19..d498351be9 100644 --- a/seahub/templates/view_file_onlyoffice.html +++ b/seahub/templates/view_file_onlyoffice.html @@ -53,6 +53,67 @@ html, body { padding:0; margin:0; height:100%; } }; var docEditor = new DocsAPI.DocEditor("placeholder", config); -{% include 'snippets/wopi_onlyoffice_js.html' %} + +{% if enable_watermark %} + + +{% endif %} diff --git a/seahub/templates/view_file_wopi.html b/seahub/templates/view_file_wopi.html index c17a1a7512..af5f265643 100644 --- a/seahub/templates/view_file_wopi.html +++ b/seahub/templates/view_file_wopi.html @@ -45,6 +45,7 @@ document.getElementById('office_form').submit(); document.getElementById('office_frame').className = ''; -{% include 'snippets/wopi_onlyoffice_js.html' %} + + diff --git a/seahub/views/file.py b/seahub/views/file.py index 75f2010b2b..eb27dd50d8 100644 --- a/seahub/views/file.py +++ b/seahub/views/file.py @@ -7,6 +7,7 @@ view_snapshot_file, view_shared_file, etc. import os import json +import time import stat import urllib.request, urllib.error, urllib.parse import chardet @@ -792,9 +793,11 @@ def view_lib_file(request, repo_id, path): if is_pro_version() and can_edit: try: if not is_locked: - seafile_api.lock_file(repo_id, path, ONLINE_OFFICE_LOCK_OWNER, 0) + 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) + seafile_api.refresh_file_lock(repo_id, path, + int(time.time()) + 40 * 60) except Exception as e: logger.error(e) @@ -1166,9 +1169,11 @@ def view_shared_file(request, fileshare): locked_by_online_office = if_locked_by_online_office(repo_id, path) try: if not is_locked: - seafile_api.lock_file(repo_id, path, ONLINE_OFFICE_LOCK_OWNER, 0) + 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) + seafile_api.refresh_file_lock(repo_id, path, + int(time.time()) + 40 * 60) except Exception as e: logger.error(e) diff --git a/seahub/wopi/views.py b/seahub/wopi/views.py index 9df8bae2f3..c3f7c2cca8 100644 --- a/seahub/wopi/views.py +++ b/seahub/wopi/views.py @@ -3,11 +3,13 @@ import os import json +import time import logging -import urllib.request, urllib.error, urllib.parse +import urllib.request +import urllib.error +import urllib.parse import requests import hashlib -import urllib.parse import posixpath import datetime @@ -21,8 +23,10 @@ from seaserv import seafile_api from seahub.base.accounts import User, ANONYMOUS_EMAIL from seahub.base.templatetags.seahub_tags import email2nickname -from seahub.utils import gen_inner_file_get_url, \ - gen_inner_file_upload_url, is_pro_version +from seahub.utils import gen_inner_file_get_url, gen_inner_file_upload_url, \ + is_pro_version +from seahub.utils.file_op import ONLINE_OFFICE_LOCK_OWNER, \ + if_locked_by_online_office from seahub.settings import SITE_ROOT from seahub.wopi.utils import get_file_info_by_token @@ -32,13 +36,13 @@ json_content_type = 'application/json; charset=utf-8' WOPI_LOCK_EXPIRATION = 30 * 60 + def generate_file_lock_key_value(request): token = request.GET.get('access_token', None) - info_dict = get_file_info_by_token(token) repo_id = info_dict['repo_id'] - file_path= info_dict['file_path'] + file_path = info_dict['file_path'] repo = seafile_api.get_repo(repo_id) if repo.is_virtual: @@ -55,25 +59,61 @@ def generate_file_lock_key_value(request): return lock_cache_key, x_wopi_lock + def lock_file(request): + key, value = generate_file_lock_key_value(request) cache.set(key, value, WOPI_LOCK_EXPIRATION) + token = request.GET.get('access_token', None) + info_dict = get_file_info_by_token(token) + repo_id = info_dict['repo_id'] + file_path = info_dict['file_path'] + seafile_api.lock_file(repo_id, file_path, ONLINE_OFFICE_LOCK_OWNER, + int(time.time()) + 40 * 60) + + def unlock_file(request): + key, value = generate_file_lock_key_value(request) cache.delete(key) + token = request.GET.get('access_token', None) + info_dict = get_file_info_by_token(token) + repo_id = info_dict['repo_id'] + file_path = info_dict['file_path'] + seafile_api.unlock_file(repo_id, file_path) + + def refresh_file_lock(request): - lock_file(request) + + key, value = generate_file_lock_key_value(request) + cache.set(key, value, WOPI_LOCK_EXPIRATION) + + token = request.GET.get('access_token', None) + info_dict = get_file_info_by_token(token) + repo_id = info_dict['repo_id'] + file_path = info_dict['file_path'] + seafile_api.refresh_file_lock(repo_id, file_path, + int(time.time()) + 40 * 60) + def file_is_locked(request): - key, value = generate_file_lock_key_value(request) - return True if cache.get(key, '') else False + + token = request.GET.get('access_token', None) + info_dict = get_file_info_by_token(token) + repo_id = info_dict['repo_id'] + file_path = info_dict['file_path'] + + return if_locked_by_online_office(repo_id, file_path) + def get_current_lock_id(request): + key, value = generate_file_lock_key_value(request) return cache.get(key, '') + def access_token_check(func): def _decorated(view, request, file_id, *args, **kwargs): @@ -87,17 +127,17 @@ def access_token_check(func): info_dict = get_file_info_by_token(token) if not info_dict: logger.error('Get wopi cache value failed: wopi_access_token_%s.' % token) - return HttpResponse(json.dumps({}), status=404, - content_type=json_content_type) + return HttpResponse(json.dumps({}), status=404, content_type=json_content_type) request_user = info_dict['request_user'] repo_id = info_dict['repo_id'] - file_path= info_dict['file_path'] + file_path = info_dict['file_path'] obj_id = info_dict['obj_id'] if not request_user or not repo_id or not file_path: - logger.error('File info invalid, user: %s, repo_id: %s, path: %s.' \ - % (request_user, repo_id, file_path)) + logger.error('File info invalid, user: %s, repo_id: %s, path: %s.' % (request_user, + repo_id, + file_path)) return HttpResponse(json.dumps({}), status=404, content_type=json_content_type) @@ -141,7 +181,7 @@ class WOPIFilesView(APIView): info_dict = get_file_info_by_token(token) request_user = info_dict['request_user'] repo_id = info_dict['repo_id'] - file_path= info_dict['file_path'] + file_path = info_dict['file_path'] obj_id = info_dict['obj_id'] can_edit = info_dict['can_edit'] can_download = info_dict['can_download'] @@ -153,8 +193,7 @@ class WOPIFilesView(APIView): obj_id = seafile_api.get_file_id_by_path(repo_id, file_path) try: - file_size = seafile_api.get_file_size(repo.store_id, - repo.version, obj_id) + file_size = seafile_api.get_file_size(repo.store_id, repo.version, obj_id) except SearpcError as e: logger.error(e) return HttpResponse(json.dumps({}), status=500, @@ -222,7 +261,8 @@ class WOPIFilesView(APIView): def post(self, request, file_id, format=None): response_409 = HttpResponse(json.dumps({}), - status=409, content_type=json_content_type) + status=409, + content_type=json_content_type) x_wopi_override = request.META.get('HTTP_X_WOPI_OVERRIDE', None) x_wopi_lock = request.META.get('HTTP_X_WOPI_LOCK', None) @@ -238,7 +278,9 @@ class WOPIFilesView(APIView): # If the file is currently locked # and the X-WOPI-OldLock value does NOT match the lock currently on the file # the host must return a “lock mismatch” response (409 Conflict) - # and include an X-WOPI-Lock response header containing the value of the current lock on the file + # and include an X-WOPI-Lock response header + # containing the value of the current lock on the file + response_409['X-WOPI-Lock'] = current_lock_id return response_409 else: @@ -317,7 +359,9 @@ class WOPIFilesView(APIView): logger.info('HTTP_X_WOPI_LOCK: %s' % x_wopi_lock) logger.info('HTTP_X_WOPI_OLDLOCK: %s' % x_wopi_oldlock) return HttpResponse(json.dumps({'error_msg': 'HTTP_X_WOPI_OVERRIDE invalid'}), - status=401, content_type=json_content_type) + status=401, + content_type=json_content_type) + class WOPIFilesContentsView(APIView): @@ -329,7 +373,7 @@ class WOPIFilesContentsView(APIView): token = request.GET.get('access_token', None) info_dict = get_file_info_by_token(token) repo_id = info_dict['repo_id'] - file_path= info_dict['file_path'] + file_path = info_dict['file_path'] obj_id = info_dict['obj_id'] if not obj_id: @@ -339,15 +383,18 @@ class WOPIFilesContentsView(APIView): file_name = os.path.basename(file_path) try: fileserver_token = seafile_api.get_fileserver_access_token(repo_id, - obj_id, 'view', '', use_onetime = False) + obj_id, + 'view', + '', + use_onetime=False) except SearpcError as e: logger.error(e) return HttpResponse(json.dumps({}), status=500, - content_type=json_content_type) + content_type=json_content_type) if not fileserver_token: return HttpResponse(json.dumps({}), status=500, - content_type=json_content_type) + content_type=json_content_type) inner_path = gen_inner_file_get_url(fileserver_token, file_name) @@ -367,19 +414,21 @@ class WOPIFilesContentsView(APIView): info_dict = get_file_info_by_token(token) request_user = info_dict['request_user'] repo_id = info_dict['repo_id'] - file_path= info_dict['file_path'] + file_path = info_dict['file_path'] try: file_obj = request.read() # get file update url - fake_obj_id = {'online_office_update': True,} + fake_obj_id = {'online_office_update': True} token = seafile_api.get_fileserver_access_token(repo_id, - json.dumps(fake_obj_id), 'update', request_user) + json.dumps(fake_obj_id), + 'update', + request_user) if not token: return HttpResponse(json.dumps({}), status=500, - content_type=json_content_type) + content_type=json_content_type) update_url = gen_inner_file_upload_url('update-api', token)