mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2024-11-25 19:26:31 -05:00
Update to move authorship calculation code to note model and support update authorship after making revision of docs
This commit is contained in:
parent
a5e6b5dd3b
commit
d23ced1fba
2 changed files with 177 additions and 101 deletions
|
@ -11,11 +11,16 @@ var shortId = require('shortid');
|
||||||
var Sequelize = require("sequelize");
|
var Sequelize = require("sequelize");
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var moment = require('moment');
|
var moment = require('moment');
|
||||||
|
var DiffMatchPatch = require('diff-match-patch');
|
||||||
|
var dmp = new DiffMatchPatch();
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var config = require("../config.js");
|
var config = require("../config.js");
|
||||||
var logger = require("../logger.js");
|
var logger = require("../logger.js");
|
||||||
|
|
||||||
|
//ot
|
||||||
|
var ot = require("../ot/index.js");
|
||||||
|
|
||||||
// permission types
|
// permission types
|
||||||
var permissionTypes = ["freely", "editable", "locked", "private"];
|
var permissionTypes = ["freely", "editable", "locked", "private"];
|
||||||
|
|
||||||
|
@ -115,6 +120,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
var fsModifiedTime = moment(fs.statSync(filePath).mtime);
|
var fsModifiedTime = moment(fs.statSync(filePath).mtime);
|
||||||
var dbModifiedTime = moment(note.lastchangeAt || note.createdAt);
|
var dbModifiedTime = moment(note.lastchangeAt || note.createdAt);
|
||||||
var body = fs.readFileSync(filePath, 'utf8');
|
var body = fs.readFileSync(filePath, 'utf8');
|
||||||
|
var contentLength = body.length;
|
||||||
var title = Note.parseNoteTitle(body);
|
var title = Note.parseNoteTitle(body);
|
||||||
body = LZString.compressToBase64(body);
|
body = LZString.compressToBase64(body);
|
||||||
title = LZString.compressToBase64(title);
|
title = LZString.compressToBase64(title);
|
||||||
|
@ -126,7 +132,20 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}).then(function (note) {
|
}).then(function (note) {
|
||||||
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
|
sequelize.models.Revision.saveNoteRevision(note, function (err, revision) {
|
||||||
if (err) return _callback(err, null);
|
if (err) return _callback(err, null);
|
||||||
|
// update authorship on after making revision of docs
|
||||||
|
var patch = dmp.patch_fromText(LZString.decompressFromBase64(revision.patch));
|
||||||
|
var operations = Note.transformPatchToOperations(patch, contentLength);
|
||||||
|
var authorship = note.authorship ? JSON.parse(LZString.decompressFromBase64(note.authorship)) : [];
|
||||||
|
for (var i = 0; i < operations.length; i++) {
|
||||||
|
authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship);
|
||||||
|
}
|
||||||
|
note.update({
|
||||||
|
authorship: authorship
|
||||||
|
}).then(function (note) {
|
||||||
return callback(null, note.id);
|
return callback(null, note.id);
|
||||||
|
}).catch(function (err) {
|
||||||
|
return _callback(err, null);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
return _callback(err, null);
|
return _callback(err, null);
|
||||||
|
@ -247,6 +266,162 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
_meta.slideOptions = meta.slideOptions;
|
_meta.slideOptions = meta.slideOptions;
|
||||||
}
|
}
|
||||||
return _meta;
|
return _meta;
|
||||||
|
},
|
||||||
|
updateAuthorshipByOperation: function (operation, userId, authorships) {
|
||||||
|
var index = 0;
|
||||||
|
var timestamp = Date.now();
|
||||||
|
for (var i = 0; i < operation.length; i++) {
|
||||||
|
var op = operation[i];
|
||||||
|
if (ot.TextOperation.isRetain(op)) {
|
||||||
|
index += op;
|
||||||
|
} else if (ot.TextOperation.isInsert(op)) {
|
||||||
|
var opStart = index;
|
||||||
|
var opEnd = index + op.length;
|
||||||
|
var inserted = false;
|
||||||
|
// authorship format: [userId, startPos, endPos, createdAt, updatedAt]
|
||||||
|
if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp]);
|
||||||
|
else {
|
||||||
|
for (var j = 0; j < authorships.length; j++) {
|
||||||
|
var authorship = authorships[j];
|
||||||
|
if (!inserted) {
|
||||||
|
var nextAuthorship = authorships[j + 1] || -1;
|
||||||
|
if (nextAuthorship != -1 && nextAuthorship[1] >= opEnd || j >= authorships.length - 1) {
|
||||||
|
if (authorship[1] < opStart && authorship[2] > opStart) {
|
||||||
|
// divide
|
||||||
|
var postLength = authorship[2] - opStart;
|
||||||
|
authorship[2] = opStart;
|
||||||
|
authorship[4] = timestamp;
|
||||||
|
authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
|
||||||
|
authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp]);
|
||||||
|
j += 2;
|
||||||
|
inserted = true;
|
||||||
|
} else if (authorship[1] >= opStart) {
|
||||||
|
authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp]);
|
||||||
|
j += 1;
|
||||||
|
inserted = true;
|
||||||
|
} else if (authorship[2] <= opStart) {
|
||||||
|
authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
|
||||||
|
j += 1;
|
||||||
|
inserted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (authorship[1] >= opStart) {
|
||||||
|
authorship[1] += op.length;
|
||||||
|
authorship[2] += op.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += op.length;
|
||||||
|
} else if (ot.TextOperation.isDelete(op)) {
|
||||||
|
var opStart = index;
|
||||||
|
var opEnd = index - op;
|
||||||
|
if (operation.length == 1) {
|
||||||
|
authorships = [];
|
||||||
|
} else if (authorships.length > 0) {
|
||||||
|
for (var j = 0; j < authorships.length; j++) {
|
||||||
|
var authorship = authorships[j];
|
||||||
|
if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
|
||||||
|
authorships.splice(j, 1);
|
||||||
|
j -= 1;
|
||||||
|
} else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) {
|
||||||
|
authorship[2] += op;
|
||||||
|
authorship[4] = timestamp;
|
||||||
|
} else if (authorship[2] >= opStart && authorship[2] <= opEnd) {
|
||||||
|
authorship[2] = opStart;
|
||||||
|
authorship[4] = timestamp;
|
||||||
|
} else if (authorship[1] >= opStart && authorship[1] <= opEnd) {
|
||||||
|
authorship[1] = opEnd;
|
||||||
|
authorship[4] = timestamp;
|
||||||
|
}
|
||||||
|
if (authorship[1] >= opEnd) {
|
||||||
|
authorship[1] += op;
|
||||||
|
authorship[2] += op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += op;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// merge
|
||||||
|
for (var j = 0; j < authorships.length; j++) {
|
||||||
|
var authorship = authorships[j];
|
||||||
|
for (var k = j + 1; k < authorships.length; k++) {
|
||||||
|
var nextAuthorship = authorships[k];
|
||||||
|
if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
|
||||||
|
var minTimestamp = Math.min(authorship[3], nextAuthorship[3]);
|
||||||
|
var maxTimestamp = Math.max(authorship[3], nextAuthorship[3]);
|
||||||
|
authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp]);
|
||||||
|
authorships.splice(k, 1);
|
||||||
|
j -= 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// clear
|
||||||
|
for (var j = 0; j < authorships.length; j++) {
|
||||||
|
var authorship = authorships[j];
|
||||||
|
if (!authorship[0]) {
|
||||||
|
authorships.splice(j, 1);
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return authorships;
|
||||||
|
},
|
||||||
|
transformPatchToOperations: function (patch, contentLength) {
|
||||||
|
var operations = [];
|
||||||
|
if (patch.length > 0) {
|
||||||
|
// calculate original content length
|
||||||
|
for (var j = patch.length - 1; j >= 0; j--) {
|
||||||
|
var p = patch[j];
|
||||||
|
for (var i = 0; i < p.diffs.length; i++) {
|
||||||
|
var diff = p.diffs[i];
|
||||||
|
switch(diff[0]) {
|
||||||
|
case 1: // insert
|
||||||
|
contentLength -= diff[1].length;
|
||||||
|
break;
|
||||||
|
case -1: // delete
|
||||||
|
contentLength += diff[1].length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// generate operations
|
||||||
|
var bias = 0;
|
||||||
|
var lengthBias = 0;
|
||||||
|
for (var j = 0; j < patch.length; j++) {
|
||||||
|
var operation = [];
|
||||||
|
var p = patch[j];
|
||||||
|
var currIndex = p.start1;
|
||||||
|
var currLength = contentLength - bias;
|
||||||
|
for (var i = 0; i < p.diffs.length; i++) {
|
||||||
|
var diff = p.diffs[i];
|
||||||
|
switch(diff[0]) {
|
||||||
|
case 0: // retain
|
||||||
|
if (i == 0) // first
|
||||||
|
operation.push(currIndex + diff[1].length);
|
||||||
|
else if (i != p.diffs.length - 1) // mid
|
||||||
|
operation.push(diff[1].length);
|
||||||
|
else // last
|
||||||
|
operation.push(currLength + lengthBias - currIndex);
|
||||||
|
currIndex += diff[1].length;
|
||||||
|
break;
|
||||||
|
case 1: // insert
|
||||||
|
operation.push(diff[1]);
|
||||||
|
lengthBias += diff[1].length;
|
||||||
|
currIndex += diff[1].length;
|
||||||
|
break;
|
||||||
|
case -1: // delete
|
||||||
|
operation.push(-diff[1].length);
|
||||||
|
bias += diff[1].length;
|
||||||
|
currIndex += diff[1].length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operations.push(operation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return operations;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
hooks: {
|
hooks: {
|
||||||
|
|
101
lib/realtime.js
101
lib/realtime.js
|
@ -642,106 +642,7 @@ function operationCallback(socket, operation) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// save authorship
|
// save authorship
|
||||||
var index = 0;
|
note.authorship = models.Note.updateAuthorshipByOperation(operation, userId, note.authorship);
|
||||||
var authorships = note.authorship;
|
|
||||||
var timestamp = Date.now();
|
|
||||||
for (var i = 0; i < operation.length; i++) {
|
|
||||||
var op = operation[i];
|
|
||||||
if (ot.TextOperation.isRetain(op)) {
|
|
||||||
index += op;
|
|
||||||
} else if (ot.TextOperation.isInsert(op)) {
|
|
||||||
var opStart = index;
|
|
||||||
var opEnd = index + op.length;
|
|
||||||
var inserted = false;
|
|
||||||
// authorship format: [userId, startPos, endPos, createdAt, updatedAt]
|
|
||||||
if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp]);
|
|
||||||
else {
|
|
||||||
for (var j = 0; j < authorships.length; j++) {
|
|
||||||
var authorship = authorships[j];
|
|
||||||
if (!inserted) {
|
|
||||||
var nextAuthorship = authorships[j + 1] || -1;
|
|
||||||
if (nextAuthorship != -1 && nextAuthorship[1] >= opEnd || j >= authorships.length - 1) {
|
|
||||||
if (authorship[1] < opStart && authorship[2] > opStart) {
|
|
||||||
// divide
|
|
||||||
var postLength = authorship[2] - opStart;
|
|
||||||
authorship[2] = opStart;
|
|
||||||
authorship[4] = timestamp;
|
|
||||||
authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
|
|
||||||
authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp]);
|
|
||||||
j += 2;
|
|
||||||
inserted = true;
|
|
||||||
} else if (authorship[1] >= opStart) {
|
|
||||||
authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp]);
|
|
||||||
j += 1;
|
|
||||||
inserted = true;
|
|
||||||
} else if (authorship[2] <= opStart) {
|
|
||||||
authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]);
|
|
||||||
j += 1;
|
|
||||||
inserted = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (authorship[1] >= opStart) {
|
|
||||||
authorship[1] += op.length;
|
|
||||||
authorship[2] += op.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index += op.length;
|
|
||||||
} else if (ot.TextOperation.isDelete(op)) {
|
|
||||||
var opStart = index;
|
|
||||||
var opEnd = index - op;
|
|
||||||
if (operation.length == 1) {
|
|
||||||
authorships = [];
|
|
||||||
} else if (authorships.length > 0) {
|
|
||||||
for (var j = 0; j < authorships.length; j++) {
|
|
||||||
var authorship = authorships[j];
|
|
||||||
if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) {
|
|
||||||
authorships.splice(j, 1);
|
|
||||||
j -= 1;
|
|
||||||
} else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) {
|
|
||||||
authorship[2] += op;
|
|
||||||
authorship[4] = timestamp;
|
|
||||||
} else if (authorship[2] >= opStart && authorship[2] <= opEnd) {
|
|
||||||
authorship[2] = opStart;
|
|
||||||
authorship[4] = timestamp;
|
|
||||||
} else if (authorship[1] >= opStart && authorship[1] <= opEnd) {
|
|
||||||
authorship[1] = opEnd;
|
|
||||||
authorship[4] = timestamp;
|
|
||||||
}
|
|
||||||
if (authorship[1] >= opEnd) {
|
|
||||||
authorship[1] += op;
|
|
||||||
authorship[2] += op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index += op;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// merge
|
|
||||||
for (var j = 0; j < authorships.length; j++) {
|
|
||||||
var authorship = authorships[j];
|
|
||||||
for (var k = j + 1; k < authorships.length; k++) {
|
|
||||||
var nextAuthorship = authorships[k];
|
|
||||||
if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) {
|
|
||||||
var minTimestamp = Math.min(authorship[3], nextAuthorship[3]);
|
|
||||||
var maxTimestamp = Math.max(authorship[3], nextAuthorship[3]);
|
|
||||||
authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp]);
|
|
||||||
authorships.splice(k, 1);
|
|
||||||
j -= 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// clear
|
|
||||||
for (var j = 0; j < authorships.length; j++) {
|
|
||||||
var authorship = authorships[j];
|
|
||||||
if (!authorship[0]) {
|
|
||||||
authorships.splice(j, 1);
|
|
||||||
j -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
note.authorship = authorships;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function connection(socket) {
|
function connection(socket) {
|
||||||
|
|
Loading…
Reference in a new issue