sync with ether/etherpad-lite

This commit is contained in:
ilmar 2018-05-02 11:08:57 +03:00
parent 64a2e5b7a3
commit c9863f81ad
20 changed files with 491 additions and 95 deletions

View file

@ -1153,6 +1153,101 @@ function handleClientReady(client, message)
client.join(padIds.padId);
//Save the revision in sessioninfos, we take the revision from the info the client send to us
sessioninfos[client.id].rev = message.client_rev;
//During the client reconnect, client might miss some revisions from other clients. By using client revision,
//this below code sends all the revisions missed during the client reconnect
var revisionsNeeded = [];
var changesets = {};
var startNum = message.client_rev + 1;
var endNum = pad.getHeadRevisionNumber() + 1;
async.series([
//push all the revision numbers needed into revisionsNeeded array
function(callback)
{
var headNum = pad.getHeadRevisionNumber();
if (endNum > headNum+1)
endNum = headNum+1;
if (startNum < 0)
startNum = 0;
for(var r=startNum;r<endNum;r++)
{
revisionsNeeded.push(r);
changesets[r] = {};
}
callback();
},
//get changesets needed for pending revisions
function(callback)
{
async.eachSeries(revisionsNeeded, function(revNum, callback)
{
pad.getRevisionChangeset(revNum, function(err, value)
{
if(ERR(err)) return;
changesets[revNum]['changeset'] = value;
callback();
});
}, callback);
},
//get author for each changeset
function(callback)
{
async.eachSeries(revisionsNeeded, function(revNum, callback)
{
pad.getRevisionAuthor(revNum, function(err, value)
{
if(ERR(err)) return;
changesets[revNum]['author'] = value;
callback();
});
}, callback);
},
//get timestamp for each changeset
function(callback)
{
async.eachSeries(revisionsNeeded, function(revNum, callback)
{
pad.getRevisionDate(revNum, function(err, value)
{
if(ERR(err)) return;
changesets[revNum]['timestamp'] = value;
callback();
});
}, callback);
}
],
//return error and pending changesets
function(err)
{
if(ERR(err, callback)) return;
async.eachSeries(revisionsNeeded, function(r, callback)
{
var forWire = Changeset.prepareForWire(changesets[r]['changeset'], pad.pool);
var wireMsg = {"type":"COLLABROOM",
"data":{type:"CLIENT_RECONNECT",
headRev:pad.getHeadRevisionNumber(),
newRev:r,
changeset:forWire.translated,
apool: forWire.pool,
author: changesets[r]['author'],
currentTime: changesets[r]['timestamp']
}};
client.json.send(wireMsg);
callback();
});
if (startNum == endNum)
{
var Msg = {"type":"COLLABROOM",
"data":{type:"CLIENT_RECONNECT",
noChanges: true,
newRev: pad.getHeadRevisionNumber()
}};
client.json.send(Msg);
}
});
}
//This is a normal first connect
else

View file

