Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

onlyoffice history #5724

Merged
merged 6 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions seahub/onlyoffice/api_urls.py
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions seahub/onlyoffice/urls.py
Original file line number Diff line number Diff line change
@@ -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'),
]
147 changes: 143 additions & 4 deletions seahub/onlyoffice/views.py
Original file line number Diff line number Diff line change
@@ -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


Expand All @@ -40,7 +45,9 @@
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
"""
Expand Down Expand Up @@ -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.
#
Expand Down Expand Up @@ -295,3 +304,133 @@ def rewrite_request(prepared_request):
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})
58 changes: 58 additions & 0 deletions seahub/templates/view_file_onlyoffice.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,65 @@
<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 }}",
Expand Down
6 changes: 2 additions & 4 deletions seahub/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -946,11 +946,9 @@
]

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):
Expand Down
Loading