1488 lines
57 KiB
JavaScript
1488 lines
57 KiB
JavaScript
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2015
|
|
*
|
|
* This program is a free software product. You can redistribute it and/or
|
|
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
|
* version 3 as published by the Free Software Foundation. In accordance with
|
|
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
|
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
|
* of any third-party rights.
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
|
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
|
*
|
|
* You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
|
|
* EU, LV-1021.
|
|
*
|
|
* The interactive user interfaces in modified source and object code versions
|
|
* of the Program must display Appropriate Legal Notices, as required under
|
|
* Section 5 of the GNU AGPL version 3.
|
|
*
|
|
* Pursuant to Section 7(b) of the License you must retain the original Product
|
|
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
|
* grant you any rights under trademark law for use of our trademarks.
|
|
*
|
|
* All the Product's GUI elements, including illustrations and icon sets, as
|
|
* well as technical writing content are licensed under the terms of the
|
|
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
|
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
|
*
|
|
*/
|
|
var sockjs = require("sockjs"),
|
|
_ = require("underscore"),
|
|
https = require("https"),
|
|
http = require("http"),
|
|
url = require("url"),
|
|
logger = require("./../../Common/sources/logger"),
|
|
config = require("./config.json"),
|
|
sqlBase = require("./baseConnector");
|
|
var defaultHttpPort = 80,
|
|
defaultHttpsPort = 443;
|
|
var messages = {},
|
|
connections = [],
|
|
objServiceInfo = {},
|
|
objServicePucker = {},
|
|
arrCacheDocumentsChanges = [],
|
|
nCacheSize = 100;
|
|
var asc_coAuthV = "3.0.8";
|
|
function DocumentChanges(docId) {
|
|
this.docId = docId;
|
|
this.arrChanges = [];
|
|
return this;
|
|
}
|
|
DocumentChanges.prototype.getLength = function () {
|
|
return this.arrChanges.length;
|
|
};
|
|
DocumentChanges.prototype.push = function (change) {
|
|
this.arrChanges.push(change);
|
|
};
|
|
DocumentChanges.prototype.splice = function (start, deleteCount) {
|
|
this.arrChanges.splice(start, deleteCount);
|
|
};
|
|
DocumentChanges.prototype.concat = function (item) {
|
|
this.arrChanges = this.arrChanges.concat(item);
|
|
};
|
|
var c_oAscServerStatus = {
|
|
NotFound: 0,
|
|
Editing: 1,
|
|
MustSave: 2,
|
|
Corrupted: 3,
|
|
Closed: 4
|
|
};
|
|
var c_oAscChangeBase = {
|
|
No: 0,
|
|
Delete: 1,
|
|
All: 2
|
|
};
|
|
var c_oAscServerCommandErrors = {
|
|
NoError: 0,
|
|
DocumentIdError: 1,
|
|
ParseError: 2,
|
|
CommandError: 3
|
|
};
|
|
var c_oAscSaveTimeOutDelay = 5000;
|
|
var c_oAscLockTimeOutDelay = 500;
|
|
var c_oAscRecalcIndexTypes = {
|
|
RecalcIndexAdd: 1,
|
|
RecalcIndexRemove: 2
|
|
};
|
|
var FileStatus = {
|
|
None: 0,
|
|
Ok: 1,
|
|
WaitQueue: 2,
|
|
NeedParams: 3,
|
|
Convert: 4,
|
|
Err: 5,
|
|
ErrToReload: 6,
|
|
SaveVersion: 7,
|
|
UpdateVersion: 8
|
|
};
|
|
var c_oAscLockTypes = {
|
|
kLockTypeNone: 1,
|
|
kLockTypeMine: 2,
|
|
kLockTypeOther: 3,
|
|
kLockTypeOther2: 4,
|
|
kLockTypeOther3: 5
|
|
};
|
|
var c_oAscLockTypeElem = {
|
|
Range: 1,
|
|
Object: 2,
|
|
Sheet: 3
|
|
};
|
|
var c_oAscLockTypeElemSubType = {
|
|
DeleteColumns: 1,
|
|
InsertColumns: 2,
|
|
DeleteRows: 3,
|
|
InsertRows: 4,
|
|
ChangeProperties: 5
|
|
};
|
|
var c_oAscLockTypeElemPresentation = {
|
|
Object: 1,
|
|
Slide: 2,
|
|
Presentation: 3
|
|
};
|
|
function CRecalcIndexElement(recalcType, position, bIsSaveIndex) {
|
|
if (! (this instanceof CRecalcIndexElement)) {
|
|
return new CRecalcIndexElement(recalcType, position, bIsSaveIndex);
|
|
}
|
|
this._recalcType = recalcType;
|
|
this._position = position;
|
|
this._count = 1;
|
|
this.m_bIsSaveIndex = !!bIsSaveIndex;
|
|
return this;
|
|
}
|
|
CRecalcIndexElement.prototype = {
|
|
constructor: CRecalcIndexElement,
|
|
getLockOther: function (position, type) {
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? +1 : -1;
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && true === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && c_oAscLockTypes.kLockTypeMine === type && false === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getLockSaveOther: function (position, type) {
|
|
if (this.m_bIsSaveIndex) {
|
|
return position;
|
|
}
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? +1 : -1;
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && true === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position === this._position && c_oAscRecalcIndexTypes.RecalcIndexRemove === this._recalcType && c_oAscLockTypes.kLockTypeMine === type && false === this.m_bIsSaveIndex) {
|
|
return null;
|
|
} else {
|
|
if (position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
getLockMe: function (position) {
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? -1 : +1;
|
|
if (position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
},
|
|
getLockMe2: function (position) {
|
|
var inc = (c_oAscRecalcIndexTypes.RecalcIndexAdd === this._recalcType) ? -1 : +1;
|
|
if (true !== this.m_bIsSaveIndex || position < this._position) {
|
|
return position;
|
|
} else {
|
|
return (position + inc);
|
|
}
|
|
}
|
|
};
|
|
function CRecalcIndex() {
|
|
if (! (this instanceof CRecalcIndex)) {
|
|
return new CRecalcIndex();
|
|
}
|
|
this._arrElements = [];
|
|
return this;
|
|
}
|
|
CRecalcIndex.prototype = {
|
|
constructor: CRecalcIndex,
|
|
add: function (recalcType, position, count, bIsSaveIndex) {
|
|
for (var i = 0; i < count; ++i) {
|
|
this._arrElements.push(new CRecalcIndexElement(recalcType, position, bIsSaveIndex));
|
|
}
|
|
},
|
|
clear: function () {
|
|
this._arrElements.length = 0;
|
|
},
|
|
getLockOther: function (position, type) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = 0; i < count; ++i) {
|
|
newPosition = this._arrElements[i].getLockOther(newPosition, type);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
},
|
|
getLockSaveOther: function (position, type) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = 0; i < count; ++i) {
|
|
newPosition = this._arrElements[i].getLockSaveOther(newPosition, type);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
},
|
|
getLockMe: function (position) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = count - 1; i >= 0; --i) {
|
|
newPosition = this._arrElements[i].getLockMe(newPosition);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
},
|
|
getLockMe2: function (position) {
|
|
var newPosition = position;
|
|
var count = this._arrElements.length;
|
|
for (var i = count - 1; i >= 0; --i) {
|
|
newPosition = this._arrElements[i].getLockMe2(newPosition);
|
|
if (null === newPosition) {
|
|
break;
|
|
}
|
|
}
|
|
return newPosition;
|
|
}
|
|
};
|
|
function sendData(conn, data) {
|
|
conn.write(JSON.stringify(data));
|
|
}
|
|
function getOriginalParticipantsId(docId) {
|
|
var result = [],
|
|
tmpObject = {},
|
|
elConnection;
|
|
for (var i = 0, length = connections.length; i < length; ++i) {
|
|
elConnection = connections[i].connection;
|
|
if (elConnection.docId === docId && false === elConnection.isViewer) {
|
|
tmpObject[elConnection.user.idOriginal] = 1;
|
|
}
|
|
}
|
|
for (var name in tmpObject) {
|
|
if (tmpObject.hasOwnProperty(name)) {
|
|
result.push(name);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function sendServerRequest(server, postData, onReplyCallback) {
|
|
if (!server.host || !server.path) {
|
|
return;
|
|
}
|
|
var options = {
|
|
host: server.host,
|
|
path: server.path,
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Content-Length": postData.length
|
|
},
|
|
rejectUnauthorized: false
|
|
};
|
|
if (server.port) {
|
|
options.port = server.port;
|
|
}
|
|
var requestFunction = server.https ? https.request : http.request;
|
|
logger.info("postData: %s", postData);
|
|
var req = requestFunction(options, function (res) {
|
|
res.setEncoding("utf8");
|
|
res.on("data", function (replyData) {
|
|
logger.info("replyData: %s", replyData);
|
|
if (onReplyCallback) {
|
|
onReplyCallback(replyData);
|
|
}
|
|
});
|
|
res.on("end", function () {
|
|
logger.info("end");
|
|
});
|
|
});
|
|
req.on("error", function (e) {
|
|
logger.warn("problem with request on server: %s", e.message);
|
|
});
|
|
req.write(postData);
|
|
req.end();
|
|
}
|
|
function parseUrl(callbackUrl) {
|
|
var result = null;
|
|
try {
|
|
var parseObject = url.parse(decodeURIComponent(callbackUrl));
|
|
var isHttps = "https:" === parseObject.protocol;
|
|
var port = parseObject.port;
|
|
if (!port) {
|
|
port = isHttps ? defaultHttpsPort : defaultHttpPort;
|
|
}
|
|
result = {
|
|
"https": isHttps,
|
|
"host": parseObject.hostname,
|
|
"port": port,
|
|
"path": parseObject.path,
|
|
"href": parseObject.href
|
|
};
|
|
} catch(e) {
|
|
result = null;
|
|
}
|
|
return result;
|
|
}
|
|
function deleteCallback(id) {
|
|
sqlBase.deleteCallback(id);
|
|
delete objServiceInfo[id];
|
|
}
|
|
function sendStatusDocument(docId, bChangeBase) {
|
|
var callback = objServiceInfo[docId];
|
|
if (null == callback) {
|
|
return;
|
|
}
|
|
var status = c_oAscServerStatus.Editing;
|
|
var participants = getOriginalParticipantsId(docId);
|
|
var oPucker = objServicePucker[docId];
|
|
if (0 === participants.length && !(oPucker && oPucker.inDataBase && 0 !== oPucker.index)) {
|
|
status = c_oAscServerStatus.Closed;
|
|
}
|
|
if (c_oAscChangeBase.No !== bChangeBase) {
|
|
if (c_oAscServerStatus.Editing === status && c_oAscChangeBase.All === bChangeBase) {
|
|
sqlBase.insertInTable(sqlBase.tableId.callbacks, docId, callback.href);
|
|
} else {
|
|
if (c_oAscServerStatus.Closed === status) {
|
|
deleteCallback(docId);
|
|
}
|
|
}
|
|
}
|
|
var sendData = JSON.stringify({
|
|
"key": docId,
|
|
"status": status,
|
|
"url": "",
|
|
"users": participants
|
|
});
|
|
sendServerRequest(callback, sendData, function (replyData) {
|
|
onReplySendStatusDocument(docId, replyData);
|
|
});
|
|
}
|
|
function onReplySendStatusDocument(docId, replyData) {
|
|
if (!replyData) {
|
|
return;
|
|
}
|
|
var i, oData, users;
|
|
try {
|
|
oData = JSON.parse(replyData);
|
|
} catch(e) {
|
|
logger.error("error reply SendStatusDocument: %s docId = %s", e, docId);
|
|
oData = null;
|
|
}
|
|
if (!oData) {
|
|
return;
|
|
}
|
|
users = Array.isArray(oData) ? oData : oData.users;
|
|
if (Array.isArray(users)) {
|
|
for (i = 0; i < users.length; ++i) {
|
|
dropUserFromDocument(docId, users[i], "");
|
|
}
|
|
}
|
|
}
|
|
function dropUserFromDocument(docId, userId, description) {
|
|
var elConnection;
|
|
for (var i = 0, length = connections.length; i < length; ++i) {
|
|
elConnection = connections[i].connection;
|
|
if (elConnection.docId === docId && userId === elConnection.user.idOriginal) {
|
|
sendData(elConnection, {
|
|
type: "drop",
|
|
description: description
|
|
});
|
|
}
|
|
}
|
|
}
|
|
function removeDocumentChanges(docId) {
|
|
for (var i = 0, length = arrCacheDocumentsChanges.length; i < length; ++i) {
|
|
if (docId === arrCacheDocumentsChanges[i].docId) {
|
|
arrCacheDocumentsChanges.splice(i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
function bindEvents(docId, callback) {
|
|
var bChangeBase = c_oAscChangeBase.Delete;
|
|
if (!objServiceInfo[docId]) {
|
|
var oCallbackUrl = parseUrl(callback);
|
|
if (null === oCallbackUrl) {
|
|
return c_oAscServerCommandErrors.ParseError;
|
|
}
|
|
objServiceInfo[docId] = oCallbackUrl;
|
|
bChangeBase = c_oAscChangeBase.All;
|
|
}
|
|
sendStatusDocument(docId, bChangeBase);
|
|
}
|
|
function removeChanges(id, isCorrupted, isConvertService) {
|
|
logger.info("removeChanges: %s", id);
|
|
delete messages[id];
|
|
deleteCallback(id);
|
|
removeDocumentChanges(id);
|
|
if (!isCorrupted) {
|
|
deletePucker(id);
|
|
sqlBase.deleteChanges(id, null);
|
|
} else {
|
|
sqlBase.updateStatusFile(id);
|
|
logger.error("saved corrupted id = %s convert = %s", id, isConvertService);
|
|
}
|
|
}
|
|
function deletePucker(docId) {
|
|
sqlBase.deletePucker(docId);
|
|
delete objServicePucker[docId];
|
|
}
|
|
function _createPucker(url, documentFormatSave, indexUser, inDataBase) {
|
|
var serverUrl = parseUrl(url);
|
|
if (null === serverUrl) {
|
|
logger.error("Error server url = %s", url);
|
|
return null;
|
|
}
|
|
return {
|
|
url: url,
|
|
server: serverUrl,
|
|
documentFormatSave: documentFormatSave,
|
|
inDataBase: inDataBase,
|
|
index: 0,
|
|
indexUser: indexUser
|
|
};
|
|
}
|
|
function createPucker(docId, url, documentFormatSave, indexUser, inDataBase) {
|
|
var pucker = null;
|
|
if (!objServicePucker.hasOwnProperty(docId)) {
|
|
pucker = _createPucker(url, documentFormatSave, indexUser, inDataBase);
|
|
if (null === pucker) {
|
|
return null;
|
|
}
|
|
objServicePucker[docId] = pucker;
|
|
}
|
|
return objServicePucker[docId];
|
|
}
|
|
function updatePucker(docId, indexUser) {
|
|
var pucker = objServicePucker[docId];
|
|
var nOldIndex;
|
|
if (pucker) {
|
|
nOldIndex = pucker.indexUser;
|
|
pucker.indexUser = indexUser;
|
|
if (!pucker.inDataBase) {
|
|
sqlBase.insertInTable(sqlBase.tableId.pucker, docId, pucker.url, pucker.documentFormatSave, pucker.indexUser);
|
|
pucker.inDataBase = true;
|
|
} else {
|
|
if (nOldIndex !== pucker.indexUser) {
|
|
sqlBase.updateIndexUser(docId, pucker.indexUser);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.version = asc_coAuthV;
|
|
exports.install = function (server, callbackFunction) {
|
|
var sockjs_opts = {
|
|
sockjs_url: "./../../Common/sources/sockjs-0.3.min.js"
|
|
},
|
|
sockjs_echo = sockjs.createServer(sockjs_opts),
|
|
locks = {},
|
|
lockDocuments = {},
|
|
arrSaveLock = {},
|
|
saveTimers = {},
|
|
urlParse = new RegExp("^/doc/([0-9-.a-zA-Z_=]*)/c.+", "i");
|
|
sockjs_echo.on("connection", function (conn) {
|
|
if (null == conn) {
|
|
logger.error("null == conn");
|
|
return;
|
|
}
|
|
conn.on("data", function (message) {
|
|
try {
|
|
var data = JSON.parse(message);
|
|
switch (data.type) {
|
|
case "auth":
|
|
auth(conn, data);
|
|
break;
|
|
case "message":
|
|
onMessage(conn, data);
|
|
break;
|
|
case "getLock":
|
|
getLock(conn, data, false);
|
|
break;
|
|
case "saveChanges":
|
|
saveChanges(conn, data);
|
|
break;
|
|
case "isSaveLock":
|
|
isSaveLock(conn, data);
|
|
break;
|
|
case "unSaveLock":
|
|
unSaveLock(conn, -1);
|
|
break;
|
|
case "getMessages":
|
|
getMessages(conn, data);
|
|
break;
|
|
case "unLockDocument":
|
|
checkEndAuthLock(data.isSave, conn.docId, conn.user.id, null, conn);
|
|
break;
|
|
}
|
|
} catch(e) {
|
|
logger.error("error receiving response: %s docId = %s type = %s", e, conn ? conn.docId : "null", (data && data.type) ? data.type : "null");
|
|
}
|
|
});
|
|
conn.on("error", function () {
|
|
logger.error("On error");
|
|
});
|
|
conn.on("close", function () {
|
|
var connection = this,
|
|
userLocks, participants, reconnected, oPucker, bHasEditors, bHasChanges;
|
|
var docId = conn.docId;
|
|
if (null == docId) {
|
|
return;
|
|
}
|
|
logger.info("Connection closed or timed out");
|
|
connections = _.reject(connections, function (el) {
|
|
return el.connection.id === connection.id;
|
|
});
|
|
reconnected = _.any(connections, function (el) {
|
|
return el.connection.sessionId === connection.sessionId;
|
|
});
|
|
var state = (false == reconnected) ? false : undefined;
|
|
participants = getParticipants(docId);
|
|
sendParticipantsState(participants, state, connection);
|
|
if (!reconnected) {
|
|
if (undefined != arrSaveLock[docId] && connection.user.id == arrSaveLock[docId].user) {
|
|
if (null != arrSaveLock[docId].saveLockTimeOutId) {
|
|
clearTimeout(arrSaveLock[docId].saveLockTimeOutId);
|
|
}
|
|
arrSaveLock[docId] = undefined;
|
|
}
|
|
oPucker = objServicePucker[docId];
|
|
bHasEditors = hasEditors(docId);
|
|
bHasChanges = oPucker && oPucker.inDataBase && 0 !== oPucker.index;
|
|
if (false === connection.isViewer) {
|
|
if (!bHasEditors) {
|
|
if (null != arrSaveLock[docId] && null != arrSaveLock[docId].saveLockTimeOutId) {
|
|
clearTimeout(arrSaveLock[docId].saveLockTimeOutId);
|
|
}
|
|
arrSaveLock[docId] = undefined;
|
|
if (bHasChanges) {
|
|
_createSaveTimer(docId);
|
|
} else {
|
|
deletePucker(docId);
|
|
sendStatusDocument(docId, c_oAscChangeBase.All);
|
|
}
|
|
} else {
|
|
sendStatusDocument(docId, c_oAscChangeBase.No);
|
|
}
|
|
userLocks = getUserLocks(docId, connection.sessionId);
|
|
if (0 < userLocks.length) {
|
|
_.each(participants, function (participant) {
|
|
if (!participant.connection.isViewer) {
|
|
sendData(participant.connection, {
|
|
type: "releaseLock",
|
|
locks: _.map(userLocks, function (e) {
|
|
return {
|
|
block: e.block,
|
|
user: e.user,
|
|
time: Date.now(),
|
|
changes: null
|
|
};
|
|
})
|
|
});
|
|
}
|
|
});
|
|
}
|
|
checkEndAuthLock(false, docId, connection.user.id, participants);
|
|
} else {
|
|
if (!bHasEditors && !bHasChanges) {
|
|
deletePucker(docId);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
function getDocumentChangesCache(docId) {
|
|
var oPucker = objServicePucker[docId];
|
|
if (oPucker && oPucker.inDataBase) {
|
|
var i, length;
|
|
for (i = 0, length = arrCacheDocumentsChanges.length; i < length; ++i) {
|
|
if (docId === arrCacheDocumentsChanges[i].docId) {
|
|
return arrCacheDocumentsChanges[i];
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function getDocumentChanges(docId, callback) {
|
|
var oPucker = objServicePucker[docId];
|
|
if (oPucker && oPucker.inDataBase) {
|
|
var i, length;
|
|
for (i = 0, length = arrCacheDocumentsChanges.length; i < length; ++i) {
|
|
if (docId === arrCacheDocumentsChanges[i].docId) {
|
|
callback(arrCacheDocumentsChanges[i].arrChanges, oPucker.index);
|
|
return;
|
|
}
|
|
}
|
|
var callbackGetChanges = function (error, arrayElements) {
|
|
if (!oPucker || error) {
|
|
return;
|
|
}
|
|
var j, element;
|
|
var objChangesDocument = new DocumentChanges(docId);
|
|
for (j = 0; j < arrayElements.length; ++j) {
|
|
element = arrayElements[j];
|
|
objChangesDocument.push({
|
|
docid: docId,
|
|
change: element["dc_data"],
|
|
time: Date.parse(element["dc_date"] + " GMT"),
|
|
user: element["dc_user_id"],
|
|
useridoriginal: element["dc_user_id_original"]
|
|
});
|
|
}
|
|
oPucker.index = objChangesDocument.getLength();
|
|
arrCacheDocumentsChanges.push(objChangesDocument);
|
|
callback(objChangesDocument.arrChanges, oPucker.index);
|
|
};
|
|
sqlBase.getChanges(docId, callbackGetChanges);
|
|
return;
|
|
}
|
|
callback(undefined, 0);
|
|
}
|
|
function getUserLocks(docId, sessionId) {
|
|
var userLocks = [],
|
|
i;
|
|
var docLock = locks[docId];
|
|
if (docLock) {
|
|
if ("array" === typeOf(docLock)) {
|
|
for (i = 0; i < docLock.length; ++i) {
|
|
if (docLock[i].sessionId === sessionId) {
|
|
userLocks.push(docLock[i]);
|
|
docLock.splice(i, 1);
|
|
--i;
|
|
}
|
|
}
|
|
} else {
|
|
for (i in docLock) {
|
|
if (docLock[i].sessionId === sessionId) {
|
|
userLocks.push(docLock[i]);
|
|
delete docLock[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return userLocks;
|
|
}
|
|
function checkEndAuthLock(isSave, docId, userId, participants, currentConnection) {
|
|
var result = false;
|
|
if (lockDocuments.hasOwnProperty(docId) && userId === lockDocuments[docId].id) {
|
|
delete lockDocuments[docId];
|
|
if (!participants) {
|
|
participants = getParticipants(docId);
|
|
}
|
|
var participantsMap = _.map(participants, function (conn) {
|
|
var tmpUser = conn.connection.user;
|
|
return {
|
|
id: tmpUser.id,
|
|
username: tmpUser.name,
|
|
indexUser: tmpUser.indexUser,
|
|
view: conn.connection.isViewer
|
|
};
|
|
});
|
|
getDocumentChanges(docId, function (objChangesDocument, changesIndex) {
|
|
var connection;
|
|
for (var i = 0, l = participants.length; i < l; ++i) {
|
|
connection = participants[i].connection;
|
|
if (userId !== connection.user.id && !connection.isViewer) {
|
|
sendAuthInfo(objChangesDocument, changesIndex, connection, participantsMap);
|
|
}
|
|
}
|
|
});
|
|
result = true;
|
|
} else {
|
|
if (isSave) {
|
|
var userLocks = getUserLocks(docId, currentConnection.sessionId);
|
|
if (0 < userLocks.length) {
|
|
if (!participants) {
|
|
participants = getParticipants(docId);
|
|
}
|
|
for (var i = 0, l = participants.length; i < l; ++i) {
|
|
var connection = participants[i].connection;
|
|
if (userId !== connection.user.id && !connection.isViewer) {
|
|
sendData(connection, {
|
|
type: "releaseLock",
|
|
locks: _.map(userLocks, function (e) {
|
|
return {
|
|
block: e.block,
|
|
user: e.user,
|
|
time: Date.now(),
|
|
changes: null
|
|
};
|
|
})
|
|
});
|
|
}
|
|
}
|
|
}
|
|
unSaveLock(currentConnection, -1);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function sendParticipantsState(participants, stateConnect, oConnection) {
|
|
var tmpUser = oConnection.user;
|
|
_.each(participants, function (participant) {
|
|
if (participant.connection.user.id !== tmpUser.id) {
|
|
sendData(participant.connection, {
|
|
type: "connectState",
|
|
state: stateConnect,
|
|
id: tmpUser.id,
|
|
username: tmpUser.name,
|
|
indexUser: tmpUser.indexUser,
|
|
view: oConnection.isViewer
|
|
});
|
|
}
|
|
});
|
|
}
|
|
function sendFileError(conn, errorId) {
|
|
logger.error("error description: %s", errorId);
|
|
sendData(conn, {
|
|
type: "error",
|
|
description: errorId
|
|
});
|
|
}
|
|
function getParticipants(docId, excludeUserId, excludeViewer) {
|
|
return _.filter(connections, function (el) {
|
|
return el.connection.docId === docId && el.connection.user.id !== excludeUserId && el.connection.isViewer !== excludeViewer;
|
|
});
|
|
}
|
|
function hasEditors(docId) {
|
|
var result = false,
|
|
elConnection;
|
|
for (var i = 0, length = connections.length; i < length; ++i) {
|
|
elConnection = connections[i].connection;
|
|
if (elConnection.docId === docId && false === elConnection.isViewer) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
function sendChangesToServer(docId) {
|
|
var sendData = JSON.stringify({
|
|
"id": docId,
|
|
"c": "sfc",
|
|
"url": "/CommandService.ashx?c=saved&conv=1&key=" + docId + "&status=",
|
|
"outputformat" : objServicePucker[docId].documentFormatSave,
|
|
"data": c_oAscSaveTimeOutDelay
|
|
});
|
|
sendServerRequest(objServicePucker[docId].server, sendData);
|
|
}
|
|
function _recalcLockArray(userId, _locks, oRecalcIndexColumns, oRecalcIndexRows) {
|
|
if (null == _locks) {
|
|
return;
|
|
}
|
|
var count = _locks.length;
|
|
var element = null,
|
|
oRangeOrObjectId = null;
|
|
var i;
|
|
var sheetId = -1;
|
|
for (i = 0; i < count; ++i) {
|
|
if (userId === _locks[i].user) {
|
|
continue;
|
|
}
|
|
element = _locks[i].block;
|
|
if (c_oAscLockTypeElem.Range !== element["type"] || c_oAscLockTypeElemSubType.InsertColumns === element["subType"] || c_oAscLockTypeElemSubType.InsertRows === element["subType"]) {
|
|
continue;
|
|
}
|
|
sheetId = element["sheetId"];
|
|
oRangeOrObjectId = element["rangeOrObjectId"];
|
|
if (oRecalcIndexColumns && oRecalcIndexColumns.hasOwnProperty(sheetId)) {
|
|
oRangeOrObjectId["c1"] = oRecalcIndexColumns[sheetId].getLockMe2(oRangeOrObjectId["c1"]);
|
|
oRangeOrObjectId["c2"] = oRecalcIndexColumns[sheetId].getLockMe2(oRangeOrObjectId["c2"]);
|
|
}
|
|
if (oRecalcIndexRows && oRecalcIndexRows.hasOwnProperty(sheetId)) {
|
|
oRangeOrObjectId["r1"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r1"]);
|
|
oRangeOrObjectId["r2"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r2"]);
|
|
}
|
|
}
|
|
}
|
|
function _addRecalcIndex(oRecalcIndex) {
|
|
if (null == oRecalcIndex) {
|
|
return null;
|
|
}
|
|
var nIndex = 0;
|
|
var nRecalcType = c_oAscRecalcIndexTypes.RecalcIndexAdd;
|
|
var oRecalcIndexElement = null;
|
|
var oRecalcIndexResult = {};
|
|
for (var sheetId in oRecalcIndex) {
|
|
if (oRecalcIndex.hasOwnProperty(sheetId)) {
|
|
if (!oRecalcIndexResult.hasOwnProperty(sheetId)) {
|
|
oRecalcIndexResult[sheetId] = new CRecalcIndex();
|
|
}
|
|
for (; nIndex < oRecalcIndex[sheetId]._arrElements.length; ++nIndex) {
|
|
oRecalcIndexElement = oRecalcIndex[sheetId]._arrElements[nIndex];
|
|
if (true === oRecalcIndexElement.m_bIsSaveIndex) {
|
|
continue;
|
|
}
|
|
nRecalcType = (c_oAscRecalcIndexTypes.RecalcIndexAdd === oRecalcIndexElement._recalcType) ? c_oAscRecalcIndexTypes.RecalcIndexRemove : c_oAscRecalcIndexTypes.RecalcIndexAdd;
|
|
oRecalcIndexResult[sheetId].add(nRecalcType, oRecalcIndexElement._position, oRecalcIndexElement._count, true);
|
|
}
|
|
}
|
|
}
|
|
return oRecalcIndexResult;
|
|
}
|
|
function compareExcelBlock(newBlock, oldBlock) {
|
|
if (null !== newBlock.subType && null !== oldBlock.subType) {
|
|
return true;
|
|
}
|
|
if ((c_oAscLockTypeElemSubType.ChangeProperties === oldBlock.subType && c_oAscLockTypeElem.Sheet !== newBlock.type) || (c_oAscLockTypeElemSubType.ChangeProperties === newBlock.subType && c_oAscLockTypeElem.Sheet !== oldBlock.type)) {
|
|
return false;
|
|
}
|
|
var resultLock = false;
|
|
if (newBlock.type === c_oAscLockTypeElem.Range) {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Range) {
|
|
if (c_oAscLockTypeElemSubType.InsertRows === oldBlock.subType || c_oAscLockTypeElemSubType.InsertColumns === oldBlock.subType) {
|
|
resultLock = false;
|
|
} else {
|
|
if (isInterSection(newBlock.rangeOrObjectId, oldBlock.rangeOrObjectId)) {
|
|
resultLock = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Sheet) {
|
|
resultLock = true;
|
|
}
|
|
}
|
|
} else {
|
|
if (newBlock.type === c_oAscLockTypeElem.Sheet) {
|
|
resultLock = true;
|
|
} else {
|
|
if (newBlock.type === c_oAscLockTypeElem.Object) {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Sheet) {
|
|
resultLock = true;
|
|
} else {
|
|
if (oldBlock.type === c_oAscLockTypeElem.Object && oldBlock.rangeOrObjectId === newBlock.rangeOrObjectId) {
|
|
resultLock = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return resultLock;
|
|
}
|
|
function isInterSection(range1, range2) {
|
|
if (range2.c1 > range1.c2 || range2.c2 < range1.c1 || range2.r1 > range1.r2 || range2.r2 < range1.r1) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
function typeOf(obj) {
|
|
if (obj === undefined) {
|
|
return "undefined";
|
|
}
|
|
if (obj === null) {
|
|
return "null";
|
|
}
|
|
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
|
|
}
|
|
function comparePresentationBlock(newBlock, oldBlock) {
|
|
var resultLock = false;
|
|
switch (newBlock.type) {
|
|
case c_oAscLockTypeElemPresentation.Presentation:
|
|
if (c_oAscLockTypeElemPresentation.Presentation === oldBlock.type) {
|
|
resultLock = newBlock.val === oldBlock.val;
|
|
}
|
|
break;
|
|
case c_oAscLockTypeElemPresentation.Slide:
|
|
if (c_oAscLockTypeElemPresentation.Slide === oldBlock.type) {
|
|
resultLock = newBlock.val === oldBlock.val;
|
|
} else {
|
|
if (c_oAscLockTypeElemPresentation.Object === oldBlock.type) {
|
|
resultLock = newBlock.val === oldBlock.slideId;
|
|
}
|
|
}
|
|
break;
|
|
case c_oAscLockTypeElemPresentation.Object:
|
|
if (c_oAscLockTypeElemPresentation.Slide === oldBlock.type) {
|
|
resultLock = newBlock.slideId === oldBlock.val;
|
|
} else {
|
|
if (c_oAscLockTypeElemPresentation.Object === oldBlock.type) {
|
|
resultLock = newBlock.objId === oldBlock.objId;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
return resultLock;
|
|
}
|
|
function auth(conn, data) {
|
|
if (data.version !== asc_coAuthV) {
|
|
sendFileError(conn, "Old Version Sdk");
|
|
return;
|
|
}
|
|
if (data.token && data.user) {
|
|
var docId;
|
|
var user = data.user;
|
|
var parsed = urlParse.exec(conn.url);
|
|
if (parsed.length > 1) {
|
|
docId = conn.docId = parsed[1];
|
|
} else {}
|
|
if (false === data.isViewer && saveTimers[docId]) {
|
|
clearTimeout(saveTimers[docId]);
|
|
}
|
|
var bIsRestore = null != data.sessionId;
|
|
var pucker = createPucker(docId, data.server, data.documentFormatSave, 0, false);
|
|
if (!pucker) {
|
|
sendFileError(conn, "pucker error");
|
|
return;
|
|
}
|
|
var curIndexUser = bIsRestore ? user.indexUser : pucker.indexUser;
|
|
var curUserId = user.id + curIndexUser;
|
|
conn.sessionState = 1;
|
|
conn.user = {
|
|
id: curUserId,
|
|
idOriginal: user.id,
|
|
name: user.name,
|
|
indexUser: curIndexUser
|
|
};
|
|
conn.isViewer = data.isViewer;
|
|
if (!conn.isViewer) {
|
|
updatePucker(docId, Math.max(pucker.indexUser, curIndexUser + 1));
|
|
}
|
|
if (bIsRestore) {
|
|
logger.info("restored old session id = %s", data.sessionId);
|
|
sqlBase.checkStatusFile(docId, function (error, result) {
|
|
if (null !== error || 0 === result.length) {
|
|
sendFileError(conn, "DataBase error");
|
|
return;
|
|
}
|
|
var status = result[0]["tr_status"];
|
|
if (FileStatus.Ok === status) {} else {
|
|
if (FileStatus.SaveVersion === status) {
|
|
sqlBase.updateStatusFile(docId);
|
|
} else {
|
|
if (FileStatus.UpdateVersion === status) {
|
|
sendFileError(conn, "Update Version error");
|
|
return;
|
|
} else {
|
|
sendFileError(conn, "Other error");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
getDocumentChanges(docId, function (objChangesDocument, changesIndex) {
|
|
var bIsSuccessRestore = true;
|
|
if (objChangesDocument && 0 < objChangesDocument.length) {
|
|
var change = objChangesDocument[objChangesDocument.length - 1];
|
|
if (change["change"]) {
|
|
if (change["user"] !== curUserId) {
|
|
bIsSuccessRestore = 0 === (((data["lastOtherSaveTime"] - change["time"]) / 1000) >> 0);
|
|
}
|
|
}
|
|
}
|
|
if (bIsSuccessRestore) {
|
|
conn.sessionId = data.sessionId;
|
|
var arrayBlocks = data["block"];
|
|
if (arrayBlocks && (0 === arrayBlocks.length || getLock(conn, data, true))) {
|
|
connections = _.reject(connections, function (el) {
|
|
return el.connection.sessionId === data.sessionId;
|
|
});
|
|
endAuth(conn, true);
|
|
} else {
|
|
sendFileError(conn, "Restore error. Locks not checked.");
|
|
}
|
|
} else {
|
|
sendFileError(conn, "Restore error. Document modified.");
|
|
}
|
|
});
|
|
});
|
|
} else {
|
|
conn.sessionId = conn.id;
|
|
endAuth(conn, false, data.documentCallbackUrl);
|
|
}
|
|
}
|
|
}
|
|
function endAuth(conn, bIsRestore, documentCallbackUrl) {
|
|
var docId = conn.docId;
|
|
connections.push({
|
|
connection: conn
|
|
});
|
|
var participants = getParticipants(docId);
|
|
var tmpConnection, tmpUser, firstParticipantNoView, participantsMap = [],
|
|
countNoView = 0;
|
|
for (var i = 0; i < participants.length; ++i) {
|
|
tmpConnection = participants[i].connection;
|
|
tmpUser = tmpConnection.user;
|
|
participantsMap.push({
|
|
id: tmpUser.id,
|
|
username: tmpUser.name,
|
|
indexUser: tmpUser.indexUser,
|
|
view: tmpConnection.isViewer
|
|
});
|
|
if (!tmpConnection.isViewer) {
|
|
++countNoView;
|
|
if (!firstParticipantNoView) {
|
|
firstParticipantNoView = participantsMap[participantsMap.length - 1];
|
|
}
|
|
}
|
|
}
|
|
if (!conn.isViewer) {
|
|
if (documentCallbackUrl) {
|
|
bindEvents(docId, documentCallbackUrl);
|
|
} else {
|
|
sendStatusDocument(docId, c_oAscChangeBase.No);
|
|
}
|
|
}
|
|
if (!bIsRestore && 2 === countNoView && !conn.isViewer) {
|
|
lockDocuments[docId] = firstParticipantNoView;
|
|
}
|
|
if (lockDocuments[docId] && !conn.isViewer) {
|
|
var sendObject = {
|
|
type: "waitAuth",
|
|
lockDocument: lockDocuments[docId]
|
|
};
|
|
sendData(conn, sendObject);
|
|
} else {
|
|
if (bIsRestore) {
|
|
sendAuthInfo(undefined, undefined, conn, participantsMap);
|
|
} else {
|
|
getDocumentChanges(docId, function (objChangesDocument, changesIndex) {
|
|
sendAuthInfo(objChangesDocument, changesIndex, conn, participantsMap);
|
|
});
|
|
}
|
|
}
|
|
sendParticipantsState(participants, true, conn);
|
|
}
|
|
function sendAuthInfo(objChangesDocument, changesIndex, conn, participantsMap) {
|
|
var docId = conn.docId;
|
|
var sendObject = {
|
|
type: "auth",
|
|
result: 1,
|
|
sessionId: conn.sessionId,
|
|
participants: participantsMap,
|
|
messages: messages[docId],
|
|
locks: locks[docId],
|
|
changes: objChangesDocument,
|
|
changesIndex: changesIndex,
|
|
indexUser: conn.user.indexUser
|
|
};
|
|
sendData(conn, sendObject);
|
|
}
|
|
function onMessage(conn, data) {
|
|
var participants = getParticipants(conn.docId),
|
|
msg = {
|
|
docid: conn.docId,
|
|
message: data.message,
|
|
time: Date.now(),
|
|
user: conn.user.id,
|
|
username: conn.user.name
|
|
};
|
|
if (!messages.hasOwnProperty(conn.docId)) {
|
|
messages[conn.docId] = [msg];
|
|
} else {
|
|
messages[conn.docId].push(msg);
|
|
}
|
|
logger.info("insert message: %s", JSON.stringify(msg));
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "message",
|
|
messages: [msg]
|
|
});
|
|
});
|
|
}
|
|
function getLock(conn, data, bIsRestore) {
|
|
logger.info("getLock docid: %s", conn.docId);
|
|
var fLock = null;
|
|
switch (data["editorType"]) {
|
|
case 0:
|
|
fLock = getLockWord;
|
|
break;
|
|
case 1:
|
|
fLock = getLockExcel;
|
|
break;
|
|
case 2:
|
|
fLock = getLockPresentation;
|
|
break;
|
|
}
|
|
return fLock ? fLock(conn, data, bIsRestore) : false;
|
|
}
|
|
function getLockWord(conn, data, bIsRestore) {
|
|
var docId = conn.docId,
|
|
participants = getParticipants(docId, undefined, true),
|
|
arrayBlocks = data.block;
|
|
if (!locks.hasOwnProperty(docId)) {
|
|
locks[docId] = {};
|
|
}
|
|
var i, documentLocks = locks[docId];
|
|
if (_checkLock(documentLocks, arrayBlocks)) {
|
|
for (i = 0; i < arrayBlocks.length; ++i) {
|
|
documentLocks[arrayBlocks[i]] = {
|
|
time: Date.now(),
|
|
user: conn.user.id,
|
|
block: arrayBlocks[i],
|
|
sessionId: conn.sessionId
|
|
};
|
|
}
|
|
} else {
|
|
if (bIsRestore) {
|
|
return false;
|
|
}
|
|
}
|
|
sendGetLock(participants, documentLocks);
|
|
return true;
|
|
}
|
|
function getLockExcel(conn, data, bIsRestore) {
|
|
var docId = conn.docId,
|
|
userId = conn.user.id,
|
|
participants = getParticipants(docId, undefined, true),
|
|
arrayBlocks = data.block;
|
|
if (!locks.hasOwnProperty(docId)) {
|
|
locks[docId] = [];
|
|
}
|
|
var i, documentLocks = locks[docId];
|
|
if (_checkLockExcel(documentLocks, arrayBlocks, userId)) {
|
|
for (i = 0; i < arrayBlocks.length; ++i) {
|
|
documentLocks.push({
|
|
time: Date.now(),
|
|
user: userId,
|
|
block: arrayBlocks[i],
|
|
sessionId: conn.sessionId
|
|
});
|
|
}
|
|
} else {
|
|
if (bIsRestore) {
|
|
return false;
|
|
}
|
|
}
|
|
sendGetLock(participants, documentLocks);
|
|
return true;
|
|
}
|
|
function getLockPresentation(conn, data, bIsRestore) {
|
|
var docId = conn.docId,
|
|
userId = conn.user.id,
|
|
participants = getParticipants(docId, undefined, true),
|
|
arrayBlocks = data.block;
|
|
if (!locks.hasOwnProperty(docId)) {
|
|
locks[docId] = [];
|
|
}
|
|
var i, documentLocks = locks[docId];
|
|
if (_checkLockPresentation(documentLocks, arrayBlocks, userId)) {
|
|
for (i = 0; i < arrayBlocks.length; ++i) {
|
|
documentLocks.push({
|
|
time: Date.now(),
|
|
user: userId,
|
|
block: arrayBlocks[i],
|
|
sessionId: conn.sessionId
|
|
});
|
|
}
|
|
} else {
|
|
if (bIsRestore) {
|
|
return false;
|
|
}
|
|
}
|
|
sendGetLock(participants, documentLocks);
|
|
return true;
|
|
}
|
|
function sendGetLock(participants, documentLocks) {
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "getLock",
|
|
locks: documentLocks
|
|
});
|
|
});
|
|
}
|
|
function saveChanges(conn, data) {
|
|
var docId = conn.docId,
|
|
userId = conn.user.id;
|
|
logger.info("saveChanges docid: %s", docId);
|
|
var pucker = objServicePucker[docId];
|
|
if (!pucker) {
|
|
logger.error("saveChanges find pucker error docid: %s", docId);
|
|
return;
|
|
}
|
|
var participants = getParticipants(docId, userId, true);
|
|
var objChangesDocument = getDocumentChangesCache(docId);
|
|
var deleteIndex = -1;
|
|
if (data.startSaveChanges && null != data.deleteIndex) {
|
|
deleteIndex = data.deleteIndex;
|
|
if (-1 !== deleteIndex) {
|
|
var deleteCount = pucker.index - deleteIndex;
|
|
if (0 < deleteCount) {
|
|
if (objChangesDocument) {
|
|
objChangesDocument.splice(deleteIndex, deleteCount);
|
|
}
|
|
pucker.index -= deleteCount;
|
|
sqlBase.deleteChanges(docId, deleteIndex);
|
|
} else {
|
|
if (0 > deleteCount) {
|
|
logger.error("saveChanges docid: %s ; deleteIndex: %s ; startIndex: %s ; deleteCount: %s", docId, deleteIndex, pucker.index, deleteCount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var startIndex = pucker.index;
|
|
var newChanges = JSON.parse(data.changes);
|
|
var arrNewDocumentChanges = [];
|
|
logger.info("saveChanges docid: %s ; deleteIndex: %s ; startIndex: %s ; length: %s", docId, deleteIndex, startIndex, newChanges.length);
|
|
if (0 < newChanges.length) {
|
|
var oElement = null;
|
|
for (var i = 0; i < newChanges.length; ++i) {
|
|
oElement = newChanges[i];
|
|
arrNewDocumentChanges.push({
|
|
docid: docId,
|
|
change: JSON.stringify(oElement),
|
|
time: Date.now(),
|
|
user: userId,
|
|
useridoriginal: conn.user.idOriginal
|
|
});
|
|
}
|
|
if (objChangesDocument) {
|
|
objChangesDocument.concat(arrNewDocumentChanges);
|
|
}
|
|
pucker.index += arrNewDocumentChanges.length;
|
|
sqlBase.insertChanges(arrNewDocumentChanges, docId, startIndex, conn.user);
|
|
}
|
|
var changesIndex = (-1 === deleteIndex && data.startSaveChanges) ? startIndex : -1;
|
|
if (data.endSaveChanges) {
|
|
if (data.isExcel && false !== data.isCoAuthoring && data.excelAdditionalInfo) {
|
|
var tmpAdditionalInfo = JSON.parse(data.excelAdditionalInfo);
|
|
var oRecalcIndexColumns = _addRecalcIndex(tmpAdditionalInfo["indexCols"]);
|
|
var oRecalcIndexRows = _addRecalcIndex(tmpAdditionalInfo["indexRows"]);
|
|
if (null !== oRecalcIndexColumns || null !== oRecalcIndexRows) {
|
|
_recalcLockArray(userId, locks[docId], oRecalcIndexColumns, oRecalcIndexRows);
|
|
}
|
|
}
|
|
var userLocks = getUserLocks(docId, conn.sessionId);
|
|
if (!checkEndAuthLock(false, docId, userId)) {
|
|
var arrLocks = _.map(userLocks, function (e) {
|
|
return {
|
|
block: e.block,
|
|
user: e.user,
|
|
time: Date.now(),
|
|
changes: null
|
|
};
|
|
});
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "saveChanges",
|
|
changes: arrNewDocumentChanges,
|
|
changesIndex: pucker.index,
|
|
locks: arrLocks,
|
|
excelAdditionalInfo: data.excelAdditionalInfo
|
|
});
|
|
});
|
|
}
|
|
unSaveLock(conn, changesIndex);
|
|
} else {
|
|
_.each(participants, function (participant) {
|
|
sendData(participant.connection, {
|
|
type: "saveChanges",
|
|
changes: arrNewDocumentChanges,
|
|
changesIndex: pucker.index,
|
|
locks: []
|
|
});
|
|
});
|
|
sendData(conn, {
|
|
type: "savePartChanges",
|
|
changesIndex: changesIndex
|
|
});
|
|
}
|
|
}
|
|
function isSaveLock(conn) {
|
|
var _docId = conn.docId;
|
|
var _userId = conn.user.id;
|
|
var _time = Date.now();
|
|
var isSaveLock = (undefined === arrSaveLock[_docId]) ? false : arrSaveLock[_docId].savelock;
|
|
if (false === isSaveLock) {
|
|
arrSaveLock[conn.docId] = {
|
|
docid: _docId,
|
|
savelock: true,
|
|
time: Date.now(),
|
|
user: conn.user.id
|
|
};
|
|
var _tmpSaveLock = arrSaveLock[_docId];
|
|
arrSaveLock[conn.docId].saveLockTimeOutId = setTimeout(function () {
|
|
if (_tmpSaveLock && _userId == _tmpSaveLock.user && _time == _tmpSaveLock.time) {
|
|
arrSaveLock[_docId] = undefined;
|
|
}
|
|
},
|
|
60000);
|
|
}
|
|
sendData(conn, {
|
|
type: "saveLock",
|
|
saveLock: isSaveLock
|
|
});
|
|
}
|
|
function unSaveLock(conn, index) {
|
|
if (undefined != arrSaveLock[conn.docId] && conn.user.id != arrSaveLock[conn.docId].user) {
|
|
return;
|
|
}
|
|
if (arrSaveLock[conn.docId] && null != arrSaveLock[conn.docId].saveLockTimeOutId) {
|
|
clearTimeout(arrSaveLock[conn.docId].saveLockTimeOutId);
|
|
}
|
|
arrSaveLock[conn.docId] = undefined;
|
|
sendData(conn, {
|
|
type: "unSaveLock",
|
|
index: index
|
|
});
|
|
}
|
|
function getMessages(conn) {
|
|
sendData(conn, {
|
|
type: "message",
|
|
messages: messages[conn.docId]
|
|
});
|
|
}
|
|
function _checkLock(documentLocks, arrayBlocks) {
|
|
var isLock = false;
|
|
var i, lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
|
for (i = 0; i < lengthArray; ++i) {
|
|
logger.info("getLock id: %s", arrayBlocks[i]);
|
|
if (documentLocks.hasOwnProperty(arrayBlocks[i]) && documentLocks[arrayBlocks[i]] !== null) {
|
|
isLock = true;
|
|
break;
|
|
}
|
|
}
|
|
if (0 === lengthArray) {
|
|
isLock = true;
|
|
}
|
|
return !isLock;
|
|
}
|
|
function _checkLockExcel(documentLocks, arrayBlocks, userId) {
|
|
var documentLock;
|
|
var isLock = false;
|
|
var isExistInArray = false;
|
|
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];
|
|
if (documentLock.user === userId && blockRange.sheetId === documentLock.block.sheetId && blockRange.type === c_oAscLockTypeElem.Object && documentLock.block.type === c_oAscLockTypeElem.Object && documentLock.block.rangeOrObjectId === blockRange.rangeOrObjectId) {
|
|
isExistInArray = true;
|
|
break;
|
|
}
|
|
if (c_oAscLockTypeElem.Sheet === blockRange.type && c_oAscLockTypeElem.Sheet === documentLock.block.type) {
|
|
if (documentLock.user === userId) {
|
|
if (blockRange.sheetId === documentLock.block.sheetId) {
|
|
isExistInArray = true;
|
|
break;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
isLock = true;
|
|
break;
|
|
}
|
|
}
|
|
if (documentLock.user === userId || !(documentLock.block) || blockRange.sheetId !== documentLock.block.sheetId) {
|
|
continue;
|
|
}
|
|
isLock = compareExcelBlock(blockRange, documentLock.block);
|
|
}
|
|
}
|
|
if (0 === lengthArray) {
|
|
isLock = true;
|
|
}
|
|
return !isLock && !isExistInArray;
|
|
}
|
|
function _checkLockPresentation(documentLocks, arrayBlocks, userId) {
|
|
var isLock = false;
|
|
var i, documentLock, 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];
|
|
if (documentLock.user === userId || !(documentLock.block)) {
|
|
continue;
|
|
}
|
|
isLock = comparePresentationBlock(blockRange, documentLock.block);
|
|
}
|
|
}
|
|
if (0 === lengthArray) {
|
|
isLock = true;
|
|
}
|
|
return !isLock;
|
|
}
|
|
function _createSaveTimer(docId) {
|
|
var oTimeoutFunction = function () {
|
|
if (sqlBase.isLockCriticalSection(docId)) {
|
|
saveTimers[docId] = setTimeout(oTimeoutFunction, c_oAscLockTimeOutDelay);
|
|
} else {
|
|
delete saveTimers[docId];
|
|
sendChangesToServer(docId);
|
|
}
|
|
};
|
|
saveTimers[docId] = setTimeout(oTimeoutFunction, c_oAscSaveTimeOutDelay);
|
|
}
|
|
sockjs_echo.installHandlers(server, {
|
|
prefix: "/doc/[0-9-.a-zA-Z_=]*/c",
|
|
log: function (severity, message) {
|
|
logger.info(message);
|
|
}
|
|
});
|
|
var callbackLoadPuckerMySql = function (error, arrayElements) {
|
|
if (null != arrayElements) {
|
|
var i, element;
|
|
for (i = 0; i < arrayElements.length; ++i) {
|
|
element = arrayElements[i];
|
|
createPucker(element["dp_key"], element["dp_callback"], element["dp_documentFormatSave"], element["dp_indexUser"], true);
|
|
}
|
|
}
|
|
sqlBase.loadTable(sqlBase.tableId.callbacks, callbackLoadCallbacksMySql);
|
|
};
|
|
var callbackLoadCallbacksMySql = function (error, arrayElements) {
|
|
if (null != arrayElements) {
|
|
var i, element, callbackUrl;
|
|
for (i = 0; i < arrayElements.length; ++i) {
|
|
element = arrayElements[i];
|
|
callbackUrl = parseUrl(element["dc_callback"]);
|
|
if (null === callbackUrl) {
|
|
logger.error("error parse callback = %s", element["dc_callback"]);
|
|
}
|
|
objServiceInfo[element["dc_key"]] = callbackUrl;
|
|
}
|
|
var docId;
|
|
for (docId in objServiceInfo) {
|
|
if (objServicePucker[docId]) {
|
|
_createSaveTimer(docId);
|
|
} else {
|
|
deleteCallback(docId);
|
|
}
|
|
}
|
|
}
|
|
callbackFunction();
|
|
};
|
|
sqlBase.loadTable(sqlBase.tableId.pucker, callbackLoadPuckerMySql);
|
|
};
|
|
exports.commandFromServer = function (query) {
|
|
var docId = query.key;
|
|
if (null == docId) {
|
|
return c_oAscServerCommandErrors.DocumentIdError;
|
|
}
|
|
logger.info("commandFromServer: docId = %s c = %s", docId, query.c);
|
|
var result = c_oAscServerCommandErrors.NoError;
|
|
switch (query.c) {
|
|
case "info":
|
|
bindEvents(docId, query.callback);
|
|
break;
|
|
case "drop":
|
|
if (query.userid) {
|
|
dropUserFromDocument(docId, query.userid, query.description);
|
|
} else {
|
|
if (query.users) {
|
|
onReplySendStatusDocument(docId, query.users);
|
|
}
|
|
}
|
|
break;
|
|
case "saved":
|
|
removeChanges(docId, "1" !== query.status, "1" === query.conv);
|
|
break;
|
|
default:
|
|
result = c_oAscServerCommandErrors.CommandError;
|
|
break;
|
|
}
|
|
return result;
|
|
}; |