Merge remote-tracking branch 'remotes/origin/develop' into feature/fix-filename

# Conflicts:
#	web/documentserver-example/java/src/main/java/controllers/IndexServlet.java
#	web/documentserver-example/python/src/views/actions.py
This commit is contained in:
Alexandr Fedorov
2021-02-05 12:56:04 +03:00
137 changed files with 1754 additions and 360 deletions

View File

@ -4,21 +4,22 @@ Django settings for example project.
Generated by 'django-admin startproject' using Django 2.2.6.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/topics/settings/
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.2/ref/settings/
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
import config
import mimetypes
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '7a5qnm_bv)iskjhx%4cbwwdmjev03%zewm=3@4s*uz)el#ds5o'
@ -66,19 +67,19 @@ WSGI_APPLICATION = 'src.wsgi.application'
# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {}
# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = []
# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
@ -92,8 +93,9 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
# https://docs.djangoproject.com/en/3.1/howto/static-files/
mimetypes.add_type("text/javascript", ".js", True)
STATIC_ROOT = ''
STATIC_URL = '/static/'
STATICFILES_DIRS = ( os.path.join('static'), os.path.join(config.STORAGE_PATH) )
STATICFILES_DIRS = ( os.path.join('static'), os.path.join(config.STORAGE_PATH), os.path.join('assets/sample'))

View File

@ -36,7 +36,9 @@ urlpatterns = [
path('create', actions.createNew),
path('edit', actions.edit),
path('track', actions.track),
path('remove', actions.remove)
path('remove', actions.remove),
path('csv', actions.csv),
path('files', actions.files)
]
urlpatterns += staticfiles_urlpatterns()

View File

@ -31,6 +31,7 @@ import shutil
import io
import re
import requests
import time
from src import settings
from . import fileUtils, historyManager
@ -102,15 +103,21 @@ def getCorrectName(filename, req):
return name
def getFileUri(filename, req):
host = config.EXAMPLE_DOMAIN.rstrip('/')
def getServerUrl (forDocumentServer, req):
if (forDocumentServer and config.EXAMPLE_DOMAIN is not None):
return config.EXAMPLE_DOMAIN
else:
return req.headers.get("x-forwarded-proto") or req.scheme + "://" + req.get_host()
def getFileUri(filename, forDocumentServer, req):
host = getServerUrl(forDocumentServer, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}{settings.STATIC_URL}{curAdr}/{filename}'
def getCallbackUrl(filename, req):
host = config.EXAMPLE_DOMAIN
host = getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}track?filename={filename}&userAddress={curAdr}'
return f'{host}/track?filename={filename}&userAddress={curAdr}'
def getRootFolder(req):
if isinstance(req, str):
@ -140,7 +147,7 @@ def getStoredFiles(req):
for f in files:
if os.path.isfile(os.path.join(directory, f)):
fileInfos.append({ 'type': fileUtils.getFileType(f), 'title': f, 'url': getFileUri(f, req) })
fileInfos.append({ 'type': fileUtils.getFileType(f), 'title': f, 'url': getFileUri(f, True, req) })
return fileInfos
@ -173,7 +180,7 @@ def createSample(fileType, sample, req):
filename = getCorrectName(f'{sampleName}{ext}', req)
path = getStoragePath(filename, req)
with io.open(os.path.join('samples', f'{sampleName}{ext}'), 'rb') as stream:
with io.open(os.path.join('assets', 'sample' if sample == 'true' else 'new', f'{sampleName}{ext}'), 'rb') as stream:
createFile(stream, path, req, True)
return filename
@ -187,9 +194,34 @@ def removeFile(filename, req):
def generateFileKey(filename, req):
path = getStoragePath(filename, req)
uri = getFileUri(filename, req)
uri = getFileUri(filename, False, req)
stat = os.stat(path)
h = str(hash(f'{uri}_{stat.st_mtime_ns}'))
replaced = re.sub(r'[^0-9-.a-zA-Z_=]', '_', h)
return replaced[:20]
def getFilesInfo(req):
fileId = req.GET.get('fileId') if req.GET.get('fileId') else None
result = []
resultID = []
for f in getStoredFiles(req):
stats = os.stat(os.path.join(getRootFolder(req), f.get("title")))
result.append(
{ "version" : historyManager.getFileVersion(historyManager.getHistoryDir(getStoragePath(f.get("title"), req))),
"id" : generateFileKey(f.get("title"), req),
"contentLength" : "%.2f KB" % (stats.st_size/1024),
"pureContentLength" : stats.st_size,
"title" : f.get("title"),
"updated" : time.strftime("%Y-%m-%dT%X%z",time.gmtime(stats.st_mtime))
})
if fileId :
if fileId == generateFileKey(f.get("title"), req) :
resultID.append(result[-1])
if fileId :
if len(resultID) > 0 : return resultID
else : return "File not found"
else :
return result

View File

