def test_can_unlock_file(self): if not LOCAL_PRO_DEV_ENV: return self.login_as(self.user) # lock file for test seafile_api.lock_file(self.repo_id, self.file_path.lstrip('/'), self.user.username, -1) # check file has been locked when init return_value = seafile_api.check_file_lock(self.repo_id, self.file_path.lstrip('/'), self.user.username) assert return_value == 2 # unlock file data = 'operation=unlock' resp = self.client.put(self.url + '?p=' + self.file_path, data, 'application/x-www-form-urlencoded') self.assertEqual(200, resp.status_code) # check file has been unlocked return_value = seafile_api.check_file_lock(self.repo_id, self.file_path.lstrip('/'), self.user.username) assert return_value == 0
def test_can_get_file_with_lock_info(self): if not LOCAL_PRO_DEV_ENV: return self.login_as(self.user) # no lock owner info returned resp = self.client.get(self.url + '?t=f') self.assertEqual(200, resp.status_code) json_resp = json.loads(resp.content) assert len(json_resp['dirent_list']) == 1 assert json_resp['dirent_list'][0]['type'] == 'file' assert json_resp['dirent_list'][0]['name'] == self.file_name assert json_resp['dirent_list'][0]['lock_owner'] == '' # lock file seafile_api.lock_file(self.repo_id, self.file_path, self.admin_name, 1) # return lock owner info resp = self.client.get(self.url + '?t=f') self.assertEqual(200, resp.status_code) json_resp = json.loads(resp.content) assert len(json_resp['dirent_list']) == 1 assert json_resp['dirent_list'][0]['type'] == 'file' assert json_resp['dirent_list'][0]['name'] == self.file_name assert json_resp['dirent_list'][0]['lock_owner'] == self.admin_name
def put(self, request, repo_id, format=None): """ Currently only for lock and unlock file operation. Permission checking: 1. user with 'rw' permission for current file; """ if not is_pro_version(): error_msg = 'file lock feature only supported in professional edition.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) # argument check path = request.GET.get('p', None) if not path: error_msg = 'p invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) operation = request.data.get('operation', None) if not operation: error_msg = 'operation invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) operation = operation.lower() if operation not in ('lock', 'unlock'): error_msg = "operation can only be 'lock', or 'unlock'." return api_error(status.HTTP_400_BAD_REQUEST, error_msg) # resource check repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) file_id = seafile_api.get_file_id_by_path(repo_id, path) if not file_id: error_msg = 'File %s not found.' % path return api_error(status.HTTP_404_NOT_FOUND, error_msg) # permission check parent_dir = os.path.dirname(path) if check_folder_permission(request, repo_id, parent_dir) != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) username = request.user.username is_locked, locked_by_me = check_file_lock(repo_id, path, username) if operation == 'lock': if not is_locked: # lock file expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS) try: seafile_api.lock_file(repo_id, path.lstrip('/'), username, expire) except SearpcError, e: logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
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 put(self, request, repo_id, format=None): repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) path = request.GET.get('p', '') if not path: error_msg = 'p invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) file_id = seafile_api.get_file_id_by_path(repo_id, path) if not file_id: error_msg = 'File %s not found.' % path return api_error(status.HTTP_404_NOT_FOUND, error_msg) if check_folder_permission(request, repo_id, path) != 'rw': error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) username = request.user.username operation = request.data.get('operation', '') if operation.lower() == 'lock': is_locked, locked_by_me = check_file_lock(repo_id, path, username) if is_locked: error_msg = 'File is already locked.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) # lock file expire = request.data.get('expire', FILE_LOCK_EXPIRATION_DAYS) try: seafile_api.lock_file(repo_id, path.lstrip('/'), username, expire) return Response({'success': True}) except SearpcError, e: logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg)
def put(self, request, repo_id, format=None): """ Currently only support lock, unlock, refresh-lock file. Permission checking: 1. user with 'rw' permission for current file; """ if not is_pro_version(): error_msg = 'file lock feature only supported in professional edition.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) # argument check path = request.GET.get('p', None) if not path: error_msg = 'p invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) path = normalize_file_path(path) operation = request.data.get('operation', None) if not operation: error_msg = 'operation invalid.' return api_error(status.HTTP_400_BAD_REQUEST, error_msg) operation = operation.lower() if operation not in ('lock', 'unlock', 'refresh-lock'): error_msg = "operation can only be 'lock', 'unlock' or 'refresh-lock'." return api_error(status.HTTP_400_BAD_REQUEST, error_msg) # resource check repo = seafile_api.get_repo(repo_id) if not repo: error_msg = 'Library %s not found.' % repo_id return api_error(status.HTTP_404_NOT_FOUND, error_msg) file_id = seafile_api.get_file_id_by_path(repo_id, path) if not file_id: error_msg = 'File %s not found.' % path return api_error(status.HTTP_404_NOT_FOUND, error_msg) # permission check parent_dir = os.path.dirname(path) if check_folder_permission(request, repo_id, parent_dir) != PERMISSION_READ_WRITE: error_msg = 'Permission denied.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) username = request.user.username try: is_locked, locked_by_me = check_file_lock(repo_id, path, username) except Exception as e: logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) # check if is locked by online office locked_by_online_office = if_locked_by_online_office(repo_id, path) if operation == 'lock': if is_locked: 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) try: seafile_api.lock_file(repo_id, path, username, expire) except SearpcError as e: logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) if operation == 'unlock': if not is_locked: error_msg = _("File is not locked.") return api_error(status.HTTP_400_BAD_REQUEST, error_msg) if locked_by_me or locked_by_online_office: # unlock file try: seafile_api.unlock_file(repo_id, path) except SearpcError as e: logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) else: error_msg = 'You can not unlock this file.' return api_error(status.HTTP_403_FORBIDDEN, error_msg) if operation == 'refresh-lock': if not is_locked: error_msg = _("File is not locked.") 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) except SearpcError as e: logger.error(e) error_msg = 'Internal Server Error' return api_error(status.HTTP_500_INTERNAL_SERVER_ERROR, error_msg) else: error_msg = _("You can not refresh this file's lock.") return api_error(status.HTTP_403_FORBIDDEN, error_msg) file_info = self.get_file_info(username, repo_id, path) return Response(file_info)
def onlyoffice_editor_callback(request): """ Callback func of OnlyOffice. The document editing service informs the document storage service about status of the document editing using the callbackUrl from JavaScript API. The document editing service use the POST request with the information in body. https://api.onlyoffice.com/editors/callback """ if request.method != 'POST': logger.error('Request method if not POST.') # The document storage service must return the following response. # otherwise the document editor will display an error message. return HttpResponse('{"error": 0}') #### 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) # {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', # u'user': {u'id': u'uid-1527736577058', # u'name': u'lian'}}, # {u'created': u'2018-05-31 03:23:55', # u'user': {u'id': u'uid-1527736951523', # u'name': u'lian'}}], # u'serverVersion': u'5.1.4'}, # u'key': u'61484dec693009f3d506', # u'lastsave': u'2018-05-31T03:23:55.767Z', # u'notmodified': False, # u'status': 2, # u'url': u'...', # u'users': [u'uid-1527736951523']} # Defines the status of the document. Can have the following values: # 0 - no document with the key identifier could be found, # 1 - document is being edited, # 2 - document is ready for saving, # 3 - document saving error has occurred, # 4 - document is closed with no changes, # 6 - document is being edited, but the current document state is saved, # 7 - error has occurred while force saving the document. # Status 1 is received every user connection to or disconnection from document co-editing. # # Status 2 (3) is received 10 seconds after the document is closed for editing with the identifier of the user who was the last to send the changes to the document editing service. # # Status 4 is received after the document is closed for editing with no changes by the last user. # # Status 6 (7) is received when the force saving request is performed. 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 in (2, 6): # Defines the link to the edited document to be saved with the document storage service. # The link is present when the status value is equal to 2 or 3 only. url = post_data.get('url') onlyoffice_resp = requests.get(url, verify=VERIFY_ONLYOFFICE_CERTIFICATE) if not onlyoffice_resp: 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: if if_locked_by_online_office(repo_id, file_path): seafile_api.unlock_file(repo_id, file_path) cache.delete(cache_key) cache.delete("ONLYOFFICE_%s" % doc_key) fake_obj_id = { 'online_office_update': True, } update_token = seafile_api.get_fileserver_access_token( repo_id, json.dumps(fake_obj_id), 'update', username) if not update_token: logger.error('[OnlyOffice] No fileserver access token.') return HttpResponse('{"error": 0}') # get file content files = { 'file': onlyoffice_resp.content, 'file_name': os.path.basename(file_path), 'target_file': file_path, } # update file update_url = gen_inner_file_upload_url('update-api', update_token) requests.post(update_url, files=files) if status == 4: 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'] cache_key = generate_onlyoffice_cache_key(repo_id, file_path) if if_locked_by_online_office(repo_id, file_path): seafile_api.unlock_file(repo_id, file_path) cache.delete(cache_key) if status == 1: connected_users = post_data.get('users') if len(connected_users) > 0: 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'] cache_key = generate_onlyoffice_cache_key(repo_id, file_path) if not if_locked_by_online_office(repo_id, file_path): seafile_api.lock_file(repo_id, file_path, ONLINE_OFFICE_LOCK_OWNER, 0) cache.set(cache_key, doc_key) return HttpResponse('{"error": 0}')