Skip to content

Commit 1229d97

Browse files
Merge pull request #450 from ONLYOFFICE/feature/wopi-save-template
Feature/wopi save template
2 parents ef01431 + 28bdee7 commit 1229d97

File tree

2 files changed

+53
-6
lines changed

2 files changed

+53
-6
lines changed

Common/sources/constants.js

+13
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,16 @@ exports.FILE_STATUS_UPDATE_VERSION = 'updateversion';
281281

282282
exports.ACTIVEMQ_QUEUE_PREFIX = 'queue://';
283283
exports.ACTIVEMQ_TOPIC_PREFIX = 'topic://';
284+
285+
exports.TEMPLATES_FOLDER_LOCALE_COLLISON_MAP = {
286+
'en': 'en-US',
287+
'pt': 'pt-BR',
288+
'zh': 'zh-CH',
289+
'pt-PT': 'pt-PT',
290+
'zh-TW': 'zh-TW'
291+
};
292+
exports.SUPPORTED_TEMPLATES_EXTENSIONS = {
293+
'Word': ['docx', 'docxf'],
294+
'Excel': ['xlsx'],
295+
'PowerPoint': ['pptx']
296+
};

DocService/sources/wopiClient.js

+40-6
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ const {URL} = require('url');
3838
const co = require('co');
3939
const jwt = require('jsonwebtoken');
4040
const config = require('config');
41+
const { createReadStream } = require('fs');
42+
const { lstat, readdir } = require('fs/promises');
4143
const utf7 = require('utf7');
4244
const mimeDB = require('mime-db');
4345
const xmlbuilder2 = require('xmlbuilder2');
4446
const logger = require('./../../Common/sources/logger');
4547
const utils = require('./../../Common/sources/utils');
4648
const constants = require('./../../Common/sources/constants');
4749
const commonDefines = require('./../../Common/sources/commondefines');
50+
const formatChecker = require('./../../Common/sources/formatchecker');
4851
const operationContext = require('./../../Common/sources/operationContext');
4952
const tenantManager = require('./../../Common/sources/tenantManager');
5053
const sqlBase = require('./databaseConnectors/baseConnector');
@@ -57,6 +60,7 @@ const cfgTokenOutboxExpires = config.get('services.CoAuthoring.token.outbox.expi
5760
const cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.browser');
5861
const cfgCallbackRequestTimeout = config.get('services.CoAuthoring.server.callbackRequestTimeout');
5962
const cfgAllowPrivateIPAddressForSignedRequests = config.get('services.CoAuthoring.server.allowPrivateIPAddressForSignedRequests');
63+
const cfgNewFileTemplate = config.get('services.CoAuthoring.server.newFileTemplate');
6064
const cfgDownloadTimeout = config.get('FileConverter.converter.downloadTimeout');
6165
const cfgWopiFileInfoBlockList = config.get('wopi.fileInfoBlockList');
6266
const cfgWopiWopiZone = config.get('wopi.wopiZone');
@@ -80,6 +84,9 @@ const cfgWopiExponentOld = config.get('wopi.exponentOld');
8084
const cfgWopiPrivateKeyOld = config.get('wopi.privateKeyOld');
8185
const cfgWopiHost = config.get('wopi.host');
8286

87+
let templatesFolderLocalesCache = null;
88+
const templateFilesSizeCache = {};
89+
8390
let mimeTypesByExt = (function() {
8491
let mimeTypesByExt = {};
8592
for (let mimeType in mimeDB) {
@@ -169,13 +176,12 @@ function discovery(req, res) {
169176
xmlApp.ele('action', {name: 'view', ext: ext.edit[j], urlsrc: urlTemplateView}).up();
170177
xmlApp.ele('action', {name: 'embedview', ext: ext.edit[j], urlsrc: urlTemplateEmbedView}).up();
171178
xmlApp.ele('action', {name: 'mobileView', ext: ext.edit[j], urlsrc: urlTemplateMobileView}).up();
172-
if ("oform" !== ext.edit[j]) {
173-
//todo config
174-
xmlApp.ele('action', {name: 'editnew', ext: ext.edit[j], requires: 'locks,update', urlsrc: urlTemplateEdit}).up();
175-
}
176179
xmlApp.ele('action', {name: 'edit', ext: ext.edit[j], default: 'true', requires: 'locks,update', urlsrc: urlTemplateEdit}).up();
177180
xmlApp.ele('action', {name: 'mobileEdit', ext: ext.edit[j], requires: 'locks,update', urlsrc: urlTemplateMobileEdit}).up();
178181
}
182+
constants.SUPPORTED_TEMPLATES_EXTENSIONS[name].forEach(
183+
extension => xmlApp.ele('action', {name: 'editnew', ext: extension, requires: 'locks,update', urlsrc: urlTemplateEdit}).up()
184+
);
179185
xmlApp.up();
180186
}
181187
//end section for MS WOPI connectors
@@ -373,6 +379,7 @@ function getEditorHtml(req, res) {
373379
try {
374380
ctx.initFromRequest(req);
375381
yield ctx.initTenantCache();
382+
const tenNewFileTemplate = ctx.getCfg('services.CoAuthoring.server.newFileTemplate', cfgNewFileTemplate);
376383
const tenTokenEnableBrowser = ctx.getCfg('services.CoAuthoring.token.enable.browser', cfgTokenEnableBrowser);
377384
const tenTokenOutboxAlgorithm = ctx.getCfg('services.CoAuthoring.token.outbox.algorithm', cfgTokenOutboxAlgorithm);
378385
const tenTokenOutboxExpires = ctx.getCfg('services.CoAuthoring.token.outbox.expires', cfgTokenOutboxExpires);
@@ -390,6 +397,8 @@ function getEditorHtml(req, res) {
390397
let mode = req.params.mode;
391398
let sc = req.query['sc'];
392399
let hostSessionId = req.query['hid'];
400+
let lang = req.query['lang'];
401+
let ui = req.query['ui'];
393402
let access_token = req.body['access_token'] || "";
394403
let access_token_ttl = parseInt(req.body['access_token_ttl']) || 0;
395404

@@ -433,13 +442,38 @@ function getEditorHtml(req, res) {
433442
return;
434443
}
435444
//save common info
445+
const fileType = getFileTypeByInfo(fileInfo);
436446
if (undefined === lockId) {
437-
let fileType = getFileTypeByInfo(fileInfo);
438447
lockId = crypto.randomBytes(16).toString('base64');
439448
let commonInfo = JSON.stringify({lockId: lockId, fileInfo: fileInfo});
440449
yield canvasService.commandOpenStartPromise(ctx, docId, utils.getBaseUrlByRequest(ctx, req), commonInfo, fileType);
441450
}
442451

452+
// TODO: throw error if format not supported?
453+
if (fileInfo.Size === 0 && fileType.length !== 0) {
454+
const wopiParams = getWopiParams(undefined, fileInfo, wopiSrc, access_token, access_token_ttl);
455+
456+
if (templatesFolderLocalesCache === null) {
457+
const dirContent = yield readdir(`${tenNewFileTemplate}/`, { withFileTypes: true });
458+
templatesFolderLocalesCache = dirContent.filter(dirObject => dirObject.isDirectory()).map(dirObject => dirObject.name);
459+
}
460+
461+
const localePrefix = lang || ui || 'en';
462+
let locale = constants.TEMPLATES_FOLDER_LOCALE_COLLISON_MAP[localePrefix] ?? templatesFolderLocalesCache.find(locale => locale.startsWith(localePrefix));
463+
if (locale === undefined) {
464+
locale = 'en-US';
465+
}
466+
467+
const filePath = `${tenNewFileTemplate}/${locale}/new.${fileType}`;
468+
if (!templateFilesSizeCache[filePath]) {
469+
templateFilesSizeCache[filePath] = yield lstat(filePath);
470+
}
471+
472+
const templateFileInfo = templateFilesSizeCache[filePath];
473+
const templateFileStream = createReadStream(filePath);
474+
yield putFile(ctx, wopiParams, undefined, templateFileStream, templateFileInfo.size, fileInfo.UserId, false, false, false);
475+
}
476+
443477
//Lock
444478
if ('view' !== mode) {
445479
let lockRes = yield lock(ctx, 'LOCK', lockId, fileInfo, userAuth);
@@ -506,7 +540,7 @@ function getConverterHtml(req, res) {
506540
return;
507541
}
508542

509-
let wopiParams = getWopiParams(null, fileInfo, wopiSrc, access_token, access_token_ttl);
543+
let wopiParams = getWopiParams(undefined, fileInfo, wopiSrc, access_token, access_token_ttl);
510544

511545
let docId = yield converterService.convertAndEdit(ctx, wopiParams, ext, targetext);
512546
if (docId) {

0 commit comments

Comments
 (0)