@ -32,6 +32,8 @@ import config
from . import users, fileUtils
from datetime import datetime
from src import settings
from src.utils import docManager
from src.utils import jwtManager
def getHistoryDir(storagePath):
return f'{storagePath}-hist'
@ -43,7 +45,7 @@ def getFileVersion(histDir):
if not os.path.exists(histDir):
return 0
cnt = 0
cnt = 1
for f in os.listdir(histDir):
if not os.path.isfile(os.path.join(histDir, f)):
@ -53,7 +55,7 @@ def getFileVersion(histDir):
def getNextVersionDir(histDir):
v = getFileVersion(histDir)
path = getVersionDir(histDir, v + 1)
path = getVersionDir(histDir, v)
if not os.path.exists(path):
os.makedirs(path)
@ -84,7 +86,7 @@ def createMeta(storagePath, req):
user = users.getUserFromReq(req)
obj = {
'created': datetime.today().strftime('%d.%m.%Y %H:%M:%S'),
'created': datetime.today().strftime('%Y-%m-%d %H:%M:%S'),
'uid': user['uid'],
'uname': user['uname']
}
@ -103,12 +105,12 @@ def readFile(path):
return stream.read()
def getPrevUri(filename, ver, ext, req):
host = config.EXAMPLE_DOMAIN.rstrip('/')
host = docManager.getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}{settings.STATIC_URL}{curAdr}/{filename}-hist/{ver}/prev{ext}'
def getZipUri(filename, ver, req):
host = config.EXAMPLE_DOMAIN.rstrip('/')
host = docManager.getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}{settings.STATIC_URL}{curAdr}/{filename}-hist/{ver}/diff.zip'
@ -128,12 +130,12 @@ def getHistoryObject(storagePath, filename, docKey, docUrl, req):
if version > 0:
hist = []
histData = {}
for i in range(version + 1):
for i in range(1, version + 1):
obj = {}
dataObj = {}
prevVerDir = getVersionDir(histDir, i)
verDir = getVersionDir(histDir, i + 1)
prevVerDir = getVersionDir(histDir, i - 1)
verDir = getVersionDir(histDir, i)
try:
key = docKey if i == version else readFile(getKeyPath(verDir))
@ -143,7 +145,7 @@ def getHistoryObject(storagePath, filename, docKey, docUrl, req):
dataObj['key'] = key
dataObj['version'] = i
if i == 0:
if i == 1:
meta = getMeta(storagePath)
if meta:
obj['created'] = meta['created']
@ -152,9 +154,9 @@ def getHistoryObject(storagePath, filename, docKey, docUrl, req):
'name': meta['uname']
}
dataObj['url'] = docUrl if i == version else getPrevUri(filename, i + 1, fileUtils.getFileExt(filename), req)
dataObj['url'] = docUrl if i == version else getPrevUri(filename, i, fileUtils.getFileExt(filename), req)
if i > 0:
if i > 1:
changes = json.loads(readFile(getChangesHistoryPath(prevVerDir)))
change = changes['changes'][0]
@ -163,16 +165,19 @@ def getHistoryObject(storagePath, filename, docKey, docUrl, req):
obj['created'] = change['created']
obj['user'] = change['user']
prev = histData[str(i - 1)]
prev = histData[str(i - 2)]
prevInfo = {
'key': prev['key'],
'url': prev['url']
}
dataObj['previous'] = prevInfo
dataObj['changesUrl'] = getZipUri(filename, i, req)
dataObj['changesUrl'] = getZipUri(filename, i - 1, req)
if jwtManager.isEnabled():
dataObj['token'] = jwtManager.encode(dataObj)
hist.append(obj)
histData[str(i)] = dataObj
histData[str(i - 1)] = dataObj
except Exception:
return {}

View File

@ -50,11 +50,12 @@ def getConverterUri(docUri, fromExt, toExt, docKey, isAsync):
payload.setdefault('async', True)
if jwtManager.isEnabled():
jwtHeader = 'Authorization' if config.DOC_SERV_JWT_HEADER is None or config.DOC_SERV_JWT_HEADER == '' else config.DOC_SERV_JWT_HEADER
headerToken = jwtManager.encode({'payload': payload})
payload['token'] = jwtManager.encode(payload)
headers['Authorization'] = f'Bearer {headerToken}'
headers[jwtHeader] = f'Bearer {headerToken}'
response = requests.post(config.DOC_SERV_CONVERTER_URL, json=payload, headers=headers )
response = requests.post(config.DOC_SERV_SITE_URL + config.DOC_SERV_CONVERTER_URL, json=payload, headers=headers )
json = response.json()
return getResponseUri(json)

View File

