Merge remote-tracking branch 'remotes/origin/develop' into feature/jwt-download-files

# Conflicts:
#	web/documentserver-example/csharp-mvc/Helpers/DocManagerHelper.cs
#	web/documentserver-example/csharp-mvc/Models/FileModel.cs
#	web/documentserver-example/csharp-mvc/WebEditor.ashx.cs
#	web/documentserver-example/csharp/DocEditor.aspx.cs
#	web/documentserver-example/csharp/WebEditor.ashx.cs
#	web/documentserver-example/java/src/main/java/controllers/IndexServlet.java
#	web/documentserver-example/java/src/main/java/entities/FileModel.java
#	web/documentserver-example/java/src/main/java/helpers/DocumentManager.java
#	web/documentserver-example/nodejs/helpers/docManager.js
#	web/documentserver-example/php/doceditor.php
#	web/documentserver-example/php/webeditor-ajax.php
#	web/documentserver-example/python/src/utils/docManager.py
#	web/documentserver-example/python/src/views/actions.py
#	web/documentserver-example/ruby/app/controllers/home_controller.rb
#	web/documentserver-example/ruby/app/models/document_helper.rb
#	web/documentserver-example/ruby/app/models/file_model.rb
This commit is contained in:
Alexandr Fedorov
2021-04-21 17:53:53 +03:00
66 changed files with 2282 additions and 1378 deletions

View File

