Pads are now saved to the database, but some weird Bugs with the Attribute Pool

This commit is contained in:
Peter 'Pita' Martischka 2011-05-17 16:33:54 +01:00
parent 73efbf216d
commit 08b8568f7b
3 changed files with 236 additions and 158 deletions

View file

@ -243,8 +243,22 @@ function handleUserChanges(client, message)
var baseRev = message.data.baseRev; var baseRev = message.data.baseRev;
var wireApool = (AttributePoolFactory.createAttributePool()).fromJsonable(message.data.apool); var wireApool = (AttributePoolFactory.createAttributePool()).fromJsonable(message.data.apool);
var changeset = message.data.changeset; var changeset = message.data.changeset;
var pad = padManager.getPad(session2pad[client.sessionId], false);
var r, apool, pad;
async.series([
//get the pad
function(callback)
{
padManager.getPad(session2pad[client.sessionId], function(err, value)
{
pad = value;
callback(err);
});
},
//create the changeset
function(callback)
{
//ex. _checkChangesetAndPool //ex. _checkChangesetAndPool
//Copied from Etherpad, don't know what it does exactly //Copied from Etherpad, don't know what it does exactly
@ -261,16 +275,37 @@ function handleUserChanges(client, message)
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool); changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
//ex. applyUserChanges //ex. applyUserChanges
apool = pad.pool;
r = baseRev;
var apool = pad.pool; //https://github.com/caolan/async#whilst
var r = baseRev; async.whilst(
function() { return r < pad.getHeadRevisionNumber(); },
while (r < pad.getHeadRevisionNumber()) { function(callback)
{
r++; r++;
var c = pad.getRevisionChangeset(r);
changeset = Changeset.follow(c, changeset, false, apool);
}
pad.getRevisionChangeset(r, function(err, c)
{
if(err)
{
callback(err);
return;
}
else
{
changeset = Changeset.follow(c, changeset, false, apool);
callback(null);
}
});
},
//use the callback of the series function
callback
);
},
//do correction changesets, and send it to all users
function (callback)
{
var prevText = pad.text(); var prevText = pad.text();
if (Changeset.oldLen(changeset) != prevText.length) { if (Changeset.oldLen(changeset) != prevText.length) {
throw "Can't apply USER_CHANGES "+changeset+" with oldLen " throw "Can't apply USER_CHANGES "+changeset+" with oldLen "
@ -287,22 +322,51 @@ function handleUserChanges(client, message)
} }
if (pad.text().lastIndexOf("\n\n") != pad.text().length-2) { if (pad.text().lastIndexOf("\n\n") != pad.text().length-2) {
var nlChangeset = Changeset.makeSplice( var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n");
pad.text(), pad.text().length-1, 0, "\n");
pad.appendRevision(nlChangeset); pad.appendRevision(nlChangeset);
} }
//ex. updatePadClients //ex. updatePadClients
for(i in pad2sessions[pad.id]) //go trough all sessions on this pad
async.forEach(pad2sessions[pad.id], function(session, callback)
{ {
var session = pad2sessions[pad.id][i];
var lastRev = sessioninfos[session].rev; var lastRev = sessioninfos[session].rev;
while (lastRev < pad.getHeadRevisionNumber()) //https://github.com/caolan/async#whilst
//send them all new changesets
async.whilst(
function (){ return lastRev < pad.getHeadRevisionNumber()},
function(callback)
{ {
var author, revChangeset;
var r = ++lastRev; var r = ++lastRev;
var author = pad.getRevisionAuthor(r);
async.parallel([
function (callback)
{
pad.getRevisionAuthor(r, function(err, value)
{
author = value;
callback(err);
});
},
function (callback)
{
pad.getRevisionChangeset(r, function(err, value)
{
revChangeset = value;
callback(err);
});
}
], function(err)
{
if(err)
{
callback(err);
return;
}
if(author == sessioninfos[session].author) if(author == sessioninfos[session].author)
{ {
@ -310,7 +374,7 @@ function handleUserChanges(client, message)
} }
else else
{ {
var forWire = Changeset.prepareForWire(pad.getRevisionChangeset(r), pad.pool); var forWire = Changeset.prepareForWire(revChangeset, pad.pool);
var wireMsg = {"type":"COLLABROOM","data":{type:"NEW_CHANGES", newRev:r, var wireMsg = {"type":"COLLABROOM","data":{type:"NEW_CHANGES", newRev:r,
changeset: forWire.translated, changeset: forWire.translated,
apool: forWire.pool, apool: forWire.pool,
@ -318,10 +382,20 @@ function handleUserChanges(client, message)
socketio.clients[session].send(wireMsg); socketio.clients[session].send(wireMsg);
} }
}
callback(null);
});
},
callback
);
sessioninfos[session].rev = pad.getHeadRevisionNumber(); sessioninfos[session].rev = pad.getHeadRevisionNumber();
},callback);
} }
], function(err)
{
if(err) throw err;
});
} }
/** /**
@ -395,8 +469,9 @@ function handleClientReady(client, message)
var author; var author;
var authorName; var authorName;
var authorColorId; var authorColorId;
var pad;
async.waterfall([ async.series([
//get all authordata of this new user //get all authordata of this new user
function(callback) function(callback)
{ {
@ -423,6 +498,14 @@ function handleClientReady(client, message)
authorName = value; authorName = value;
callback(err); callback(err);
}); });
},
function(callback)
{
padManager.getPad(message.padId, function(err, value)
{
pad = value;
callback(err);
});
} }
], callback); ], callback);
}); });
@ -455,12 +538,6 @@ function handleClientReady(client, message)
//Saves in pad2sessions that this session belongs to this pad //Saves in pad2sessions that this session belongs to this pad
pad2sessions[message.padId].push(sessionId); pad2sessions[message.padId].push(sessionId);
//Tell the PadManager that it should ensure that this Pad exist
padManager.ensurePadExists(message.padId);
//Ask the PadManager for a function Wrapper for this Pad
var pad = padManager.getPad(message.padId, false);
//prepare all values for the wire //prepare all values for the wire
atext = pad.atext; atext = pad.atext;
var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool); var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);

View file

@ -1,5 +1,7 @@
var Changeset = require("../Changeset"); var Changeset = require("../Changeset");
var AttributePoolFactory = require("../AttributePoolFactory"); var AttributePoolFactory = require("../AttributePoolFactory");
var db = require("../db").db;
var async = require("async");
exports.startText = "Welcome to Etherpad Lite. This pad text is synchronized as you type, so that everyone viewing this page sees the same text."; exports.startText = "Welcome to Etherpad Lite. This pad text is synchronized as you type, so that everyone viewing this page sees the same text.";
@ -27,23 +29,13 @@ Class('Pad', {
getterName : 'apool' // legacy getterName : 'apool' // legacy
}, // pool }, // pool
rev : {
is : 'rw',
init : []
}, // rev
head : { head : {
is : 'rw', is : 'rw',
init : -1, init : -1,
getterName : 'getHeadRevisionNumber' getterName : 'getHeadRevisionNumber'
}, // head }, // head
authors : { id : { is : 'r' }
is : 'rw',
init : []
},
id : { is : 'rw' }
}, },
methods : { methods : {
@ -65,11 +57,12 @@ Class('Pad', {
Changeset.copyAText(newAText, this.atext); Changeset.copyAText(newAText, this.atext);
var newRev = ++this.head; var newRev = ++this.head;
this.rev[newRev] = {};
this.rev[newRev].changeset = aChangeset; var newRevData = {};
this.rev[newRev].meta = {}; newRevData.changeset = aChangeset;
this.rev[newRev].meta.author = author; newRevData.meta = {};
this.rev[newRev].meta.timestamp = new Date().getTime(); newRevData.meta.author = author;
newRevData.meta.timestamp = new Date().getTime();
//ex. getNumForAuthor //ex. getNumForAuthor
if(author != '') if(author != '')
@ -77,34 +70,21 @@ Class('Pad', {
if(newRev % 100 == 0) if(newRev % 100 == 0)
{ {
this.rev[newRev].meta.atext = this.atext; newRevData.meta.atext = this.atext;
} }
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
db.set("pad:"+this.id, {atext: this.atext, pool: this.pool.toJsonable(), head: this.head});
}, //appendRevision }, //appendRevision
getRevisionChangeset : function(revNum) getRevisionChangeset : function(revNum, callback)
{ {
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
if(revNum < this.rev.length)
{
return this.rev[revNum].changeset;
} else {
throw 'this revision does not exist! : ' + revNum;
return null;
}
}, // getRevisionChangeset }, // getRevisionChangeset
getRevisionAuthor : function(revNum) getRevisionAuthor : function(revNum, callback)
{ {
if(revNum < this.rev.length) db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
{
return this.rev[revNum].meta.author;
} else {
throw 'this revision author does not exist! : ' + revNum;
return null;
}
}, // getRevisionAuthor }, // getRevisionAuthor
getAllAuthors : function() getAllAuthors : function()
@ -125,21 +105,39 @@ Class('Pad', {
text : function() text : function()
{ {
return this.atext.text; return this.atext.text;
},
init : function (callback)
{
var _this = this;
//try to load the pad
db.get("pad:"+this.id, function(err, value)
{
if(err)
{
callback(err, null);
return;
}
//if this pad exists, load it
if(value != null)
{
_this.head = value.head;
_this.atext = value.atext;
_this.pool = _this.pool.fromJsonable(value.pool);
}
//this pad doesn't exist, so create it
else
{
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(exports.startText));
_this.appendRevision(firstChangeset, '');
}
callback(null);
});
} }
}, // methods }, // methods
after : {
initialize : function (props)
{
this.id = props.id;
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(exports.startText));
this.appendRevision(firstChangeset, '');
}
}
}); });

View file

@ -31,31 +31,34 @@ globalPads = [];
* @param id A String with the id of the pad * @param id A String with the id of the pad
* @param createIfNotExist A Boolean which says the function if it should create the Pad if it not exist * @param createIfNotExist A Boolean which says the function if it should create the Pad if it not exist
*/ */
exports.getPad = function(id, createIfNotExist) exports.getPad = function(id, callback)
{ {
var pad = globalPads[id]; var pad = globalPads[id];
if(!pad && createIfNotExist == true) //return pad if its already loaded
if(pad != null)
{
callback(null, pad);
}
//try to load pad
else
{ {
pad = new Pad(id); pad = new Pad(id);
globalPads[id] = pad;
}
if(!pad) return null; //initalize the pad
pad.init(function(err)
{
if(err)
{
callback(err, null);
}
else
{
globalPads[id] = pad;
callback(null, pad);
}
});
}
//globalPads[id].timestamp = new Date().getTime(); //globalPads[id].timestamp = new Date().getTime();
return pad;
}
/**
* Ensures that the Pad exists
* @param id The Pad id
*/
exports.ensurePadExists = function(id)
{
if(!globalPads[id])
{
exports.getPad(id, true);
}
} }