init repo
This commit is contained in:
902
NodeJsProjects/CoAuthoring/sources/DocsCoServer.js
Normal file
902
NodeJsProjects/CoAuthoring/sources/DocsCoServer.js
Normal file
@@ -0,0 +1,902 @@
|
||||
/*
|
||||
* (c) Copyright Ascensio System SIA 2010-2014
|
||||
*
|
||||
* 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"),
|
||||
dataBase = null,
|
||||
http = require("http"),
|
||||
config = require("./config.json");
|
||||
if (config["mongodb"]) {
|
||||
dataBase = require("./database");
|
||||
}
|
||||
var logger = require("./../../Common/sources/logger");
|
||||
var c_oAscRecalcIndexTypes = {
|
||||
RecalcIndexAdd: 1,
|
||||
RecalcIndexRemove: 2
|
||||
};
|
||||
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;
|
||||
}
|
||||
};
|
||||
exports.install = function (server, callbackFunction) {
|
||||
var sockjs_opts = {
|
||||
sockjs_url: "http://cdn.sockjs.org/sockjs-0.3.min.js"
|
||||
},
|
||||
sockjs_echo = sockjs.createServer(sockjs_opts),
|
||||
connections = [],
|
||||
messages = {},
|
||||
objchanges = {},
|
||||
indexuser = {},
|
||||
locks = {},
|
||||
arrsavelock = [],
|
||||
dataHandler,
|
||||
urlParse = new RegExp("^/doc/([0-9-.a-zA-Z_=]*)/c.+", "i"),
|
||||
serverPort = 80;
|
||||
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);
|
||||
dataHandler[data.type](conn, data);
|
||||
} catch(e) {
|
||||
logger.error("error receiving response:" + e);
|
||||
}
|
||||
});
|
||||
conn.on("error", function () {
|
||||
logger.error("On error");
|
||||
});
|
||||
conn.on("close", function () {
|
||||
var connection = this,
|
||||
docLock, userLocks, participants, reconected;
|
||||
logger.info("Connection closed or timed out");
|
||||
connections = _.reject(connections, function (el) {
|
||||
return el.connection.id === connection.id;
|
||||
});
|
||||
reconected = _.any(connections, function (el) {
|
||||
return el.connection.sessionId === connection.sessionId;
|
||||
});
|
||||
var state = (false == reconected) ? false : undefined;
|
||||
participants = getParticipants(conn.docId);
|
||||
var participantsMap = _.map(participants, function (conn) {
|
||||
return {
|
||||
id: conn.connection.userId,
|
||||
username: conn.connection.userName
|
||||
};
|
||||
});
|
||||
sendParticipantsState(participants, state, connection.userId, connection.userName, participantsMap);
|
||||
if (!reconected) {
|
||||
if (undefined != arrsavelock[conn.docId] && connection.userId == arrsavelock[conn.docId].user) {
|
||||
if (null != arrsavelock[conn.docId].saveLockTimeOutId) {
|
||||
clearTimeout(arrsavelock[conn.docId].saveLockTimeOutId);
|
||||
}
|
||||
arrsavelock[conn.docId] = undefined;
|
||||
}
|
||||
if (0 >= participants.length) {
|
||||
if (dataBase) {
|
||||
dataBase.remove("messages", {
|
||||
docid: conn.docId
|
||||
});
|
||||
}
|
||||
delete messages[conn.docId];
|
||||
if (objchanges[conn.docId] && 0 < objchanges[conn.docId].length) {
|
||||
sendChangesToServer(conn.serverHost, conn.serverPath, conn.docId);
|
||||
}
|
||||
if (dataBase) {
|
||||
dataBase.remove("changes", {
|
||||
docid: conn.docId
|
||||
});
|
||||
}
|
||||
delete objchanges[conn.docId];
|
||||
if (null != arrsavelock[conn.docId] && null != arrsavelock[conn.docId].saveLockTimeOutId) {
|
||||
clearTimeout(arrsavelock[conn.docId].saveLockTimeOutId);
|
||||
}
|
||||
arrsavelock[conn.docId] = undefined;
|
||||
}
|
||||
docLock = locks[connection.docId];
|
||||
if (docLock) {
|
||||
userLocks = [];
|
||||
if ("array" === typeOf(docLock)) {
|
||||
for (var nIndex = 0; nIndex < docLock.length; ++nIndex) {
|
||||
if (docLock[nIndex].sessionId === connection.sessionId) {
|
||||
userLocks.push(docLock[nIndex]);
|
||||
docLock.splice(nIndex, 1);
|
||||
--nIndex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var keyLockElem in docLock) {
|
||||
if (docLock[keyLockElem].sessionId === connection.sessionId) {
|
||||
userLocks.push(docLock[keyLockElem]);
|
||||
delete docLock[keyLockElem];
|
||||
}
|
||||
}
|
||||
}
|
||||
_.each(participants, function (participant) {
|
||||
sendData(participant.connection, {
|
||||
type: "releaselock",
|
||||
locks: _.map(userLocks, function (e) {
|
||||
return {
|
||||
block: e.block,
|
||||
user: e.user,
|
||||
time: Date.now(),
|
||||
changes: null
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
function sendData(conn, data) {
|
||||
conn.write(JSON.stringify(data));
|
||||
}
|
||||
function sendParticipantsState(participants, stateConnect, _userId, _userName, participantsMap) {
|
||||
_.each(participants, function (participant) {
|
||||
if (participant.connection.userId !== _userId) {
|
||||
sendData(participant.connection, {
|
||||
type: "participants",
|
||||
participants: participantsMap
|
||||
});
|
||||
sendData(participant.connection, {
|
||||
type: "connectstate",
|
||||
state: stateConnect,
|
||||
id: _userId,
|
||||
username: _userName
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function getParticipants(docId, exludeuserId) {
|
||||
return _.filter(connections, function (el) {
|
||||
return el.connection.docId === docId && el.connection.userId !== exludeuserId;
|
||||
});
|
||||
}
|
||||
function sendChangesToServer(serverHost, serverPath, docId) {
|
||||
if (!serverHost || !serverPath) {
|
||||
return;
|
||||
}
|
||||
var options = {
|
||||
host: serverHost,
|
||||
port: serverPort,
|
||||
path: serverPath,
|
||||
method: "POST"
|
||||
};
|
||||
var req = http.request(options, function (res) {
|
||||
res.setEncoding("utf8");
|
||||
});
|
||||
req.on("error", function (e) {
|
||||
logger.warn("problem with request on server: " + e.message);
|
||||
});
|
||||
var sendData = JSON.stringify({
|
||||
"id": docId,
|
||||
"c": "cc"
|
||||
});
|
||||
req.write(sendData);
|
||||
req.end();
|
||||
}
|
||||
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.hasOwnProperty(sheetId)) {
|
||||
oRangeOrObjectId["c1"] = oRecalcIndexColumns[sheetId].getLockMe2(oRangeOrObjectId["c1"]);
|
||||
oRangeOrObjectId["c2"] = oRecalcIndexColumns[sheetId].getLockMe2(oRangeOrObjectId["c2"]);
|
||||
}
|
||||
if (oRecalcIndexRows.hasOwnProperty(sheetId)) {
|
||||
oRangeOrObjectId["r1"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r1"]);
|
||||
oRangeOrObjectId["r2"] = oRecalcIndexRows[sheetId].getLockMe2(oRangeOrObjectId["r2"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
function _addRecalcIndex(oRecalcIndex) {
|
||||
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;
|
||||
}
|
||||
dataHandler = (function () {
|
||||
function auth(conn, data) {
|
||||
if (data.token && data.user) {
|
||||
var parsed = urlParse.exec(conn.url);
|
||||
if (parsed.length > 1) {
|
||||
conn.docId = parsed[1];
|
||||
} else {}
|
||||
if (!indexuser.hasOwnProperty(conn.docId)) {
|
||||
indexuser[conn.docId] = 1;
|
||||
} else {
|
||||
indexuser[conn.docId] += 1;
|
||||
}
|
||||
conn.sessionState = 1;
|
||||
conn.userId = data.user + indexuser[conn.docId];
|
||||
conn.userName = data.username;
|
||||
conn.serverHost = data.serverHost;
|
||||
conn.serverPath = data.serverPath;
|
||||
if (data.sessionId !== null && _.isString(data.sessionId) && data.sessionId !== "") {
|
||||
logger.info("restored old session id=" + data.sessionId);
|
||||
connections = _.reject(connections, function (el) {
|
||||
return el.connection.sessionId === data.sessionId;
|
||||
});
|
||||
conn.sessionId = data.sessionId;
|
||||
} else {
|
||||
conn.sessionId = conn.id;
|
||||
}
|
||||
connections.push({
|
||||
connection: conn
|
||||
});
|
||||
var participants = getParticipants(conn.docId);
|
||||
var participantsMap = _.map(participants, function (conn) {
|
||||
return {
|
||||
id: conn.connection.userId,
|
||||
username: conn.connection.userName
|
||||
};
|
||||
});
|
||||
sendData(conn, {
|
||||
type: "auth",
|
||||
result: 1,
|
||||
sessionId: conn.sessionId,
|
||||
participants: participantsMap,
|
||||
messages: messages[conn.docid],
|
||||
locks: locks[conn.docId],
|
||||
changes: objchanges[conn.docId],
|
||||
indexuser: indexuser[conn.docId]
|
||||
});
|
||||
sendParticipantsState(participants, true, conn.userId, conn.userName, participantsMap);
|
||||
}
|
||||
}
|
||||
function message(conn, data) {
|
||||
var participants = getParticipants(conn.docId),
|
||||
msg = {
|
||||
docid: conn.docId,
|
||||
message: data.message,
|
||||
time: Date.now(),
|
||||
user: conn.userId,
|
||||
username: conn.userName
|
||||
};
|
||||
if (!messages.hasOwnProperty(conn.docId)) {
|
||||
messages[conn.docId] = [msg];
|
||||
} else {
|
||||
messages[conn.docId].push(msg);
|
||||
}
|
||||
logger.info("database insert message: " + JSON.stringify(msg));
|
||||
if (dataBase) {
|
||||
dataBase.insert("messages", msg);
|
||||
}
|
||||
_.each(participants, function (participant) {
|
||||
sendData(participant.connection, {
|
||||
type: "message",
|
||||
messages: [msg]
|
||||
});
|
||||
});
|
||||
}
|
||||
function getlock(conn, data) {
|
||||
var participants = getParticipants(conn.docId),
|
||||
documentLocks;
|
||||
if (!locks.hasOwnProperty(conn.docId)) {
|
||||
locks[conn.docId] = {};
|
||||
}
|
||||
documentLocks = locks[conn.docId];
|
||||
var arrayBlocks = data.block;
|
||||
var isLock = false;
|
||||
var i = 0;
|
||||
var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
||||
for (; i < lengthArray; ++i) {
|
||||
logger.info("getLock id: " + arrayBlocks[i]);
|
||||
if (documentLocks.hasOwnProperty(arrayBlocks[i]) && documentLocks[arrayBlocks[i]] !== null) {
|
||||
isLock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 === lengthArray) {
|
||||
isLock = true;
|
||||
}
|
||||
if (!isLock) {
|
||||
for (i = 0; i < lengthArray; ++i) {
|
||||
documentLocks[arrayBlocks[i]] = {
|
||||
time: Date.now(),
|
||||
user: conn.userId,
|
||||
block: arrayBlocks[i],
|
||||
sessionId: conn.sessionId
|
||||
};
|
||||
}
|
||||
}
|
||||
_.each(participants, function (participant) {
|
||||
sendData(participant.connection, {
|
||||
type: "getlock",
|
||||
locks: locks[conn.docId]
|
||||
});
|
||||
});
|
||||
}
|
||||
function getlockrange(conn, data) {
|
||||
var participants = getParticipants(conn.docId),
|
||||
documentLocks,
|
||||
documentLock;
|
||||
if (!locks.hasOwnProperty(conn.docId)) {
|
||||
locks[conn.docId] = [];
|
||||
}
|
||||
documentLocks = locks[conn.docId];
|
||||
var arrayBlocks = data.block;
|
||||
var isLock = false;
|
||||
var isExistInArray = false;
|
||||
var i = 0,
|
||||
blockRange = null;
|
||||
var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
||||
for (; 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 === conn.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 === conn.userId) {
|
||||
if (blockRange.sheetId === documentLock.block.sheetId) {
|
||||
isExistInArray = true;
|
||||
break;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
isLock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (documentLock.user === conn.userId || !(documentLock.block) || blockRange.sheetId !== documentLock.block.sheetId) {
|
||||
continue;
|
||||
}
|
||||
isLock = compareExcelBlock(blockRange, documentLock.block);
|
||||
}
|
||||
}
|
||||
if (0 === lengthArray) {
|
||||
isLock = true;
|
||||
}
|
||||
if (!isLock && !isExistInArray) {
|
||||
for (i = 0; i < lengthArray; ++i) {
|
||||
blockRange = arrayBlocks[i];
|
||||
documentLocks.push({
|
||||
time: Date.now(),
|
||||
user: conn.userId,
|
||||
block: blockRange,
|
||||
sessionId: conn.sessionId
|
||||
});
|
||||
}
|
||||
}
|
||||
_.each(participants, function (participant) {
|
||||
sendData(participant.connection, {
|
||||
type: "getlock",
|
||||
locks: locks[conn.docId]
|
||||
});
|
||||
});
|
||||
}
|
||||
function getlockpresentation(conn, data) {
|
||||
var participants = getParticipants(conn.docId),
|
||||
documentLocks,
|
||||
documentLock;
|
||||
if (!locks.hasOwnProperty(conn.docId)) {
|
||||
locks[conn.docId] = [];
|
||||
}
|
||||
documentLocks = locks[conn.docId];
|
||||
var arrayBlocks = data.block;
|
||||
var isLock = false;
|
||||
var isExistInArray = false;
|
||||
var i = 0,
|
||||
blockRange = null;
|
||||
var lengthArray = (arrayBlocks) ? arrayBlocks.length : 0;
|
||||
for (; 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 === conn.userId || !(documentLock.block)) {
|
||||
continue;
|
||||
}
|
||||
isLock = comparePresentationBlock(blockRange, documentLock.block);
|
||||
}
|
||||
}
|
||||
if (0 === lengthArray) {
|
||||
isLock = true;
|
||||
}
|
||||
if (!isLock && !isExistInArray) {
|
||||
for (i = 0; i < lengthArray; ++i) {
|
||||
blockRange = arrayBlocks[i];
|
||||
documentLocks.push({
|
||||
time: Date.now(),
|
||||
user: conn.userId,
|
||||
block: blockRange,
|
||||
sessionId: conn.sessionId
|
||||
});
|
||||
}
|
||||
}
|
||||
_.each(participants, function (participant) {
|
||||
sendData(participant.connection, {
|
||||
type: "getlock",
|
||||
locks: locks[conn.docId]
|
||||
});
|
||||
});
|
||||
}
|
||||
function savechanges(conn, data) {
|
||||
var docLock, userLocks, participants;
|
||||
if (data.endSaveChanges) {
|
||||
docLock = locks[conn.docId];
|
||||
if (docLock) {
|
||||
if ("array" === typeOf(docLock)) {
|
||||
userLocks = [];
|
||||
for (var nIndex = 0; nIndex < docLock.length; ++nIndex) {
|
||||
if (null !== docLock[nIndex] && docLock[nIndex].sessionId === conn.sessionId) {
|
||||
userLocks.push(docLock[nIndex]);
|
||||
docLock.splice(nIndex, 1);
|
||||
--nIndex;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userLocks = _.filter(docLock, function (el) {
|
||||
return el !== null && el.sessionId === conn.sessionId;
|
||||
});
|
||||
for (var i = 0; i < userLocks.length; i++) {
|
||||
delete docLock[userLocks[i].block];
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userLocks = [];
|
||||
}
|
||||
var objchange = {
|
||||
docid: conn.docId,
|
||||
changes: data.changes,
|
||||
time: Date.now(),
|
||||
user: conn.userId
|
||||
};
|
||||
if (!objchanges.hasOwnProperty(conn.docId)) {
|
||||
objchanges[conn.docId] = [objchange];
|
||||
} else {
|
||||
objchanges[conn.docId].push(objchange);
|
||||
}
|
||||
logger.info("database insert changes: " + JSON.stringify(objchange));
|
||||
if (dataBase) {
|
||||
dataBase.insert("changes", objchange);
|
||||
}
|
||||
if (!data.endSaveChanges) {
|
||||
sendData(conn, {
|
||||
type: "savePartChanges"
|
||||
});
|
||||
} else {
|
||||
if (data.isExcel) {
|
||||
var oElement = null;
|
||||
var oRecalcIndexColumns = null,
|
||||
oRecalcIndexRows = null;
|
||||
var oChanges = JSON.parse(data.changes);
|
||||
var nCount = oChanges.length;
|
||||
var nIndexChanges = 0;
|
||||
for (; nIndexChanges < nCount; ++nIndexChanges) {
|
||||
oElement = oChanges[nIndexChanges];
|
||||
if ("object" === typeof oElement) {
|
||||
if ("0" === oElement["type"]) {
|
||||
oRecalcIndexColumns = _addRecalcIndex(oElement["index"]);
|
||||
} else {
|
||||
if ("1" === oElement["type"]) {
|
||||
oRecalcIndexRows = _addRecalcIndex(oElement["index"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (null !== oRecalcIndexColumns && null !== oRecalcIndexRows) {
|
||||
_recalcLockArray(conn.userId, locks[conn.docId], oRecalcIndexColumns, oRecalcIndexRows);
|
||||
oRecalcIndexColumns = null;
|
||||
oRecalcIndexRows = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
participants = getParticipants(conn.docId, conn.userId);
|
||||
_.each(participants, function (participant) {
|
||||
sendData(participant.connection, {
|
||||
type: "savechanges",
|
||||
changes: data.changes,
|
||||
locks: _.map(userLocks, function (e) {
|
||||
return {
|
||||
block: e.block,
|
||||
user: e.user,
|
||||
time: Date.now(),
|
||||
changes: null
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
function issavelock(conn) {
|
||||
var _docId = conn.docId;
|
||||
var _userId = conn.userId;
|
||||
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.userId
|
||||
};
|
||||
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) {
|
||||
if (undefined != arrsavelock[conn.docId] && conn.userId != 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"
|
||||
});
|
||||
}
|
||||
function getmessages(conn) {
|
||||
sendData(conn, {
|
||||
type: "message",
|
||||
messages: messages[conn.docId]
|
||||
});
|
||||
}
|
||||
function getusers(conn) {
|
||||
var participants = getParticipants(conn.docId);
|
||||
sendData(conn, {
|
||||
type: "getusers",
|
||||
participants: _.map(participants, function (conn) {
|
||||
return {
|
||||
id: conn.connection.userId,
|
||||
username: conn.connection.userName
|
||||
};
|
||||
})
|
||||
});
|
||||
}
|
||||
return {
|
||||
auth: auth,
|
||||
message: message,
|
||||
getlock: getlock,
|
||||
getlockrange: getlockrange,
|
||||
getlockpresentation: getlockpresentation,
|
||||
savechanges: savechanges,
|
||||
issavelock: issavelock,
|
||||
unsavelock: unsavelock,
|
||||
getmessages: getmessages,
|
||||
getusers: getusers
|
||||
};
|
||||
} ());
|
||||
sockjs_echo.installHandlers(server, {
|
||||
prefix: "/doc/[0-9-.a-zA-Z_=]*/c",
|
||||
log: function (severity, message) {
|
||||
logger.info(message);
|
||||
}
|
||||
});
|
||||
var callbackLoadMessages = (function (arrayElements) {
|
||||
if (null != arrayElements) {
|
||||
messages = arrayElements;
|
||||
if (dataBase) {
|
||||
dataBase.remove("messages", {});
|
||||
}
|
||||
}
|
||||
if (dataBase) {
|
||||
dataBase.load("changes", callbackLoadChanges);
|
||||
} else {
|
||||
callbackLoadChanges(null);
|
||||
}
|
||||
});
|
||||
var callbackLoadChanges = (function (arrayElements) {
|
||||
if (null != arrayElements) {
|
||||
if (dataBase) {
|
||||
dataBase.remove("changes", {});
|
||||
}
|
||||
}
|
||||
callbackFunction();
|
||||
});
|
||||
if (dataBase) {
|
||||
dataBase.load("messages", callbackLoadMessages);
|
||||
} else {
|
||||
callbackLoadMessages(null);
|
||||
}
|
||||
};
|
||||
11
NodeJsProjects/CoAuthoring/sources/config.json
Normal file
11
NodeJsProjects/CoAuthoring/sources/config.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"server": {
|
||||
"port": 8000,
|
||||
"mode": "development"
|
||||
},
|
||||
"no_mongodb": {
|
||||
"host": "localhost",
|
||||
"port": 8000,
|
||||
"database": "coAuthoring"
|
||||
}
|
||||
}
|
||||
116
NodeJsProjects/CoAuthoring/sources/database.js
Normal file
116
NodeJsProjects/CoAuthoring/sources/database.js
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* (c) Copyright Ascensio System SIA 2010-2014
|
||||
*
|
||||
* 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 mongoDB = require("mongodb");
|
||||
var config = require("./config.json");
|
||||
var _errorConnection = true;
|
||||
var logger = require("./../../Common/sources/logger");
|
||||
function CreateDbClient() {
|
||||
return new mongoDB.Db(config["mongodb"]["database"], new mongoDB.Server(config["mongodb"]["host"], config["mongodb"]["port"], {
|
||||
auto_reconnect: true
|
||||
}), {
|
||||
safe: false
|
||||
});
|
||||
}
|
||||
exports.insert = function (_collectionName, _newElement) {
|
||||
var _db = CreateDbClient();
|
||||
if (!_db) {
|
||||
logger.error("Error _db");
|
||||
return;
|
||||
}
|
||||
_db.open(function (err, db) {
|
||||
if (!err) {
|
||||
db.collection(_collectionName, function (err, collection) {
|
||||
if (!err) {
|
||||
collection.insert(_newElement);
|
||||
} else {
|
||||
logger.error("Error collection");
|
||||
return;
|
||||
}
|
||||
db.close();
|
||||
});
|
||||
} else {
|
||||
logger.error("Error open database");
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
exports.remove = function (_collectionName, _removeElements) {
|
||||
var _db = CreateDbClient();
|
||||
if (!_db) {
|
||||
logger.error("Error _db");
|
||||
return;
|
||||
}
|
||||
_db.open(function (err, db) {
|
||||
if (!err) {
|
||||
db.collection(_collectionName, function (err, collection) {
|
||||
if (!err) {
|
||||
collection.remove(_removeElements, function (err, collection) {
|
||||
logger.info("All elements remove");
|
||||
});
|
||||
} else {
|
||||
logger.error("Error collection");
|
||||
return;
|
||||
}
|
||||
db.close();
|
||||
});
|
||||
} else {
|
||||
logger.error("Error open database");
|
||||
return;
|
||||
}
|
||||
});
|
||||
};
|
||||
exports.load = function (_collectionName, callbackFunction) {
|
||||
var _db = CreateDbClient();
|
||||
if (!_db) {
|
||||
logger.error("Error _db");
|
||||
return callbackFunction(null);
|
||||
}
|
||||
var result = [];
|
||||
_db.open(function (err, db) {
|
||||
db.collection(_collectionName, function (err, collection) {
|
||||
collection.find(function (err, cursor) {
|
||||
cursor.each(function (err, item) {
|
||||
if (item != null) {
|
||||
if (!result.hasOwnProperty(item.docid)) {
|
||||
result[item.docid] = [item];
|
||||
} else {
|
||||
result[item.docid].push(item);
|
||||
}
|
||||
} else {
|
||||
callbackFunction(result);
|
||||
}
|
||||
});
|
||||
db.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
71
NodeJsProjects/CoAuthoring/sources/server.js
Normal file
71
NodeJsProjects/CoAuthoring/sources/server.js
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* (c) Copyright Ascensio System SIA 2010-2014
|
||||
*
|
||||
* 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 config = require("./config.json");
|
||||
process.env.NODE_ENV = config["server"]["mode"];
|
||||
var logger = require("./../../Common/sources/logger");
|
||||
var express = require("express");
|
||||
var http = require("http");
|
||||
var https = require("https");
|
||||
var fs = require("fs");
|
||||
var app = express();
|
||||
var server = {};
|
||||
if (config["ssl"]) {
|
||||
var privateKey = fs.readFileSync(config["ssl"]["key"]).toString();
|
||||
var certificateKey = fs.readFileSync(config["ssl"]["cert"]).toString();
|
||||
var trustedCertificate = fs.readFileSync(config["ssl"]["ca"]).toString();
|
||||
var options = {
|
||||
key: privateKey,
|
||||
cert: certificateKey,
|
||||
ca: [trustedCertificate]
|
||||
};
|
||||
server = https.createServer(options, app);
|
||||
} else {
|
||||
server = http.createServer(app);
|
||||
}
|
||||
app.configure("development", function () {
|
||||
app.use(express.errorHandler({
|
||||
dumpExceptions: true,
|
||||
showStack: true
|
||||
}));
|
||||
});
|
||||
app.configure("production", function () {
|
||||
app.use(express.errorHandler());
|
||||
});
|
||||
var docsCoServer = require("./DocsCoServer");
|
||||
docsCoServer.install(server, function () {
|
||||
server.listen(config["server"]["port"], function () {
|
||||
logger.info("Express server listening on port %d in %s mode", config["server"]["port"], app.settings.env);
|
||||
});
|
||||
app.get("/index.html", function (req, res) {
|
||||
res.send("Server is functioning normally");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user