@ -27,9 +27,11 @@
import config
import json
import os
import urllib.parse
import magic
from datetime import datetime
from django.http import HttpResponse, HttpResponseRedirect
from django.http import HttpResponse, HttpResponseRedirect, FileResponse
from django.shortcuts import render
from src.utils import docManager, fileUtils, serviceConverter, users, jwtManager, historyManager
@ -64,7 +66,7 @@ def convert(request):
try:
filename = fileUtils.getFileName(request.GET['filename'])
fileUri = docManager.getFileUri(filename, request)
fileUri = docManager.getFileUri(filename, True,request)
fileExt = fileUtils.getFileExt(filename)
fileType = fileUtils.getFileType(filename)
newExt = docManager.getInternalExtension(fileType)
@ -112,7 +114,8 @@ def edit(request):
ext = fileUtils.getFileExt(filename)
fileUri = docManager.getFileUri(filename, request)
fileUri = docManager.getFileUri(filename, True, request)
fileUriUser = docManager.getFileUri(filename, False, request)
docKey = docManager.generateFileKey(filename, request)
fileType = fileUtils.getFileType(filename)
user = users.getUserFromReq(request)
@ -133,15 +136,15 @@ def edit(request):
if (meta):
infObj = {
'author': meta['uname'],
'created': meta['created']
'owner': meta['uname'],
'uploaded': meta['created']
}
else:
infObj = {
'author': 'Me',
'created': datetime.today().strftime('%d.%m.%Y %H:%M:%S')
'owner': 'Me',
'uploaded': datetime.today().strftime('%d.%m.%Y %H:%M:%S')
}
infObj['favorite'] = request.COOKIES.get('uid') == 'uid-2' if request.COOKIES.get('uid') else None
edConfig = {
'type': edType,
'documentType': fileType,
@ -171,23 +174,41 @@ def edit(request):
'name': user['uname']
},
'embedded': {
'saveUrl': fileUri,
'embedUrl': fileUri,
'shareUrl': fileUri,
'saveUrl': fileUriUser,
'embedUrl': fileUriUser,
'shareUrl': fileUriUser,
'toolbarDocked': 'top'
},
'customization': {
'about': True,
'feedback': True,
'goback': {
'url': config.EXAMPLE_DOMAIN
'url': docManager.getServerUrl(False, request)
}
}
}
}
dataInsertImage = {
'fileType': 'png',
'url': docManager.getServerUrl(True, request) + 'static/images/logo.png'
}
dataCompareFile = {
'fileType': 'docx',
'url': docManager.getServerUrl(True, request) + 'static/sample.docx'
}
dataMailMergeRecipients = {
'fileType': 'csv',
'url': docManager.getServerUrl(True, request) + 'csv'
}
if jwtManager.isEnabled():
edConfig['token'] = jwtManager.encode(edConfig)
dataInsertImage['token'] = jwtManager.encode(dataInsertImage)
dataCompareFile['token'] = jwtManager.encode(dataCompareFile)
dataMailMergeRecipients['token'] = jwtManager.encode(dataMailMergeRecipients)
hist = historyManager.getHistoryObject(storagePath, filename, docKey, fileUri, request)
@ -196,7 +217,10 @@ def edit(request):
'history': json.dumps(hist['history']) if 'history' in hist else None,
'historyData': json.dumps(hist['historyData']) if 'historyData' in hist else None,
'fileType': fileType,
'apiUrl': config.DOC_SERV_API_URL
'apiUrl': config.DOC_SERV_SITE_URL + config.DOC_SERV_API_URL,
'dataInsertImage': json.dumps(dataInsertImage)[1 : len(json.dumps(dataInsertImage)) - 1],
'dataCompareFile': dataCompareFile,
'dataMailMergeRecipients': json.dumps(dataMailMergeRecipients)
}
return render(request, 'editor.html', context)
@ -213,7 +237,8 @@ def track(request):
token = body.get('token')
if (not token):
token = request.headers.get('Authorization')
jwtHeader = 'Authorization' if config.DOC_SERV_JWT_HEADER is None or config.DOC_SERV_JWT_HEADER == '' else config.DOC_SERV_JWT_HEADER
token = request.headers.get(jwtHeader)
if token:
token = token[len('Bearer '):]
@ -261,4 +286,20 @@ def remove(request):
docManager.removeFile(filename, request)
response.setdefault('success', True)
return HttpResponse(json.dumps(response), content_type='application/json')
return HttpResponse(json.dumps(response), content_type='application/json')
def files(request):
try:
response = docManager.getFilesInfo(request)
except Exception as e:
response = {}
response.setdefault('error', e.args[0])
return HttpResponse(json.dumps(response), content_type='application/json')
def csv(request):
filePath = os.path.join('assets', 'sample', "csv.csv")
response = FileResponse(open(filePath, 'rb'), True)
response['Content-Length'] = os.path.getsize(filePath)
response['Content-Disposition'] = "attachment;filename*=UTF-8\'\'" + urllib.parse.unquote(os.path.basename(filePath))
response['Content-Type'] = magic.from_file(filePath, mime=True)
return response

View File

@ -36,7 +36,7 @@ def default(request):
context = {
'users': users.USERS,
'languages': docManager.LANGUAGES,
'preloadurl': config.DOC_SERV_PRELOADER_URL,
'preloadurl': config.DOC_SERV_SITE_URL + config.DOC_SERV_PRELOADER_URL,
'editExt': json.dumps(config.DOC_SERV_EDITED),
'convExt': json.dumps(config.DOC_SERV_CONVERT),
'files': docManager.getStoredFiles(request)

View File

@ -4,7 +4,7 @@ WSGI config for example project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
"""
import os