@ -25,6 +25,10 @@ exports.createServer = function () {
else{
console.warn("Admin username and password not set in settings.json. To access admin please uncomment and edit 'users' in settings.json");
}
var env = process.env.NODE_ENV || 'development';
if(env !== 'production'){
console.warn("Etherpad is running in Development mode. This mode is slower for users and less secure than production mode. You should set the NODE_ENV environment variable to production by using: export NODE_ENV=production");
}
}
exports.restartServer = function () {

View file

@ -3,7 +3,7 @@ var apiLogger = log4js.getLogger("API");
var clientLogger = log4js.getLogger("client");
var formidable = require('formidable');
var apiHandler = require('../../handler/APIHandler');
var isVarName = require('is-var-name');
var isValidJSONPName = require('./isValidJSONPName');
//This is for making an api call, collecting all post information and passing it to the apiHandler
var apiCaller = function(req, res, fields) {
@ -19,7 +19,7 @@ var apiCaller = function(req, res, fields) {
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
//is this a jsonp call, if yes, add the function call
if(req.query.jsonp && isVarName(req.query.jsonp))
if(req.query.jsonp && isValidJSONPName.check(req.query.jsonp))
response = req.query.jsonp + "(" + response + ")";
res._____send(response);
@ -46,7 +46,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
//The Etherpad client side sends information about how a disconnect happened
args.app.post('/ep/pad/connection-diagnostic-info', function(req, res) {
new formidable.IncomingForm().parse(req, function(err, fields, files) {
new formidable.IncomingForm().parse(req, function(err, fields, files) {
clientLogger.info("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
res.end("OK");
});
@ -54,7 +54,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
//The Etherpad client side sends information about client side javscript errors
args.app.post('/jserror', function(req, res) {
new formidable.IncomingForm().parse(req, function(err, fields, files) {
new formidable.IncomingForm().parse(req, function(err, fields, files) {
try {
var data = JSON.parse(fields.errorInfo)
}catch(e){
@ -64,7 +64,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.end("OK");
});
});
//Provide a possibility to query the latest available API version
args.app.get('/api', function (req, res) {
res.json({"currentVersion" : apiHandler.latestApiVersion});

View file

@ -2,6 +2,7 @@ var hasPadAccess = require("../../padaccess");
var settings = require('../../utils/Settings');
var exportHandler = require('../../handler/ExportHandler');
var importHandler = require('../../handler/ImportHandler');
var padManager = require("../../db/PadManager");
exports.expressCreateServer = function (hook_name, args, cb) {
args.app.get('/p/:pad/:rev?/export/:type', function(req, res, next) {
@ -22,14 +23,29 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.header("Access-Control-Allow-Origin", "*");
hasPadAccess(req, res, function() {
exportHandler.doExport(req, res, req.params.pad, req.params.type);
console.log('req.params.pad', req.params.pad);
padManager.doesPadExists(req.params.pad, function(err, exists)
{
if(!exists) {
return next();
}
exportHandler.doExport(req, res, req.params.pad, req.params.type);
});
});
});
//handle import requests
args.app.post('/p/:pad/import', function(req, res, next) {
hasPadAccess(req, res, function() {
importHandler.doImport(req, res, req.params.pad);
padManager.doesPadExists(req.params.pad, function(err, exists)
{
if(!exists) {
return next();
}
importHandler.doImport(req, res, req.params.pad);
});
});
});
}

View file

@ -0,0 +1,83 @@
const RESERVED_WORDS = [
'abstract',
'arguments',
'await',
'boolean',
'break',
'byte',
'case',
'catch',
'char',
'class',
'const',
'continue',
'debugger',
'default',
'delete',
'do',
'double',
'else',
'enum',
'eval',
'export',
'extends',
'false',
'final',
'finally',
'float',
'for',
'function',
'goto',
'if',
'implements',
'import',
'in',
'instanceof',
'int',
'interface',
'let',
'long',
'native',
'new',
'null',
'package',
'private',
'protected',
'public',
'return',
'short',
'static',
'super',
'switch',
'synchronized',
'this',
'throw',
'throws',
'transient',
'true',
'try',
'typeof',
'var',
'void',
'volatile',
'while',
'with',
'yield'
];
const regex = /^[a-zA-Z_$][0-9a-zA-Z_$]*(?:\[(?:".+"|\'.+\'|\d+)\])*?$/;
module.exports.check = function(inputStr) {
var isValid = true;
inputStr.split(".").forEach(function(part) {
if (!regex.test(part)) {
isValid = false;
}
if (RESERVED_WORDS.indexOf(part) !== -1) {
isValid = false;
}
});
return isValid;
}

View file

@ -20,7 +20,7 @@ exports.basicAuth = function (req, res, next) {
// Do not require auth for static paths and the API...this could be a bit brittle
if (req.path.match(/^\/(static|javascripts|pluginfw|api)/)) return cb(true);
if (req.path.indexOf('/admin') != 0) {
if (req.path.toLowerCase().indexOf('/admin') != 0) {
if (!settings.requireAuthentication) return cb(true);
if (!settings.requireAuthorization && req.session && req.session.user) return cb(true);
}
@ -38,7 +38,7 @@ exports.basicAuth = function (req, res, next) {
var password = userpass.join(':');
var fallback = function(success) {
if (success) return cb(true);
if (settings.users[username] != undefined && settings.users[username].password == password) {
if (settings.users[username] != undefined && settings.users[username].password === password) {
settings.users[username].username = username;
req.session.user = settings.users[username];
return cb(true);
@ -129,4 +129,3 @@ exports.expressConfigure = function (hook_name, args, cb) {
args.app.use(exports.basicAuth);
}

View file

@ -22,25 +22,18 @@ var ERR = require("async-stacktrace");
exports.getPadRaw = function(padId, callback){
async.waterfall([
function(cb){
// Get the Pad
db.findKeys("pad:"+padId, null, function(err,padcontent){
if(!err){
cb(err, padcontent);
}
})
db.get("pad:"+padId, cb);
},
function(padcontent,cb){
var records = ["pad:"+padId];
for (var i = 0; i <= padcontent.head; i++) {
records.push("pad:"+padId+":revs:" + i);
}
for (var i = 0; i <= padcontent.chatHead; i++) {
records.push("pad:"+padId+":chat:" + i);
}
// Get the Pad available content keys
db.findKeys("pad:"+padId+":*", null, function(err,records){
if(!err){
for (var key in padcontent) { records.push(padcontent[key]);}
cb(err, records);
}
})
},
function(records, cb){
var data = {};
async.forEachSeries(Object.keys(records), function(key, r){
@ -69,7 +62,7 @@ exports.getPadRaw = function(padId, callback){
}
r(null); // callback;
});
}, function(err){
}, function(err){
cb(err, data);
})
}

View file

@ -356,15 +356,7 @@ function getHTMLFromAtext(pad, atext, authorColors)
}
}
}
var context = {
line: line,
lineContent: lineContent,
apool: apool,
attribLine: attribLines[i],
text: textLines[i],
padId: pad.id
}
var lineContentFromHook = hooks.callAll("getLineHTMLForExport", context);
if (whichList >= lists.length)//means we are on a deeper level of indentation than the previous line
{
if(lists.length > 0){
@ -381,14 +373,14 @@ function getHTMLFromAtext(pad, atext, authorColors)
if(toOpen > 0){
pieces.push(new Array(toOpen + 1).join('<ol>'))
}
pieces.push('<ol class="'+line.listTypeName+'"><li>', context.lineContent || '<br>');
pieces.push('<ol class="'+line.listTypeName+'"><li>', lineContent || '<br>');
}
else
{
if(toOpen > 0){
pieces.push(new Array(toOpen + 1).join('<ul>'))
}
pieces.push('<ul class="'+line.listTypeName+'"><li>', context.lineContent || '<br>');
pieces.push('<ul class="'+line.listTypeName+'"><li>', lineContent || '<br>');
}
}
//the following code *seems* dead after my patch.
@ -424,16 +416,16 @@ function getHTMLFromAtext(pad, atext, authorColors)
if(lists[lists.length - 1][1] == "number")
{
pieces.push(new Array(toClose+1).join('</ol>'))
pieces.push('<li>', context.lineContent || '<br>');
pieces.push('<li>', lineContent || '<br>');
}
else
{
pieces.push(new Array(toClose+1).join('</ul>'))
pieces.push('<li>', context.lineContent || '<br>');
pieces.push('<li>', lineContent || '<br>');
}
lists = lists.slice(0,whichList+1)
} else {
pieces.push('</li><li>', context.lineContent || '<br>');
pieces.push('</li><li>', lineContent || '<br>');
}
}
}
@ -459,9 +451,16 @@ function getHTMLFromAtext(pad, atext, authorColors)
padId: pad.id
}
hooks.callAll("getLineHTMLForExport", context);
var lineContentFromHook = hooks.callAllStr("getLineHTMLForExport", context, " ", " ", "");
pieces.push(context.lineContent, '<br>');
if (lineContentFromHook)
{
pieces.push(lineContentFromHook, '');
}
else
{
pieces.push(lineContent, '<br>');
}
}
}