mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-04-07 14:04:35 +08:00
[bug] Refactor Locks storage; for bug 65773
This commit is contained in:
@ -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) {
|
||||
|
||||
@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user