@ -73,18 +73,23 @@ LANGUAGES = {
'vi': 'Vietnamese'
}
# check if the file extension can be viewed
def isCanView(ext):
return ext in config.DOC_SERV_VIEWED
# check if the file extension can be edited
def isCanEdit(ext):
return ext in config.DOC_SERV_EDITED
# check if the file extension can be converted
def isCanConvert(ext):
return ext in config.DOC_SERV_CONVERT
# check if the file extension is supported by the editor (it can be viewed or edited or converted)
def isSupportedExt(ext):
return isCanView(ext) | isCanEdit(ext) | isCanConvert(ext)
# get internal extension for a given file type
def getInternalExtension(fileType):
mapping = {
'word': '.docx',
@ -92,31 +97,35 @@ def getInternalExtension(fileType):
'slide': '.pptx'
}
return mapping.get(fileType, '.docx')
return mapping.get(fileType, '.docx') # the default file type is .docx
# get file name with an index if such a file name already exists
def getCorrectName(filename, req):
basename = fileUtils.getFileNameWithoutExt(filename)
ext = fileUtils.getFileExt(filename)
name = f'{basename}{ext}'
i = 1
while os.path.exists(getStoragePath(name, req)):
name = f'{basename} ({i}){ext}'
while os.path.exists(getStoragePath(name, req)): # if file with such a name already exists
name = f'{basename} ({i}){ext}' # add an index to its name
i += 1
return name
# get server url
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()
# get file url
def getFileUri(filename, forDocumentServer, req):
host = getServerUrl(forDocumentServer, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}{settings.STATIC_URL}{curAdr}/{filename}'
# get absolute URL to the document storage service
def getCallbackUrl(filename, req):
host = getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
@ -131,6 +140,7 @@ def getDownloadUrl(filename, req):
curAdr = req.META['REMOTE_ADDR']
return f'{host}/download?fileName={filename}&userAddress={curAdr}'
# get root folder for the current file
def getRootFolder(req):
if isinstance(req, str):
curAdr = req
@ -139,16 +149,18 @@ def getRootFolder(req):
directory = os.path.join(config.STORAGE_PATH, curAdr)
if not os.path.exists(directory):
if not os.path.exists(directory): # if such a directory does not exist, make it
os.makedirs(directory)
return directory
# get the file path
def getStoragePath(filename, req):
directory = getRootFolder(req)
return os.path.join(directory, fileUtils.getFileName(filename))
# get the path to the forcesaved file version
def getForcesavePath(filename, req, create):
if isinstance(req, str):
curAdr = req
@ -156,47 +168,50 @@ def getForcesavePath(filename, req, create):
curAdr = req.META['REMOTE_ADDR']
directory = os.path.join(config.STORAGE_PATH, curAdr)
if not os.path.exists(directory):
if not os.path.exists(directory): # the directory with host address doesn't exist
return ""
directory = os.path.join(directory, f'{filename}-hist')
directory = os.path.join(directory, f'{filename}-hist') # get the path to the history of the given file
if (not os.path.exists(directory)):
if create:
os.makedirs(directory)
else:
if create: # if the history directory doesn't exist
os.makedirs(directory) # create history directory if it doesn't exist
else: # the history directory doesn't exist and we are not supposed to create it
return ""
directory = os.path.join(directory, filename)
directory = os.path.join(directory, filename) # and get the path to the given file
if (not os.path.exists(directory) and not create):
return ""
return directory
# get information about all the stored files
def getStoredFiles(req):
directory = getRootFolder(req)
files = os.listdir(directory)
files.sort(key=lambda x: os.path.getmtime(os.path.join(directory, x)), reverse=True)
files.sort(key=lambda x: os.path.getmtime(os.path.join(directory, x)), reverse=True) # sort files by time of last modification
fileInfos = []
for f in files:
if os.path.isfile(os.path.join(directory, f)):
fileInfos.append({ 'type': fileUtils.getFileType(f), 'title': f, 'url': getFileUri(f, True, req), 'canEdit': isCanEdit(fileUtils.getFileExt(f))})
fileInfos.append({ 'type': fileUtils.getFileType(f), 'title': f, 'url': getFileUri(f, True, req), 'canEdit': isCanEdit(fileUtils.getFileExt(f))}) # write information about file type, title and url
return fileInfos
# create a file
def createFile(stream, path, req = None, meta = False):
bufSize = 8192
with io.open(path, 'wb') as out:
with io.open(path, 'wb') as out: # write data to the file by streams
read = stream.read(bufSize)
while len(read) > 0:
out.write(read)
read = stream.read(bufSize)
if meta:
historyManager.createMeta(path, req)
historyManager.createMeta(path, req) # create meta data for the file if needed
return
# create file response
def createFileResponse(response, path, req, meta):
response.raise_for_status()
with open(path, 'wb') as file:
@ -204,43 +219,48 @@ def createFileResponse(response, path, req, meta):
file.write(chunk)
return
# save file from the given url
def saveFileFromUri(uri, path, req = None, meta = False):
resp = requests.get(uri, stream=True)
createFileResponse(resp, path, req, meta)
return
# create sample file
def createSample(fileType, sample, req):
ext = getInternalExtension(fileType)
ext = getInternalExtension(fileType) # get the internal extension of the given file type
if not sample:
sample = 'false'
sampleName = 'sample' if sample == 'true' else 'new'
sampleName = 'sample' if sample == 'true' else 'new' # create sample or new template
filename = getCorrectName(f'{sampleName}{ext}', req)
filename = getCorrectName(f'{sampleName}{ext}', req) # get file name with an index if such a file name already exists
path = getStoragePath(filename, req)
with io.open(os.path.join('assets', 'sample' if sample == 'true' else 'new', 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: # create sample file of the necessary extension in the directory
createFile(stream, path, req, True)
return filename
# remove file from the directory
def removeFile(filename, req):
path = getStoragePath(filename, req)
if os.path.exists(path):
os.remove(path)
histDir = historyManager.getHistoryDir(path)
if os.path.exists(histDir):
histDir = historyManager.getHistoryDir(path) # get history directory
if os.path.exists(histDir): # remove all the history information about this file
shutil.rmtree(histDir)
# generate file key
def generateFileKey(filename, req):
path = getStoragePath(filename, req)
uri = getFileUri(filename, False, req)
stat = os.stat(path)
stat = os.stat(path) # get the directory parameters
h = str(hash(f'{uri}_{stat.st_mtime_ns}'))
h = str(hash(f'{uri}_{stat.st_mtime_ns}')) # get the hash value of the file url and the date of its last modification and turn it into a string format
replaced = re.sub(r'[^0-9-.a-zA-Z_=]', '_', h)
return replaced[:20]
return replaced[:20] # take the first 20 characters for the key
# generate the document key value
def generateRevisionId(expectedKey):
if (len(expectedKey) > 20):
expectedKey = str(hash(expectedKey))
@ -248,14 +268,15 @@ def generateRevisionId(expectedKey):
key = re.sub(r'[^0-9-.a-zA-Z_=]', '_', expectedKey)
return key[:20]
# get files information
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(
for f in getStoredFiles(req): # run through all the files from the directory
stats = os.stat(os.path.join(getRootFolder(req), f.get("title"))) # get file information
result.append( # write file parameters to the file object
{ "version" : historyManager.getFileVersion(historyManager.getHistoryDir(getStoragePath(f.get("title"), req))),
"id" : generateFileKey(f.get("title"), req),
"contentLength" : "%.2f KB" % (stats.st_size/1024),
@ -263,9 +284,9 @@ def getFilesInfo(req):
"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 file id is defined
if fileId == generateFileKey(f.get("title"), req) : # and it is equal to the file key value
resultID.append(result[-1]) # add file object to the response array
if fileId :
if len(resultID) > 0 : return resultID
@ -273,8 +294,9 @@ def getFilesInfo(req):
else :
return result
# download the file
def download(filePath):
response = FileResponse(open(filePath, 'rb'), True)
response = FileResponse(open(filePath, 'rb'), True) # write headers to the response object
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)

View File

@ -26,20 +26,24 @@
import config
# get file name from the document url
def getFileName(str):
ind = str.rfind('/')
return str[ind+1:]
# get file name without extension from the document url
def getFileNameWithoutExt(str):
fn = getFileName(str)
ind = fn.rfind('.')
return fn[:ind]
# get file extension from the document url
def getFileExt(str):
fn = getFileName(str)
ind = fn.rfind('.')
return fn[ind:].lower()
# get file type
def getFileType(str):
ext = getFileExt(str)
if ext in config.EXT_DOCUMENT:
@ -49,4 +53,4 @@ def getFileType(str):
if ext in config.EXT_PRESENTATION:
return 'slide'
return 'word'
return 'word' # default file type is word

View File

@ -35,57 +35,67 @@ from src import settings
from src.utils import docManager
from src.utils import jwtManager
# get the path to the history direction
def getHistoryDir(storagePath):
return f'{storagePath}-hist'
# get the path to the given file version
def getVersionDir(histDir, version):
return os.path.join(histDir, str(version))
# get file version of the given history directory
def getFileVersion(histDir):
if not os.path.exists(histDir):
return 0
if not os.path.exists(histDir): # if the history directory doesn't exist
return 0 # file version is 0
cnt = 1
for f in os.listdir(histDir):
if not os.path.isfile(os.path.join(histDir, f)):
for f in os.listdir(histDir): # run through all the files in the history directory
if not os.path.isfile(os.path.join(histDir, f)): # and count the number of files
cnt += 1
return cnt
# get the path to the next file version
def getNextVersionDir(histDir):
v = getFileVersion(histDir)
path = getVersionDir(histDir, v)
v = getFileVersion(histDir) # get file version of the given history directory
path = getVersionDir(histDir, v) # get the path to the next file version
if not os.path.exists(path):
os.makedirs(path)
if not os.path.exists(path): # if this path doesn't exist
os.makedirs(path) # make the directory for this file version
return path
# get the path to a file archive with differences in the given file version
def getChangesZipPath(verDir):
return os.path.join(verDir, 'diff.zip')
# get the path to a json file with changes of the given file version
def getChangesHistoryPath(verDir):
return os.path.join(verDir, 'changes.json')
# get the path to the previous file version
def getPrevFilePath(verDir, ext):
return os.path.join(verDir, f'prev{ext}')
# get the path to a txt file with a key information in it
def getKeyPath(verDir):
return os.path.join(verDir, 'key.txt')
# get the path to a json file with meta data about this file
def getMetaPath(histDir):
return os.path.join(histDir, 'createdInfo.json')
# create a json file with file meta data using the storage path and request
def createMeta(storagePath, req):
histDir = getHistoryDir(storagePath)
path = getMetaPath(histDir)
path = getMetaPath(histDir) # get the path to a json file with meta data about file
if not os.path.exists(histDir):
os.makedirs(histDir)
user = users.getUserFromReq(req)
user = users.getUserFromReq(req) # get the user information (id and name)
obj = {
obj = { # create the meta data object
'created': datetime.today().strftime('%Y-%m-%d %H:%M:%S'),
'uid': user['uid'],
'uname': None if user['uid'] == 'uid-0' else user['uname']
@ -95,14 +105,15 @@ def createMeta(storagePath, req):
return
# create a json file with file meta data using the file name, user id, user name and user address
def createMetaData(filename, uid, uname, usAddr):
histDir = getHistoryDir(docManager.getStoragePath(filename, usAddr))
path = getMetaPath(histDir)
path = getMetaPath(histDir) # get the path to a json file with meta data about file
if not os.path.exists(histDir):
os.makedirs(histDir)
obj = {
obj = { # create the meta data object
'created': datetime.today().strftime('%Y-%m-%d %H:%M:%S'),
'uid': uid,
'uname': uname
@ -112,93 +123,99 @@ def createMetaData(filename, uid, uname, usAddr):
return
# create file with a given content in it
def writeFile(path, content):
with io.open(path, 'w') as out:
out.write(content)
return
# read a file
def readFile(path):
with io.open(path, 'r') as stream:
return stream.read()
# get the url to the previous file version with a given extension
def getPrevUri(filename, ver, ext, req):
host = docManager.getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}{settings.STATIC_URL}{curAdr}/{filename}-hist/{ver}/prev{ext}'
# get the url to a file archive with changes of the given file version
def getZipUri(filename, ver, req):
host = docManager.getServerUrl(True, req)
curAdr = req.META['REMOTE_ADDR']
return f'{host}{settings.STATIC_URL}{curAdr}/{filename}-hist/{ver}/diff.zip'
# get the meta data of the file
def getMeta(storagePath):
histDir = getHistoryDir(storagePath)
path = getMetaPath(histDir)
if os.path.exists(path):
if os.path.exists(path): # check if the json file with file meta data exists
with io.open(path, 'r') as stream:
return json.loads(stream.read())
return json.loads(stream.read()) # turn meta data into python format
return None
# get the document history of a given file
def getHistoryObject(storagePath, filename, docKey, docUrl, req):
histDir = getHistoryDir(storagePath)
version = getFileVersion(histDir)
if version > 0:
if version > 0: # if the file was modified (the file version is greater than 0)
hist = []
histData = {}
for i in range(1, version + 1):
for i in range(1, version + 1): # run through all the file versions
obj = {}
dataObj = {}
prevVerDir = getVersionDir(histDir, i - 1)
verDir = getVersionDir(histDir, i)
prevVerDir = getVersionDir(histDir, i - 1) # get the path to the previous file version
verDir = getVersionDir(histDir, i) # get the path to the given file version
try:
key = docKey if i == version else readFile(getKeyPath(verDir))
key = docKey if i == version else readFile(getKeyPath(verDir)) # get document key
obj['key'] = key
obj['version'] = i
dataObj['key'] = key
dataObj['version'] = i
if i == 1:
meta = getMeta(storagePath)
if meta:
if i == 1: # check if the version number is equal to 1
meta = getMeta(storagePath) # get meta data of this file
if meta: # write meta information to the object (user information and creation date)
obj['created'] = meta['created']
obj['user'] = {
'id': meta['uid'],
'name': meta['uname']
}
dataObj['url'] = docUrl if i == version else getPrevUri(filename, i, fileUtils.getFileExt(filename), req)
dataObj['url'] = docUrl if i == version else getPrevUri(filename, i, fileUtils.getFileExt(filename), req) # write file url to the data object
if i > 1:
changes = json.loads(readFile(getChangesHistoryPath(prevVerDir)))
if i > 1: # check if the version number is greater than 1 (the file was modified)
changes = json.loads(readFile(getChangesHistoryPath(prevVerDir))) # get the path to the changes.json file
change = changes['changes'][0]
obj['changes'] = changes['changes']
obj['changes'] = changes['changes'] # write information about changes to the object
obj['serverVersion'] = changes['serverVersion']
obj['created'] = change['created']
obj['user'] = change['user']
prev = histData[str(i - 2)]
prevInfo = {
prev = histData[str(i - 2)] # get the history data from the previous file version
prevInfo = { # write key and url information about previous file version
'key': prev['key'],
'url': prev['url']
}
dataObj['previous'] = prevInfo
dataObj['changesUrl'] = getZipUri(filename, i - 1, req)
dataObj['previous'] = prevInfo # write information about previous file version to the data object
dataObj['changesUrl'] = getZipUri(filename, i - 1, req) # write the path to the diff.zip archive with differences in this file version
if jwtManager.isEnabled():
dataObj['token'] = jwtManager.encode(dataObj)
hist.append(obj)
histData[str(i - 1)] = dataObj
hist.append(obj) # add object dictionary to the hist list
histData[str(i - 1)] = dataObj # write data object information to the history data
except Exception:
return {}
histObj = {
histObj = { # write history information about the current file version to the history object
'currentVersion': version,
'history': hist
}

View File

@ -27,11 +27,14 @@
import config
import jwt
# check if a secret key to generate token exists or not
def isEnabled():
return bool(config.DOC_SERV_JWT_SECRET)
# encode a payload object into a token using a secret key and decodes it into the utf-8 format
def encode(payload):
return jwt.encode(payload, config.DOC_SERV_JWT_SECRET, algorithm='HS256').decode('utf-8')
# decode a token into a payload object using a secret key
def decode(string):
return jwt.decode(string, config.DOC_SERV_JWT_SECRET, algorithms=['HS256'])

View File

@ -30,13 +30,14 @@ import config
from . import fileUtils, jwtManager
# convert file and give url to a new file
def getConverterUri(docUri, fromExt, toExt, docKey, isAsync, filePass = None):
if not fromExt:
fromExt = fileUtils.getFileExt(docUri)
if not fromExt: # check if the extension from the request matches the real file extension
fromExt = fileUtils.getFileExt(docUri) # if not, overwrite the extension value
title = fileUtils.getFileName(docUri)
payload = {
payload = { # write all the necessary data to the payload object
'url': docUri,
'outputtype': toExt.replace('.', ''),
'filetype': fromExt.replace('.', ''),
@ -47,20 +48,21 @@ def getConverterUri(docUri, fromExt, toExt, docKey, isAsync, filePass = None):
headers={'accept': 'application/json'}
if (isAsync):
payload.setdefault('async', True)
if (isAsync): # check if the operation is asynchronous
payload.setdefault('async', True) # and write this information to the payload object
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[jwtHeader] = f'Bearer {headerToken}'
if jwtManager.isEnabled(): # check if a secret key to generate token exists or not
jwtHeader = 'Authorization' if config.DOC_SERV_JWT_HEADER is None or config.DOC_SERV_JWT_HEADER == '' else config.DOC_SERV_JWT_HEADER # get jwt header
headerToken = jwtManager.encode({'payload': payload}) # encode a payload object into a header token
payload['token'] = jwtManager.encode(payload) # encode a payload object into a body token
headers[jwtHeader] = f'Bearer {headerToken}' # add a header Authorization with a header token with Authorization prefix in it
response = requests.post(config.DOC_SERV_SITE_URL + 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 ) # send the headers and body values to the converter and write the result to the response
json = response.json()
return getResponseUri(json)
# get response url
def getResponseUri(json):
isEnd = json.get('endConvert')
error = json.get('error')
@ -70,6 +72,7 @@ def getResponseUri(json):
if isEnd:
return json.get('fileUrl')
# display an error that occurs during conversion
def processError(error):
prefix = 'Error occurred in the ConvertService: '

View File

@ -31,80 +31,85 @@ import os
import json
from . import jwtManager, docManager, historyManager, fileUtils, serviceConverter
# read request body
def readBody(request):
body = json.loads(request.body)
if (jwtManager.isEnabled()):
token = body.get('token')
if (jwtManager.isEnabled()): # if the secret key to generate token exists
token = body.get('token') # get the document token
if (not token):
if (not token): # if JSON web token is not received
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)
token = request.headers.get(jwtHeader) # get it from the Authorization header
if token:
token = token[len('Bearer '):]
token = token[len('Bearer '):] # and save it without Authorization prefix
if (not token):
raise Exception('Expected JWT')
if (not token): # if the token is not received
raise Exception('Expected JWT') # an error occurs
body = jwtManager.decode(token)
if (body.get('payload')):
if (body.get('payload')): # get the payload object from the request body
body = body['payload']
return body
# file saving process
def processSave(body, filename, usAddr):
download = body.get('url')
changesUri = body.get('changesurl')
newFilename = filename
curExt = fileUtils.getFileExt(filename)
downloadExt = fileUtils.getFileExt(download)
curExt = fileUtils.getFileExt(filename) # get current file extension
downloadExt = fileUtils.getFileExt(download) # get the extension of the downloaded file
# convert downloaded file to the file with the current extension if these extensions aren't equal
if (curExt != downloadExt):
try:
newUri = serviceConverter.getConverterUri(download, downloadExt, curExt, docManager.generateRevisionId(download), False)
newUri = serviceConverter.getConverterUri(download, downloadExt, curExt, docManager.generateRevisionId(download), False) # convert file and give url to a new file
if not newUri:
newFilename = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + downloadExt, usAddr)
newFilename = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + downloadExt, usAddr) # get the correct file name if it already exists
else:
download = newUri
except Exception:
newFilename = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + downloadExt, usAddr)
path = docManager.getStoragePath(newFilename, usAddr)
path = docManager.getStoragePath(newFilename, usAddr) # get the file path
histDir = historyManager.getHistoryDir(path)
if not os.path.exists(histDir):
os.makedirs(histDir)
histDir = historyManager.getHistoryDir(path) # get the path to the history direction
if not os.path.exists(histDir): # if the path doesn't exist
os.makedirs(histDir) # create it
versionDir = historyManager.getNextVersionDir(histDir)
versionDir = historyManager.getNextVersionDir(histDir) # get the path to the next file version
os.rename(docManager.getStoragePath(filename, usAddr), historyManager.getPrevFilePath(versionDir, curExt))
docManager.saveFileFromUri(download, path)
docManager.saveFileFromUri(changesUri, historyManager.getChangesZipPath(versionDir))
os.rename(docManager.getStoragePath(filename, usAddr), historyManager.getPrevFilePath(versionDir, curExt)) # get the path to the previous file version and rename the storage path with it
docManager.saveFileFromUri(download, path) # save file to the storage path
docManager.saveFileFromUri(changesUri, historyManager.getChangesZipPath(versionDir)) # save file changes to the diff.zip archive
hist = None
hist = body.get('changeshistory')
if (not hist) & ('history' in body):
hist = json.dumps(body.get('history'))
if hist:
historyManager.writeFile(historyManager.getChangesHistoryPath(versionDir), hist)
historyManager.writeFile(historyManager.getChangesHistoryPath(versionDir), hist) # write the history changes to the changes.json file
historyManager.writeFile(historyManager.getKeyPath(versionDir), body.get('key'))
historyManager.writeFile(historyManager.getKeyPath(versionDir), body.get('key')) # write the key value to the key.txt file
forcesavePath = docManager.getForcesavePath(newFilename, usAddr, False)
if (forcesavePath != ""):
os.remove(forcesavePath)
forcesavePath = docManager.getForcesavePath(newFilename, usAddr, False) # get the path to the forcesaved file version
if (forcesavePath != ""): # if the forcesaved file version exists
os.remove(forcesavePath) # remove it
return
# file force saving process
def processForceSave(body, filename, usAddr):
download = body.get('url')
curExt = fileUtils.getFileExt(filename)
downloadExt = fileUtils.getFileExt(download)
curExt = fileUtils.getFileExt(filename) # get current file extension
downloadExt = fileUtils.getFileExt(download) # get the extension of the downloaded file
newFilename = False
# convert downloaded file to the file with the current extension if these extensions aren't equal
if (curExt != downloadExt):
try:
newUri = serviceConverter.getConverterUri(download, downloadExt, curExt, docManager.generateRevisionId(download), False)
newUri = serviceConverter.getConverterUri(download, downloadExt, curExt, docManager.generateRevisionId(download), False) # convert file and give url to a new file
if not newUri:
newFilename = True
else:
@ -112,11 +117,11 @@ def processForceSave(body, filename, usAddr):
except Exception:
newFilename = True
isSubmitForm = body.get('forcesavetype') == 3
isSubmitForm = body.get('forcesavetype') == 3 # SubmitForm
if(isSubmitForm):
if (newFilename):
filename = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + "-form" + downloadExt, usAddr)
filename = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + "-form" + downloadExt, usAddr) # get the correct file name if it already exists
else :
filename = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + "-form" + curExt, usAddr)
forcesavePath = docManager.getStoragePath(filename, usAddr)
@ -130,10 +135,11 @@ def processForceSave(body, filename, usAddr):
docManager.saveFileFromUri(download, forcesavePath)
if(isSubmitForm):
uid = body['actions'][0]['userid']
historyManager.createMetaData(filename, uid, "Filling Form", usAddr)
uid = body['actions'][0]['userid'] # get the user id
historyManager.createMetaData(filename, uid, "Filling Form", usAddr) # create meta data for forcesaved file
return
# create a command request
def commandRequest(method, key):
documentCommandUrl = config.DOC_SERV_SITE_URL + config.DOC_SERV_COMMAND_URL
@ -144,12 +150,12 @@ def commandRequest(method, key):
headers={'accept': 'application/json'}
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})
headers[jwtHeader] = f'Bearer {headerToken}'
if jwtManager.isEnabled(): # check if a secret key to generate token exists or not
jwtHeader = 'Authorization' if config.DOC_SERV_JWT_HEADER is None or config.DOC_SERV_JWT_HEADER == '' else config.DOC_SERV_JWT_HEADER # get jwt header
headerToken = jwtManager.encode({'payload': payload}) # encode a payload object into a header token
headers[jwtHeader] = f'Bearer {headerToken}' # add a header Authorization with a header token with Authorization prefix in it
payload['token'] = jwtManager.encode(payload)
payload['token'] = jwtManager.encode(payload) # encode a payload object into a body token
response = requests.post(documentCommandUrl, json=payload, headers=headers)

View File

@ -47,11 +47,12 @@ USERS = [
DEFAULT_USER = USERS[0]
# get user information from the request
def getUserFromReq(req):
uid = req.COOKIES.get('uid')
uname = req.COOKIES.get('uname')
if (not uid) | (not uname):
return DEFAULT_USER
if (not uid) | (not uname): # check if we got both the user id and name parameters
return DEFAULT_USER # if not, return default user values
else:
return { 'uid': unquote(uid), 'uname': unquote(uname) }

View File

@ -36,31 +36,33 @@ from django.shortcuts import render
from src.utils import docManager, fileUtils, serviceConverter, users, jwtManager, historyManager, trackManager
# upload a file from the document storage service to the document editing service
def upload(request):
response = {}
try:
fileInfo = request.FILES['uploadedFile']
if ((fileInfo.size > config.FILE_SIZE_MAX) | (fileInfo.size <= 0)):
if ((fileInfo.size > config.FILE_SIZE_MAX) | (fileInfo.size <= 0)): # check if the file size exceeds the maximum size allowed (5242880)
raise Exception('File size is incorrect')
curExt = fileUtils.getFileExt(fileInfo.name)
if not docManager.isSupportedExt(curExt):
if not docManager.isSupportedExt(curExt): # check if the file extension is supported by the document manager
raise Exception('File type is not supported')
name = docManager.getCorrectName(fileInfo.name, request)
name = docManager.getCorrectName(fileInfo.name, request) # get file name with an index if such a file name already exists
path = docManager.getStoragePath(name, request)
docManager.createFile(fileInfo.file, path, request, True)
docManager.createFile(fileInfo.file, path, request, True) # create file with meta information in the storage directory
response.setdefault('filename', name)
response.setdefault('documentType', fileUtils.getFileType(name))
except Exception as e:
response.setdefault('error', e.args[0])
except Exception as e: # if an error occurs
response.setdefault('error', e.args[0]) # save an error message to the response variable
return HttpResponse(json.dumps(response), content_type='application/json')
return HttpResponse(json.dumps(response), content_type='application/json') # return http response in json format
# convert a file from one format to another
def convert(request):
response = {}
@ -71,30 +73,31 @@ def convert(request):
fileUri = docManager.getFileUri(filename, True,request)
fileExt = fileUtils.getFileExt(filename)
fileType = fileUtils.getFileType(filename)
newExt = docManager.getInternalExtension(fileType)
newExt = docManager.getInternalExtension(fileType) # internal editor extensions: .docx, .xlsx or .pptx
if docManager.isCanConvert(fileExt):
key = docManager.generateFileKey(filename, request)
if docManager.isCanConvert(fileExt): # check if the file extension is available for converting
key = docManager.generateFileKey(filename, request) # generate the file key
newUri = serviceConverter.getConverterUri(fileUri, fileExt, newExt, key, True, filePass)
newUri = serviceConverter.getConverterUri(fileUri, fileExt, newExt, key, True, filePass) # get the url of the converted file
if not newUri:
if not newUri: # if the converter url is not received, the original file name is passed to the response
response.setdefault('step', '0')
response.setdefault('filename', filename)
else:
correctName = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + newExt, request)
correctName = docManager.getCorrectName(fileUtils.getFileNameWithoutExt(filename) + newExt, request) # otherwise, create a new name with the necessary extension
path = docManager.getStoragePath(correctName, request)
docManager.saveFileFromUri(newUri, path, request, True)
docManager.removeFile(filename, request)
response.setdefault('filename', correctName)
docManager.saveFileFromUri(newUri, path, request, True) # save the file from the new url in the storage directory
docManager.removeFile(filename, request) # remove the original file
response.setdefault('filename', correctName) # pass the name of the converted file to the response
else:
response.setdefault('filename', filename)
response.setdefault('filename', filename) # if the file can't be converted, the original file name is passed to the response
except Exception as e:
response.setdefault('error', e.args[0])
return HttpResponse(json.dumps(response), content_type='application/json')
# create a new file
def createNew(request):
response = {}
@ -102,15 +105,16 @@ def createNew(request):
fileType = request.GET['fileType']
sample = request.GET.get('sample', False)
filename = docManager.createSample(fileType, sample, request)
filename = docManager.createSample(fileType, sample, request) # create a new sample file of the necessary type
return HttpResponseRedirect(f'edit?filename={filename}')
return HttpResponseRedirect(f'edit?filename={filename}') # return http response with redirection url
except Exception as e:
response.setdefault('error', e.args[0])
return HttpResponse(json.dumps(response), content_type='application/json')
# edit a file
def edit(request):
filename = fileUtils.getFileName(request.GET['filename'])
@ -120,7 +124,7 @@ def edit(request):
fileUriUser = docManager.getFileUri(filename, False, request)
docKey = docManager.generateFileKey(filename, request)
fileType = fileUtils.getFileType(filename)
user = users.getUserFromReq(request)
user = users.getUserFromReq(request) # get user id and name
userGroup = None
reviewGroups = None
if (user['uid'] == 'uid-2'):
@ -130,32 +134,33 @@ def edit(request):
userGroup = 'group-3'
reviewGroups = ['group-2']
edMode = request.GET.get('mode') if request.GET.get('mode') else 'edit'
canEdit = docManager.isCanEdit(ext)
submitForm = canEdit & ((edMode == 'edit') | (edMode == 'fillForms'))
mode = 'edit' if canEdit & (edMode != 'view') else 'view'
edMode = request.GET.get('mode') if request.GET.get('mode') else 'edit' # get the editor mode: view/edit/review/comment/fillForms/embedded (the default mode is edit)
canEdit = docManager.isCanEdit(ext) # check if the file with this extension can be edited
submitForm = canEdit & ((edMode == 'edit') | (edMode == 'fillForms')) # if the Submit form button is displayed or hidden
mode = 'edit' if canEdit & (edMode != 'view') else 'view' # if the file can't be edited, the mode is view
edType = request.GET.get('type') if request.GET.get('type') else 'desktop'
lang = request.COOKIES.get('ulang') if request.COOKIES.get('ulang') else 'en'
edType = request.GET.get('type') if request.GET.get('type') else 'desktop' # get the editor type: embedded/mobile/desktop (the default type is desktop)
lang = request.COOKIES.get('ulang') if request.COOKIES.get('ulang') else 'en' # get the editor language (the default language is English)
storagePath = docManager.getStoragePath(filename, request)
meta = historyManager.getMeta(storagePath)
meta = historyManager.getMeta(storagePath) # get the document meta data
infObj = None
actionData = request.GET.get('actionLink')
actionData = request.GET.get('actionLink') # get the action data that will be scrolled to (comment or bookmark)
actionLink = json.loads(actionData) if actionData else None
if (meta):
infObj = {
if (meta): # if the document meta data exists,
infObj = { # write author and creation time parameters to the information object
'owner': meta['uname'],
'uploaded': meta['created']
}
else:
else: # otherwise, write current meta information to this object
infObj = {
'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
# specify the document config
edConfig = {
'type': edType,
'documentType': fileType,
@ -165,10 +170,12 @@ def edit(request):
'fileType': ext[1:],
'key': docKey,
'info': infObj,
'permissions': {
'permissions': { # the permission for the document to be edited and downloaded or not
'comment': (edMode != 'view') & (edMode != 'fillForms') & (edMode != 'embedded') & (edMode != "blockcontent"),
'download': True,
'copy': False if user['uid'] == 'uid-2' else True,
'download': False if user['uid'] == 'uid-2' else True,
'edit': canEdit & ((edMode == 'edit') | (edMode == 'view') | (edMode == 'filter') | (edMode == "blockcontent")),
'print': False if user['uid'] == 'uid-2' else True,
'fillForms': (edMode != 'view') & (edMode != 'comment') & (edMode != 'embedded') & (edMode != "blockcontent"),
'modifyFilter': edMode != 'filter',
'modifyContentControl': edMode != "blockcontent",
@ -180,94 +187,100 @@ def edit(request):
'actionLink': actionLink,
'mode': mode,
'lang': lang,
'callbackUrl': docManager.getCallbackUrl(filename, request),
'callbackUrl': docManager.getCallbackUrl(filename, request), # absolute URL to the document storage service
'createUrl': docManager.getCreateUrl(edType, request),
'user': {
'user': { # the user currently viewing or editing the document
'id': user['uid'],
'name': None if user['uid'] == 'uid-0' else user['uname'],
'group': userGroup
},
'embedded': {
'saveUrl': fileUriUser,
'embedUrl': fileUriUser,
'shareUrl': fileUriUser,
'toolbarDocked': 'top'
'embedded': { # the parameters for the embedded document type
'saveUrl': fileUriUser, # the absolute URL that will allow the document to be saved onto the user personal computer
'embedUrl': fileUriUser, # the absolute URL to the document serving as a source file for the document embedded into the web page
'shareUrl': fileUriUser, # the absolute URL that will allow other users to share this document
'toolbarDocked': 'top' # the place for the embedded viewer toolbar (top or bottom)
},
'customization': {
'about': True,
'feedback': True,
'forcesave': False,
'submitForm': submitForm,
'goback': {
'url': docManager.getServerUrl(False, request)
'customization': { # the parameters for the editor interface
'about': True, # the About section display
'feedback': True, # the Feedback & Support menu button display
'forcesave': False, # adds the request for the forced file saving to the callback handler
'submitForm': submitForm, # if the Submit form button is displayed or not
'goback': { # settings for the Open file location menu button and upper right corner button
'url': docManager.getServerUrl(False, request) # the absolute URL to the website address which will be opened when clicking the Open file location menu button
}
}
}
}
# an image which will be inserted into the document
dataInsertImage = {
'fileType': 'png',
'url': docManager.getServerUrl(True, request) + 'static/images/logo.png'
}
# a document which will be compared with the current document
dataCompareFile = {
'fileType': 'docx',
'url': docManager.getServerUrl(True, request) + 'static/sample.docx'
}
# recipient data for mail merging
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)
if jwtManager.isEnabled(): # if the secret key to generate token exists
edConfig['token'] = jwtManager.encode(edConfig) # encode the edConfig object into a token
dataInsertImage['token'] = jwtManager.encode(dataInsertImage) # encode the dataInsertImage object into a token
dataCompareFile['token'] = jwtManager.encode(dataCompareFile) # encode the dataCompareFile object into a token
dataMailMergeRecipients['token'] = jwtManager.encode(dataMailMergeRecipients) # encode the dataMailMergeRecipients object into a token
hist = historyManager.getHistoryObject(storagePath, filename, docKey, fileUri, request)
hist = historyManager.getHistoryObject(storagePath, filename, docKey, fileUri, request) # get the document history
context = {
'cfg': json.dumps(edConfig),
'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_SITE_URL + config.DOC_SERV_API_URL,
'dataInsertImage': json.dumps(dataInsertImage)[1 : len(json.dumps(dataInsertImage)) - 1],
'dataCompareFile': dataCompareFile,
'dataMailMergeRecipients': json.dumps(dataMailMergeRecipients)
context = { # the data that will be passed to the template
'cfg': json.dumps(edConfig), # the document config in json format
'history': json.dumps(hist['history']) if 'history' in hist else None, # the information about the current version
'historyData': json.dumps(hist['historyData']) if 'historyData' in hist else None, # the information about the previous document versions if they exist
'fileType': fileType, # the file type of the document (text, spreadsheet or presentation)
'apiUrl': config.DOC_SERV_SITE_URL + config.DOC_SERV_API_URL, # the absolute URL to the api
'dataInsertImage': json.dumps(dataInsertImage)[1 : len(json.dumps(dataInsertImage)) - 1], # the image which will be inserted into the document
'dataCompareFile': dataCompareFile, # document which will be compared with the current document
'dataMailMergeRecipients': json.dumps(dataMailMergeRecipients) # recipient data for mail merging
}
return render(request, 'editor.html', context)
return render(request, 'editor.html', context) # execute the "editor.html" template with context data
# track the document changes
def track(request):
response = {}
try:
body = trackManager.readBody(request)
status = body['status']
body = trackManager.readBody(request) # read request body
status = body['status'] # and get status from it
if (status == 1): # Editing
if (body['actions'] and body['actions'][0]['type'] == 0):# finished edit
user = body['actions'][0]['userid']
if (status == 1): # editing
if (body['actions'] and body['actions'][0]['type'] == 0): # finished edit
user = body['actions'][0]['userid'] # the user who finished editing
if (not user in body['users']):
trackManager.commandRequest('forcesave', body['key'])
trackManager.commandRequest('forcesave', body['key']) # create a command request with the forcasave method
filename = fileUtils.getFileName(request.GET['filename'])
usAddr = request.GET['userAddress']
if (status == 2) | (status == 3): # mustsave, corrupted
if (status == 2) | (status == 3): # mustsave, corrupted
trackManager.processSave(body, filename, usAddr)
if (status == 6) | (status == 7): # mustforcesave, corruptedforcesave
if (status == 6) | (status == 7): # mustforcesave, corruptedforcesave
trackManager.processForceSave(body, filename, usAddr)
except Exception as e:
response.setdefault('error', 1)
response.setdefault('error', 1) # set the default error value as 1 (document key is missing or no document with such key could be found)
response.setdefault('message', e.args[0])
response.setdefault('error', 0)
response.setdefault('error', 0) # if no exceptions are raised, the default error value is 0 (no errors)
# the response status is 200 if the changes are saved successfully; otherwise, it is equal to 500
return HttpResponse(json.dumps(response), content_type='application/json', status=200 if response['error'] == 0 else 500)
# remove a file
def remove(request):
filename = fileUtils.getFileName(request.GET['filename'])
@ -278,6 +291,7 @@ def remove(request):
response.setdefault('success', True)
return HttpResponse(json.dumps(response), content_type='application/json')
# get file information
def files(request):
try:
response = docManager.getFilesInfo(request)
@ -286,14 +300,16 @@ def files(request):
response.setdefault('error', e.args[0])
return HttpResponse(json.dumps(response), content_type='application/json')
# download a csv file
def csv(request):
filePath = os.path.join('assets', 'sample', "csv.csv")
response = docManager.download(filePath)
return response
# download a file
def download(request):
try:
fileName = fileUtils.getFileName(request.GET['fileName'])
fileName = fileUtils.getFileName(request.GET['fileName']) # get the file name
userAddress = request.GET.get('userAddress') if request.GET.get('userAddress') else request
if (jwtManager.isEnabled()):
@ -306,10 +322,10 @@ def download(request):
except Exception:
return HttpResponse('JWT validation failed', status=403)
filePath = docManager.getForcesavePath(fileName, userAddress, False)
filePath = docManager.getForcesavePath(fileName, userAddress, False) # get the path to the forcesaved file version
if (filePath == ""):
filePath = docManager.getStoragePath(fileName, userAddress)
response = docManager.download(filePath)
filePath = docManager.getStoragePath(fileName, userAddress) # get file from the storage directory
response = docManager.download(filePath) # download this file
return response
except Exception:
response = {}

View File

@ -32,13 +32,13 @@ from django.shortcuts import render
from src.utils import users
from src.utils import docManager
def default(request):
def default(request): # default parameters that will be passed to the template
context = {
'users': users.USERS,
'languages': docManager.LANGUAGES,
'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)
'editExt': json.dumps(config.DOC_SERV_EDITED), # file extensions that can be edited
'convExt': json.dumps(config.DOC_SERV_CONVERT), # file extensions that can be converted
'files': docManager.getStoredFiles(request) # information about stored files
}
return render(request, 'index.html', context)
return render(request, 'index.html', context) # execute the "index.html" template with context data and return http response in json format