diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index dc343b1e..577b385b 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -1649,7 +1649,7 @@ exports.install = function(server, callbackFunction) { yield* onCursor(ctx, conn, data); break; case 'getLock' : - yield* getLock(ctx, conn, data, false); + yield getLock(ctx, conn, data, false); break; case 'saveChanges' : yield* saveChanges(ctx, conn, data); @@ -1834,7 +1834,7 @@ exports.install = function(server, callbackFunction) { } } //Release locks - userLocks = yield* removeUserLocks(ctx, docId, conn.user.id); + userLocks = yield removeUserLocks(ctx, docId, conn.user.id); if (0 < userLocks.length) { //todo send nothing in case of close document //sendReleaseLock(conn, userLocks); @@ -1927,31 +1927,19 @@ exports.install = function(server, callbackFunction) { return objChangesDocument; } - function* getAllLocks(ctx, docId) { - var docLockRes = []; - var docLock = yield editorData.getLocks(ctx, docId); - for (var i = 0; i < docLock.length; ++i) { - docLockRes.push(docLock[i]); - } - return docLockRes; - } - function* removeUserLocks(ctx, docId, userId) { - var userLocks = [], i; - var toCache = []; - var docLock = yield* getAllLocks(ctx, docId); - for (i = 0; i < docLock.length; ++i) { - var elem = docLock[i]; - if (elem.user === userId) { - userLocks.push(elem); - } else { - toCache.push(elem); + async function removeUserLocks(ctx, docId, userId) { + let locks = await editorData.getLocks(ctx, docId); + let res = []; + let toRemove = {}; + for (let lockId in locks) { + let lock = locks[lockId]; + if (lock.user === userId) { + toRemove[lockId] = lock; + res.push(lock); } } - //remove all - yield editorData.removeLocks(ctx, docId); - //set all - yield editorData.addLocks(ctx, docId, toCache); - return userLocks; + await editorData.removeLocks(ctx, docId, toRemove); + return res; } function* checkEndAuthLock(ctx, unlock, isSave, docId, userId, releaseLocks, deleteIndex, conn) { @@ -1987,7 +1975,7 @@ exports.install = function(server, callbackFunction) { //Release locks if (releaseLocks && conn) { - const userLocks = yield* removeUserLocks(ctx, docId, userId); + const userLocks = yield removeUserLocks(ctx, docId, userId); if (0 < userLocks.length) { sendReleaseLock(ctx, conn, userLocks); yield publish(ctx, { @@ -2070,20 +2058,20 @@ exports.install = function(server, callbackFunction) { // Recalculation only for foreign Lock when saving on a client that added/deleted rows or columns function _recalcLockArray(userId, _locks, oRecalcIndexColumns, oRecalcIndexRows) { + let res = {}; if (null == _locks) { - return false; + return res; } - var count = _locks.length; var element = null, oRangeOrObjectId = null; - var i; var sheetId = -1; - var isModify = false; - for (i = 0; i < count; ++i) { + for (let lockId in _locks) { + let isModify = false; + let lock = _locks[lockId]; // we do not count for ourselves - if (userId === _locks[i].user) { + if (userId === lock.user) { continue; } - element = _locks[i].block; + element = lock.block; if (c_oAscLockTypeElem.Range !== element["type"] || c_oAscLockTypeElemSubType.InsertColumns === element["subType"] || c_oAscLockTypeElemSubType.InsertRows === element["subType"]) { @@ -2105,8 +2093,11 @@ exports.install = function(server, callbackFunction) { oRangeOrObjectId["r2"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r2"]); isModify = true; } + if (isModify) { + res[lockId] = lock; + } } - return isModify; + return res; } function _addRecalcIndex(oRecalcIndex) { @@ -2745,7 +2736,7 @@ exports.install = function(server, callbackFunction) { if (bIsSuccessRestore) { // check locks var arrayBlocks = data['block']; - var getLockRes = yield* getLock(ctx, conn, data, true); + var getLockRes = yield getLock(ctx, conn, data, true); if (arrayBlocks && (0 === arrayBlocks.length || getLockRes)) { yield* authRestore(ctx, conn, data.sessionId); } else { @@ -2967,17 +2958,13 @@ exports.install = function(server, callbackFunction) { const tenTypesUpload = ctx.getCfg('services.CoAuthoring.utils.limits_image_types_upload', cfgTypesUpload); const docId = conn.docId; - let docLock; - if(EditorTypes.document == conn.editorType){ - docLock = {}; - let elem; - const allLocks = yield* getAllLocks(ctx, docId); - for(let i = 0 ; i < allLocks.length; ++i) { - elem = allLocks[i]; - docLock[elem.block] = elem; + let docLock = yield editorData.getLocks(ctx, docId); + if (EditorTypes.document !== conn.editorType){ + let docLockList = []; + for (let lockId in docLock) { + docLockList.push(docLock[lockId]); } - } else { - docLock = yield* getAllLocks(ctx, docId); + docLock = docLockList; } let allMessages = yield editorData.getMessages(ctx, docId); allMessages = allMessages.length > 0 ? allMessages : undefined;//todo client side @@ -3036,98 +3023,53 @@ exports.install = function(server, callbackFunction) { var messages = [msg]; yield publish(ctx, {type: commonDefines.c_oPublishType.cursor, ctx: ctx, docId: docId, userId: userId, messages: messages}, docId, userId); } - - function* getLock(ctx, conn, data, bIsRestore) { - ctx.logger.info("getLock"); - var fLock = null; + // For Word block is now string "guid" + // For Excel block is now object { sheetId, type, rangeOrObjectId, guid } + // For presentations, this is an object { type, val } or { type, slideId, objId } + async function getLock(ctx, conn, data, bIsRestore) { + ctx.logger.debug("getLock"); + var fCheckLock = null; switch (conn.editorType) { case EditorTypes.document: // Word - fLock = getLockWord; + fCheckLock = _checkLockWord; break; case EditorTypes.spreadsheet: // Excel - fLock = getLockExcel; + fCheckLock = _checkLockExcel; break; case EditorTypes.presentation: // PP - fLock = getLockPresentation; + fCheckLock = _checkLockPresentation; break; + default: + return false; } - return fLock ? yield* fLock(ctx, conn, data, bIsRestore) : false; - } - - function* getLockWord(ctx, conn, data, bIsRestore) { - var docId = conn.docId, userId = conn.user.id, arrayBlocks = data.block; - var i; - var checkRes = yield* _checkLock(ctx, docId, arrayBlocks); - var documentLocks = checkRes.documentLocks; - if (checkRes.res) { - //Ok. take lock - var toCache = []; - for (i = 0; i < arrayBlocks.length; ++i) { - var block = arrayBlocks[i]; - var elem = {time: Date.now(), user: userId, block: block}; - documentLocks[block] = elem; - toCache.push(elem); + let docId = conn.docId, userId = conn.user.id, arrayBlocks = data.block; + let locks = arrayBlocks.reduce(function(map, block) { + //todo use one id + map[block.guid || block] = {time: Date.now(), user: userId, block: block}; + return map; + }, {}); + let addRes = await editorData.addLocksNX(ctx, docId, locks); + let documentLocks = addRes.allLocks; + let isAllAdded = Object.keys(addRes.lockConflict).length === 0; + if (!isAllAdded || !fCheckLock(ctx, docId, documentLocks, arrayBlocks, userId)) { + //remove new locks + let toRemove = {}; + for (let lockId in locks) { + if (!addRes.lockConflict[lockId]) { + toRemove[lockId] = locks[lockId]; + delete documentLocks[lockId]; + } } - yield editorData.addLocks(ctx, docId, toCache); - } else if (bIsRestore) { - return false; - } - //to the one who made the request we return as quickly as possible - sendData(ctx, conn, {type: "getLock", locks: documentLocks}); - yield publish(ctx, {type: commonDefines.c_oPublishType.getLock, ctx: ctx, docId: docId, userId: userId, documentLocks: documentLocks}, docId, userId); - return true; - } - - // For Excel block is now object { sheetId, type, rangeOrObjectId, guid } - function* getLockExcel(ctx, conn, data, bIsRestore) { - var docId = conn.docId, userId = conn.user.id, arrayBlocks = data.block; - var i; - var checkRes = yield* _checkLockExcel(ctx, docId, arrayBlocks, userId); - var documentLocks = checkRes.documentLocks; - if (checkRes.res) { - //Ok. take lock - var toCache = []; - for (i = 0; i < arrayBlocks.length; ++i) { - var block = arrayBlocks[i]; - var elem = {time: Date.now(), user: userId, block: block}; - documentLocks.push(elem); - toCache.push(elem); + await editorData.removeLocks(ctx, docId, toRemove); + if (bIsRestore) { + return false; } - yield editorData.addLocks(ctx, docId, toCache); - } else if (bIsRestore) { - return false; } - //to the one who made the request we return as quickly as possible sendData(ctx, conn, {type: "getLock", locks: documentLocks}); - yield publish(ctx, {type: commonDefines.c_oPublishType.getLock, ctx: ctx, docId: docId, userId: userId, documentLocks: documentLocks}, docId, userId); - return true; - } - - // For presentations, this is an object { type, val } or { type, slideId, objId } - function* getLockPresentation(ctx, conn, data, bIsRestore) { - var docId = conn.docId, userId = conn.user.id, arrayBlocks = data.block; - var i; - var checkRes = yield* _checkLockPresentation(ctx, docId, arrayBlocks, userId); - var documentLocks = checkRes.documentLocks; - if (checkRes.res) { - //Ok. take lock - var toCache = []; - for (i = 0; i < arrayBlocks.length; ++i) { - var block = arrayBlocks[i]; - var elem = {time: Date.now(), user: userId, block: block}; - documentLocks.push(elem); - toCache.push(elem); - } - yield editorData.addLocks(ctx, docId, toCache); - } else if (bIsRestore) { - return false; - } - //to the one who made the request we return as quickly as possible - sendData(ctx, conn, {type: "getLock", locks: documentLocks}); - yield publish(ctx, {type: commonDefines.c_oPublishType.getLock, ctx: ctx, docId: docId, userId: userId, documentLocks: documentLocks}, docId, userId); + await publish(ctx, {type: commonDefines.c_oPublishType.getLock, ctx: ctx, docId: docId, userId: userId, documentLocks: documentLocks}, docId, userId); return true; } @@ -3211,14 +3153,10 @@ exports.install = function(server, callbackFunction) { const oRecalcIndexRows = _addRecalcIndex(tmpAdditionalInfo["indexRows"]); // Now we need to recalculate indexes for lock elements if (null !== oRecalcIndexColumns || null !== oRecalcIndexRows) { - const docLock = yield* getAllLocks(ctx, docId); - if (_recalcLockArray(userId, docLock, oRecalcIndexColumns, oRecalcIndexRows)) { - let toCache = []; - for (let i = 0; i < docLock.length; ++i) { - toCache.push(docLock[i]); - } - yield editorData.removeLocks(ctx, docId); - yield editorData.addLocks(ctx, docId, toCache); + let docLock = yield editorData.getLocks(ctx, docId); + let docLockMod = _recalcLockArray(userId, docLock, oRecalcIndexColumns, oRecalcIndexRows); + if (Object.keys(docLockMod).length > 0) { + yield editorData.addLocks(ctx, docId, docLockMod); } } } @@ -3226,7 +3164,7 @@ exports.install = function(server, callbackFunction) { let userLocks = []; if (data.releaseLocks) { //Release locks - userLocks = yield* removeUserLocks(ctx, docId, userId); + userLocks = yield removeUserLocks(ctx, docId, userId); } // For this user, we remove Lock from the document if the unlock flag has arrived const checkEndAuthLockRes = yield* checkEndAuthLock(ctx, data.unlock, false, docId, userId); @@ -3327,47 +3265,19 @@ exports.install = function(server, callbackFunction) { sendDataMessage(ctx, conn, allMessages); } - function* _checkLock(ctx, docId, arrayBlocks) { - // Data is array now - var isLock = false; - var allLocks = yield* getAllLocks(ctx, docId); - var documentLocks = {}; - for(var i = 0 ; i < allLocks.length; ++i) { - var elem = allLocks[i]; - documentLocks[elem.block] =elem; - } - if (arrayBlocks.length > 0) { - for (var i = 0; i < arrayBlocks.length; ++i) { - var block = arrayBlocks[i]; - ctx.logger.info("getLock id: %s", block); - if (documentLocks.hasOwnProperty(block) && documentLocks[block] !== null) { - isLock = true; - break; - } - } - } else { - isLock = true; - } - return {res: !isLock, documentLocks: documentLocks}; + function _checkLockWord(ctx, docId, documentLocks, arrayBlocks, userId) { + return true; } - - function* _checkLockExcel(ctx, docId, arrayBlocks, userId) { + function _checkLockExcel(ctx, docId, documentLocks, arrayBlocks, userId) { // Data is array now var documentLock; var isLock = false; var isExistInArray = false; var i, blockRange; - var documentLocks = yield* getAllLocks(ctx, docId); var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0; for (i = 0; i < lengthArray && false === isLock; ++i) { blockRange = arrayBlocks[i]; - for (var keyLockInArray in documentLocks) { - if (true === isLock) { - break; - } - if (!documentLocks.hasOwnProperty(keyLockInArray)) { - continue; - } + for (let keyLockInArray in documentLocks) { documentLock = documentLocks[keyLockInArray]; // Checking if an object is in an array (the current user sent a lock again) if (documentLock.user === userId && @@ -3402,41 +3312,39 @@ exports.install = function(server, callbackFunction) { continue; } isLock = compareExcelBlock(blockRange, documentLock.block); + if (true === isLock) { + break; + } } } if (0 === lengthArray) { isLock = true; } - return {res: !isLock && !isExistInArray, documentLocks: documentLocks}; + return !isLock && !isExistInArray; } - function* _checkLockPresentation(ctx, docId, arrayBlocks, userId) { + function _checkLockPresentation(ctx, docId, documentLocks, arrayBlocks, userId) { // Data is array now var isLock = false; - var i, documentLock, blockRange; - var documentLocks = yield* getAllLocks(ctx, docId); + var i, blockRange; var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0; for (i = 0; i < lengthArray && false === isLock; ++i) { blockRange = arrayBlocks[i]; - for (var keyLockInArray in documentLocks) { - if (true === isLock) { - break; - } - if (!documentLocks.hasOwnProperty(keyLockInArray)) { - continue; - } - documentLock = documentLocks[keyLockInArray]; - + for (let keyLockInArray in documentLocks) { + let documentLock = documentLocks[keyLockInArray]; if (documentLock.user === userId || !(documentLock.block)) { continue; } isLock = comparePresentationBlock(blockRange, documentLock.block); + if (true === isLock) { + break; + } } } if (0 === lengthArray) { isLock = true; } - return {res: !isLock, documentLocks: documentLocks}; + return !isLock; } function _checkLicense(ctx, conn) { diff --git a/DocService/sources/editorDataMemory.js b/DocService/sources/editorDataMemory.js index b6f3ac56..1134f0c7 100644 --- a/DocService/sources/editorDataMemory.js +++ b/DocService/sources/editorDataMemory.js @@ -140,17 +140,40 @@ EditorData.prototype.removePresenceDocument = async function(ctx, docId) {}; EditorData.prototype.addLocks = async function(ctx, docId, locks) { let data = this._getDocumentData(ctx, docId); if (!data.locks) { - data.locks = []; + data.locks = {}; } - data.locks = data.locks.concat(locks); + Object.assign(data.locks, locks); }; -EditorData.prototype.removeLocks = async function(ctx, docId) { +EditorData.prototype.addLocksNX = async function(ctx, docId, locks) { + let data = this._getDocumentData(ctx, docId); + if (!data.locks) { + data.locks = {}; + } + let lockConflict = {}; + for (let lockId in locks) { + if (undefined === data.locks[lockId]) { + data.locks[lockId] = locks[lockId]; + } else { + lockConflict[lockId] = locks[lockId]; + } + } + return {lockConflict, allLocks: data.locks}; +}; +EditorData.prototype.removeLocks = async function(ctx, docId, locks) { + let data = this._getDocumentData(ctx, docId); + if (!data.locks) { + for (let lockId in locks) { + delete data.locks[lockId]; + } + } +}; +EditorData.prototype.removeAllLocks = async function(ctx, docId) { let data = this._getDocumentData(ctx, docId); data.locks = undefined; }; EditorData.prototype.getLocks = async function(ctx, docId) { let data = this._getDocumentData(ctx, docId); - return data.locks || []; + return data.locks || {}; }; EditorData.prototype.addMessage = async function(ctx, docId, msg) {