mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-31 06:34:40 +00:00
onlyoffice history (#5724)
* support view file history on onlyoffice page * check repo permission * check repo permission when gen jwt token * update * only verify jwt token when get file content * update
This commit is contained in:
14
seahub/onlyoffice/api_urls.py
Normal file
14
seahub/onlyoffice/api_urls.py
Normal file
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
from django.urls import path
|
||||
|
||||
from seahub.onlyoffice.views import OnlyofficeConvert
|
||||
from seahub.onlyoffice.views import OnlyofficeFileHistory
|
||||
from seahub.onlyoffice.views import OnlyofficeGetHistoryFileAccessToken
|
||||
|
||||
urlpatterns = [
|
||||
path('convert/', OnlyofficeConvert.as_view(), name='onlyoffice_api_convert'),
|
||||
path('file-history/', OnlyofficeFileHistory.as_view(), name='onlyoffice_api_file_history'),
|
||||
path('get-history-file-access-token/',
|
||||
OnlyofficeGetHistoryFileAccessToken.as_view(),
|
||||
name='onlyoffice_api_get_history_file_access_token'),
|
||||
]
|
8
seahub/onlyoffice/urls.py
Normal file
8
seahub/onlyoffice/urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2012-2016 Seafile Ltd.
|
||||
from django.urls import path
|
||||
|
||||
from seahub.onlyoffice.views import onlyoffice_editor_callback
|
||||
|
||||
urlpatterns = [
|
||||
path('editor-callback/', onlyoffice_editor_callback, name='onlyoffice_editor_callback'),
|
||||
]
|
@@ -1,34 +1,39 @@
|
||||
# Copyright (c) 2012-2017 Seafile Ltd.
|
||||
import os
|
||||
import jwt
|
||||
import json
|
||||
import logging
|
||||
import requests
|
||||
import posixpath
|
||||
import email.utils
|
||||
import urllib.parse
|
||||
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
|
||||
from seahub.api2.utils import api_error
|
||||
from seahub.api2.throttling import UserRateThrottle
|
||||
from seahub.api2.authentication import TokenAuthentication
|
||||
|
||||
from django.urls import reverse
|
||||
from django.core.cache import cache
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from seaserv import seafile_api
|
||||
|
||||
from seahub.onlyoffice.settings import VERIFY_ONLYOFFICE_CERTIFICATE
|
||||
from seahub.onlyoffice.settings import VERIFY_ONLYOFFICE_CERTIFICATE, ONLYOFFICE_JWT_SECRET
|
||||
from seahub.onlyoffice.utils import get_onlyoffice_dict
|
||||
from seahub.onlyoffice.utils import delete_doc_key, get_file_info_by_doc_key
|
||||
from seahub.onlyoffice.converter_utils import get_file_name_without_ext, \
|
||||
get_file_ext, get_file_type, get_internal_extension
|
||||
from seahub.onlyoffice.converter import get_converter_uri
|
||||
from seahub.utils import gen_inner_file_upload_url, is_pro_version, \
|
||||
normalize_file_path, check_filename_with_rename
|
||||
normalize_file_path, check_filename_with_rename, \
|
||||
gen_inner_file_get_url, get_service_url
|
||||
from seahub.utils.file_op import if_locked_by_online_office
|
||||
|
||||
|
||||
@@ -40,7 +45,9 @@ logger = logging.getLogger('onlyoffice')
|
||||
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.
|
||||
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
|
||||
"""
|
||||
@@ -85,7 +92,9 @@ def onlyoffice_editor_callback(request):
|
||||
|
||||
# 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 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.
|
||||
#
|
||||
@@ -295,3 +304,133 @@ class OnlyofficeConvert(APIView):
|
||||
result['file_path'] = posixpath.join(parent_dir, file_name)
|
||||
|
||||
return Response(result)
|
||||
|
||||
|
||||
class OnlyofficeFileHistory(APIView):
|
||||
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def get(self, request):
|
||||
|
||||
if not ONLYOFFICE_JWT_SECRET:
|
||||
error_msg = 'feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
bearer_string = request.headers.get('authorization', '')
|
||||
if not bearer_string:
|
||||
logger.error('No authentication header.')
|
||||
error_msg = 'No authentication header.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
bearer_list = bearer_string.split()
|
||||
if len(bearer_list) != 2 or bearer_list[0].lower() != 'bearer':
|
||||
logger.error(f'Bearer {bearer_string} invalid')
|
||||
error_msg = 'Bearer invalid.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
token = bearer_list[1]
|
||||
try:
|
||||
payload = jwt.decode(token, ONLYOFFICE_JWT_SECRET, algorithms=['HS256'])
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
logger.error(token)
|
||||
error_msg = 'Decode JWT token failed.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
payload = payload.get('payload')
|
||||
if not payload:
|
||||
logger.error(payload)
|
||||
error_msg = 'Payload invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
url = payload.get('url')
|
||||
if not url:
|
||||
logger.error(payload)
|
||||
error_msg = 'No url in payload.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
query_string = request.META.get('QUERY_STRING', '')
|
||||
if request.path not in url or query_string not in url:
|
||||
error_msg = 'Bearer invalid.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
parsed_url = urllib.parse.urlparse(url)
|
||||
query_parameters = urllib.parse.parse_qs(parsed_url.query)
|
||||
# username = query_parameters.get('username')[0]
|
||||
repo_id = query_parameters.get('repo_id')[0]
|
||||
file_path = query_parameters.get('path')[0]
|
||||
obj_id = query_parameters.get('obj_id')[0]
|
||||
|
||||
if not repo_id or not file_path or not obj_id:
|
||||
logger.error(url)
|
||||
error_msg = 'url invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
file_name = os.path.basename(file_path)
|
||||
fileserver_token = seafile_api.get_fileserver_access_token(repo_id,
|
||||
obj_id,
|
||||
'view',
|
||||
'',
|
||||
use_onetime=False)
|
||||
|
||||
inner_path = gen_inner_file_get_url(fileserver_token, file_name)
|
||||
file_content = urllib.request.urlopen(inner_path).read()
|
||||
return HttpResponse(file_content, content_type="application/octet-stream")
|
||||
|
||||
|
||||
class OnlyofficeGetHistoryFileAccessToken(APIView):
|
||||
|
||||
authentication_classes = (TokenAuthentication, SessionAuthentication)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
throttle_classes = (UserRateThrottle,)
|
||||
|
||||
def post(self, request):
|
||||
|
||||
if not ONLYOFFICE_JWT_SECRET:
|
||||
error_msg = 'feature is not enabled.'
|
||||
return api_error(501, error_msg)
|
||||
|
||||
repo_id = request.data.get('repo_id')
|
||||
if not repo_id:
|
||||
error_msg = 'repo_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
file_path = request.data.get('file_path')
|
||||
if not file_path:
|
||||
error_msg = 'file_path invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
obj_id = request.data.get('obj_id')
|
||||
if not obj_id:
|
||||
error_msg = 'obj_id invalid.'
|
||||
return api_error(status.HTTP_400_BAD_REQUEST, error_msg)
|
||||
|
||||
if not seafile_api.get_repo(repo_id):
|
||||
error_msg = 'Library %s not found.' % repo_id
|
||||
return api_error(status.HTTP_404_NOT_FOUND, error_msg)
|
||||
|
||||
username = request.user.username
|
||||
if not seafile_api.check_permission_by_path(repo_id, '/', username):
|
||||
error_msg = 'Permission denied.'
|
||||
return api_error(status.HTTP_403_FORBIDDEN, error_msg)
|
||||
|
||||
service_url = get_service_url().rstrip('/')
|
||||
url = reverse('onlyoffice_api_file_history')
|
||||
query_dict = {
|
||||
"username": username,
|
||||
"repo_id": repo_id,
|
||||
"path": file_path,
|
||||
"obj_id": obj_id
|
||||
}
|
||||
query_string = urllib.parse.urlencode(query_dict)
|
||||
full_url = f"{service_url}{url}?{query_string}"
|
||||
|
||||
payload = {}
|
||||
payload['key'] = obj_id
|
||||
payload['url'] = full_url
|
||||
payload['version'] = obj_id
|
||||
|
||||
jwt_token = jwt.encode(payload, ONLYOFFICE_JWT_SECRET)
|
||||
payload['token'] = jwt_token
|
||||
|
||||
return Response({"data": payload})
|
||||
|
@@ -20,7 +20,65 @@ html, body { padding:0; margin:0; height:100%; }
|
||||
<script type="text/javascript" src="{{ MEDIA_URL }}js/base.js?t=1536127546642"></script>
|
||||
<script type="text/javascript" src="{{ ONLYOFFICE_APIJS_URL }}"></script>
|
||||
<script type="text/javascript">
|
||||
{% if onlyoffice_jwt_token %}
|
||||
var onRequestHistory = function() {
|
||||
$.ajax({
|
||||
url: '{% url "api-v2.1-new-file-history-view" repo_id %}' + '?path={{path|urlencode}}',
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
success: function(resp) {
|
||||
var history = [];
|
||||
resp.data.reverse().forEach (function (item) {
|
||||
history.push({
|
||||
"created": item.ctime,
|
||||
"key": item.rev_file_id,
|
||||
"user": {
|
||||
"id": item.creator_email,
|
||||
"name": item.creator_name
|
||||
},
|
||||
"version": item.rev_file_id
|
||||
});
|
||||
});
|
||||
docEditor.refreshHistory({
|
||||
"currentVersion": history.at(-1).version,
|
||||
"history": history
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
var onRequestHistoryData = function(event) {
|
||||
var version = event.data;
|
||||
var data = {
|
||||
"repo_id": "{{repo_id}}",
|
||||
"file_path": "{{path}}",
|
||||
"obj_id": version
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: '{% url "onlyoffice_api_get_history_file_access_token" %}',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
cache: false,
|
||||
beforeSend: prepareCSRFToken,
|
||||
data: data,
|
||||
success: function(resp) {
|
||||
docEditor.setHistoryData(resp.data);
|
||||
}
|
||||
});
|
||||
};
|
||||
var onRequestHistoryClose = function () {
|
||||
document.location.reload();
|
||||
};
|
||||
{% endif %}
|
||||
var config = {
|
||||
{% if onlyoffice_jwt_token %}
|
||||
"events": {
|
||||
"onRequestHistory": onRequestHistory,
|
||||
"onRequestHistoryData": onRequestHistoryData,
|
||||
"onRequestHistoryClose": onRequestHistoryClose,
|
||||
},
|
||||
{% endif %}
|
||||
"type": window.screen.width < 992 ? 'mobile' : 'desktop',
|
||||
"document": {
|
||||
"fileType": "{{ file_type }}",
|
||||
|
@@ -948,11 +948,9 @@ if getattr(settings, 'ENABLE_MULTI_ADFS', False) or getattr(settings, 'ENABLE_AD
|
||||
]
|
||||
|
||||
if getattr(settings, 'ENABLE_ONLYOFFICE', False):
|
||||
from seahub.onlyoffice.views import onlyoffice_editor_callback
|
||||
from seahub.onlyoffice.views import OnlyofficeConvert
|
||||
urlpatterns += [
|
||||
path('onlyoffice/editor-callback/', onlyoffice_editor_callback, name='onlyoffice_editor_callback'),
|
||||
path('onlyoffice-api/convert/', OnlyofficeConvert.as_view(), name='onlyoffice_api_convert'),
|
||||
path('onlyoffice/', include('seahub.onlyoffice.urls')),
|
||||
path('onlyoffice-api/', include('seahub.onlyoffice.api_urls')),
|
||||
]
|
||||
|
||||
if getattr(settings, 'ENABLE_BISHENG_OFFICE', False):
|
||||
|
Reference in New Issue
Block a user