mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-02-08 03:02:03 +01:00
Merge branch 'master' of github.com:Pita/etherpad-lite
This commit is contained in:
commit
dfa106df03
4 changed files with 415 additions and 458 deletions
829
node/db/Pad.js
829
node/db/Pad.js
|
@ -2,8 +2,6 @@
|
||||||
* The pad object, defined with joose
|
* The pad object, defined with joose
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require('joose');
|
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var Changeset = require("../utils/Changeset");
|
var Changeset = require("../utils/Changeset");
|
||||||
var AttributePoolFactory = require("../utils/AttributePoolFactory");
|
var AttributePoolFactory = require("../utils/AttributePoolFactory");
|
||||||
|
@ -22,490 +20,451 @@ var crypto = require("crypto");
|
||||||
*/
|
*/
|
||||||
exports.cleanText = function (txt) {
|
exports.cleanText = function (txt) {
|
||||||
return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' ');
|
return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' ');
|
||||||
}
|
};
|
||||||
|
|
||||||
Class('Pad', {
|
|
||||||
|
|
||||||
// these are the properties
|
var Pad = function Pad(id) {
|
||||||
has : {
|
|
||||||
|
|
||||||
atext : {
|
this.atext = Changeset.makeAText("\n");
|
||||||
is : 'rw', // readwrite
|
this.pool = AttributePoolFactory.createAttributePool();
|
||||||
init : function() { return Changeset.makeAText("\n"); } // first value
|
this.head = -1;
|
||||||
}, // atext
|
this.chatHead = -1;
|
||||||
|
this.publicStatus = false;
|
||||||
|
this.passwordHash = null;
|
||||||
|
this.id = id;
|
||||||
|
|
||||||
pool : {
|
};
|
||||||
is: 'rw',
|
|
||||||
init : function() { return AttributePoolFactory.createAttributePool(); },
|
|
||||||
getterName : 'apool' // legacy
|
|
||||||
}, // pool
|
|
||||||
|
|
||||||
head : {
|
exports.Pad = Pad;
|
||||||
is : 'rw',
|
|
||||||
init : -1,
|
|
||||||
getterName : 'getHeadRevisionNumber'
|
|
||||||
}, // head
|
|
||||||
|
|
||||||
chatHead : {
|
Pad.prototype.apool = function apool() {
|
||||||
is: 'rw',
|
return this.pool;
|
||||||
init: -1
|
};
|
||||||
}, // chatHead
|
|
||||||
|
|
||||||
publicStatus : {
|
Pad.prototype.getHeadRevisionNumber = function getHeadRevisionNumber() {
|
||||||
is: 'rw',
|
return this.head;
|
||||||
init: false,
|
};
|
||||||
getterName : 'getPublicStatus'
|
|
||||||
}, //publicStatus
|
|
||||||
|
|
||||||
passwordHash : {
|
Pad.prototype.getPublicStatus = function getPublicStatus() {
|
||||||
is: 'rw',
|
return this.publicStatus;
|
||||||
init: null
|
};
|
||||||
}, // passwordHash
|
|
||||||
|
|
||||||
id : { is : 'r' }
|
Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
|
||||||
},
|
if(!author)
|
||||||
|
author = '';
|
||||||
|
|
||||||
methods : {
|
var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
|
||||||
|
Changeset.copyAText(newAText, this.atext);
|
||||||
|
|
||||||
BUILD : function (id)
|
var newRev = ++this.head;
|
||||||
|
|
||||||
|
var newRevData = {};
|
||||||
|
newRevData.changeset = aChangeset;
|
||||||
|
newRevData.meta = {};
|
||||||
|
newRevData.meta.author = author;
|
||||||
|
newRevData.meta.timestamp = new Date().getTime();
|
||||||
|
|
||||||
|
//ex. getNumForAuthor
|
||||||
|
if(author != '')
|
||||||
|
this.pool.putAttrib(['author', author || '']);
|
||||||
|
|
||||||
|
if(newRev % 100 == 0)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
chatHead: this.chatHead,
|
||||||
|
publicStatus: this.publicStatus,
|
||||||
|
passwordHash: this.passwordHash});
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
|
||||||
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getRevisionAuthor = function getRevisionAuthor(revNum, callback) {
|
||||||
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getRevisionDate = function getRevisionDate(revNum, callback) {
|
||||||
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getAllAuthors = function getAllAuthors() {
|
||||||
|
var authors = [];
|
||||||
|
|
||||||
|
for(key in this.pool.numToAttrib)
|
||||||
|
{
|
||||||
|
if(this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "")
|
||||||
{
|
{
|
||||||
return {
|
authors.push(this.pool.numToAttrib[key][1]);
|
||||||
'id' : id,
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
appendRevision : function(aChangeset, author)
|
return authors;
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targetRev, callback) {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
var keyRev = this.getKeyRevisionNumber(targetRev);
|
||||||
|
var atext;
|
||||||
|
var changesets = [];
|
||||||
|
|
||||||
|
//find out which changesets are needed
|
||||||
|
var neededChangesets = [];
|
||||||
|
var curRev = keyRev;
|
||||||
|
while (curRev < targetRev)
|
||||||
|
{
|
||||||
|
curRev++;
|
||||||
|
neededChangesets.push(curRev);
|
||||||
|
}
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//get all needed data out of the database
|
||||||
|
function(callback)
|
||||||
{
|
{
|
||||||
if(!author)
|
async.parallel([
|
||||||
author = '';
|
//get the atext of the key revision
|
||||||
|
function (callback)
|
||||||
var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
|
|
||||||
Changeset.copyAText(newAText, this.atext);
|
|
||||||
|
|
||||||
var newRev = ++this.head;
|
|
||||||
|
|
||||||
var newRevData = {};
|
|
||||||
newRevData.changeset = aChangeset;
|
|
||||||
newRevData.meta = {};
|
|
||||||
newRevData.meta.author = author;
|
|
||||||
newRevData.meta.timestamp = new Date().getTime();
|
|
||||||
|
|
||||||
//ex. getNumForAuthor
|
|
||||||
if(author != '')
|
|
||||||
this.pool.putAttrib(['author', author || '']);
|
|
||||||
|
|
||||||
if(newRev % 100 == 0)
|
|
||||||
{
|
|
||||||
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,
|
|
||||||
chatHead: this.chatHead,
|
|
||||||
publicStatus: this.publicStatus,
|
|
||||||
passwordHash: this.passwordHash});
|
|
||||||
}, //appendRevision
|
|
||||||
|
|
||||||
getRevisionChangeset : function(revNum, callback)
|
|
||||||
{
|
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
|
||||||
}, // getRevisionChangeset
|
|
||||||
|
|
||||||
getRevisionAuthor : function(revNum, callback)
|
|
||||||
{
|
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
|
|
||||||
}, // getRevisionAuthor
|
|
||||||
|
|
||||||
getRevisionDate : function(revNum, callback)
|
|
||||||
{
|
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
|
|
||||||
}, // getRevisionAuthor
|
|
||||||
|
|
||||||
getAllAuthors : function()
|
|
||||||
{
|
|
||||||
var authors = [];
|
|
||||||
|
|
||||||
for(key in this.pool.numToAttrib)
|
|
||||||
{
|
|
||||||
if(this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "")
|
|
||||||
{
|
{
|
||||||
authors.push(this.pool.numToAttrib[key][1]);
|
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
atext = Changeset.cloneAText(_atext);
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//get all needed changesets
|
||||||
|
function (callback)
|
||||||
|
{
|
||||||
|
async.forEach(neededChangesets, function(item, callback)
|
||||||
|
{
|
||||||
|
_this.getRevisionChangeset(item, function(err, changeset)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
changesets[item] = changeset;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}, callback);
|
||||||
}
|
}
|
||||||
}
|
], callback);
|
||||||
|
|
||||||
return authors;
|
|
||||||
},
|
},
|
||||||
|
//apply all changesets to the key changeset
|
||||||
getInternalRevisionAText : function(targetRev, callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var _this = this;
|
var apool = _this.apool();
|
||||||
|
|
||||||
var keyRev = this.getKeyRevisionNumber(targetRev);
|
|
||||||
var atext;
|
|
||||||
var changesets = [];
|
|
||||||
|
|
||||||
//find out which changesets are needed
|
|
||||||
var neededChangesets = [];
|
|
||||||
var curRev = keyRev;
|
var curRev = keyRev;
|
||||||
|
|
||||||
while (curRev < targetRev)
|
while (curRev < targetRev)
|
||||||
{
|
{
|
||||||
curRev++;
|
curRev++;
|
||||||
neededChangesets.push(curRev);
|
var cs = changesets[curRev];
|
||||||
|
atext = Changeset.applyToAText(cs, atext, apool);
|
||||||
}
|
}
|
||||||
|
|
||||||
async.series([
|
callback(null);
|
||||||
//get all needed data out of the database
|
}
|
||||||
function(callback)
|
], function(err)
|
||||||
{
|
{
|
||||||
async.parallel([
|
if(ERR(err, callback)) return;
|
||||||
//get the atext of the key revision
|
callback(null, atext);
|
||||||
function (callback)
|
});
|
||||||
{
|
};
|
||||||
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
atext = Changeset.cloneAText(_atext);
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//get all needed changesets
|
|
||||||
function (callback)
|
|
||||||
{
|
|
||||||
async.forEach(neededChangesets, function(item, callback)
|
|
||||||
{
|
|
||||||
_this.getRevisionChangeset(item, function(err, changeset)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
changesets[item] = changeset;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}, callback);
|
|
||||||
}
|
|
||||||
], callback);
|
|
||||||
},
|
|
||||||
//apply all changesets to the key changeset
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
var apool = _this.apool();
|
|
||||||
var curRev = keyRev;
|
|
||||||
|
|
||||||
while (curRev < targetRev)
|
Pad.prototype.getKeyRevisionNumber = function getKeyRevisionNumber(revNum) {
|
||||||
{
|
return Math.floor(revNum / 100) * 100;
|
||||||
curRev++;
|
};
|
||||||
var cs = changesets[curRev];
|
|
||||||
atext = Changeset.applyToAText(cs, atext, apool);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null);
|
Pad.prototype.text = function text() {
|
||||||
}
|
return this.atext.text;
|
||||||
], function(err)
|
};
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
callback(null, atext);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getKeyRevisionNumber : function(revNum)
|
Pad.prototype.setText = function setText(newText) {
|
||||||
{
|
//clean the new text
|
||||||
return Math.floor(revNum / 100) * 100;
|
newText = exports.cleanText(newText);
|
||||||
},
|
|
||||||
|
|
||||||
text : function()
|
var oldText = this.text();
|
||||||
{
|
|
||||||
return this.atext.text;
|
|
||||||
},
|
|
||||||
|
|
||||||
setText : function(newText)
|
//create the changeset
|
||||||
{
|
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
||||||
//clean the new text
|
|
||||||
newText = exports.cleanText(newText);
|
|
||||||
|
|
||||||
var oldText = this.text();
|
//append the changeset
|
||||||
|
this.appendRevision(changeset);
|
||||||
|
};
|
||||||
|
|
||||||
//create the changeset
|
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||||
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
|
||||||
|
|
||||||
//append the changeset
|
|
||||||
this.appendRevision(changeset);
|
|
||||||
},
|
|
||||||
|
|
||||||
appendChatMessage: function(text, userId, time)
|
|
||||||
{
|
|
||||||
this.chatHead++;
|
this.chatHead++;
|
||||||
//save the chat entry in the database
|
//save the chat entry in the database
|
||||||
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
||||||
//save the new chat head
|
//save the new chat head
|
||||||
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
||||||
},
|
};
|
||||||
|
|
||||||
getChatMessage: function(entryNum, callback)
|
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
||||||
|
var _this = this;
|
||||||
|
var entry;
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//get the chat entry
|
||||||
|
function(callback)
|
||||||
{
|
{
|
||||||
var _this = this;
|
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
|
||||||
var entry;
|
|
||||||
|
|
||||||
async.series([
|
|
||||||
//get the chat entry
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
entry = _entry;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//add the authorName
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
//this chat message doesn't exist, return null
|
|
||||||
if(entry == null)
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get the authorName
|
|
||||||
authorManager.getAuthorName(entry.userId, function(err, authorName)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
entry.userName = authorName;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], function(err)
|
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, entry);
|
entry = _entry;
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
//add the authorName
|
||||||
getLastChatMessages: function(count, callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//return an empty array if there are no chat messages
|
//this chat message doesn't exist, return null
|
||||||
if(this.chatHead == -1)
|
if(entry == null)
|
||||||
{
|
{
|
||||||
callback(null, []);
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _this = this;
|
//get the authorName
|
||||||
|
authorManager.getAuthorName(entry.userId, function(err, authorName)
|
||||||
//works only if we decrement the amount, for some reason
|
|
||||||
count--;
|
|
||||||
|
|
||||||
//set the startpoint
|
|
||||||
var start = this.chatHead-count;
|
|
||||||
if(start < 0)
|
|
||||||
start = 0;
|
|
||||||
|
|
||||||
//set the endpoint
|
|
||||||
var end = this.chatHead;
|
|
||||||
|
|
||||||
//collect the numbers of chat entries and in which order we need them
|
|
||||||
var neededEntries = [];
|
|
||||||
var order = 0;
|
|
||||||
for(var i=start;i<=end; i++)
|
|
||||||
{
|
|
||||||
neededEntries.push({entryNum:i, order: order});
|
|
||||||
order++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get all entries out of the database
|
|
||||||
var entries = [];
|
|
||||||
async.forEach(neededEntries, function(entryObject, callback)
|
|
||||||
{
|
|
||||||
_this.getChatMessage(entryObject.entryNum, function(err, entry)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
entries[entryObject.order] = entry;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}, function(err)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
//sort out broken chat entries
|
|
||||||
//it looks like in happend in the past that the chat head was
|
|
||||||
//incremented, but the chat message wasn't added
|
|
||||||
var cleanedEntries = [];
|
|
||||||
for(var i=0;i<entries.length;i++)
|
|
||||||
{
|
|
||||||
if(entries[i]!=null)
|
|
||||||
cleanedEntries.push(entries[i]);
|
|
||||||
else
|
|
||||||
console.warn("WARNING: Found broken chat entry in pad " + _this.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null, cleanedEntries);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
init : function (text, callback)
|
|
||||||
{
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
//replace text with default text if text isn't set
|
|
||||||
if(text == null)
|
|
||||||
{
|
|
||||||
text = settings.defaultPadText;
|
|
||||||
}
|
|
||||||
|
|
||||||
//try to load the pad
|
|
||||||
db.get("pad:"+this.id, function(err, value)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) 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);
|
|
||||||
|
|
||||||
//ensure we have a local chatHead variable
|
|
||||||
if(value.chatHead != null)
|
|
||||||
_this.chatHead = value.chatHead;
|
|
||||||
else
|
|
||||||
_this.chatHead = -1;
|
|
||||||
|
|
||||||
//ensure we have a local publicStatus variable
|
|
||||||
if(value.publicStatus != null)
|
|
||||||
_this.publicStatus = value.publicStatus;
|
|
||||||
else
|
|
||||||
_this.publicStatus = false;
|
|
||||||
|
|
||||||
//ensure we have a local passwordHash variable
|
|
||||||
if(value.passwordHash != null)
|
|
||||||
_this.passwordHash = value.passwordHash;
|
|
||||||
else
|
|
||||||
_this.passwordHash = null;
|
|
||||||
}
|
|
||||||
//this pad doesn't exist, so create it
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text));
|
|
||||||
|
|
||||||
_this.appendRevision(firstChangeset, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
remove: function(callback)
|
|
||||||
{
|
|
||||||
var padID = this.id;
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
//kick everyone from this pad
|
|
||||||
padMessageHandler.kickSessionsFromPad(padID);
|
|
||||||
|
|
||||||
async.series([
|
|
||||||
//delete all relations
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
async.parallel([
|
|
||||||
//is it a group pad? -> delete the entry of this pad in the group
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
//is it a group pad?
|
|
||||||
if(padID.indexOf("$")!=-1)
|
|
||||||
{
|
|
||||||
var groupID = padID.substring(0,padID.indexOf("$"));
|
|
||||||
|
|
||||||
db.get("group:" + groupID, function (err, group)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
//remove the pad entry
|
|
||||||
delete group.pads[padID];
|
|
||||||
|
|
||||||
//set the new value
|
|
||||||
db.set("group:" + groupID, group);
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//its no group pad, nothing to do here
|
|
||||||
else
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
//remove the readonly entries
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
db.remove("pad2readonly:" + padID);
|
|
||||||
db.remove("readonly2pad:" + readonlyID);
|
|
||||||
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
//delete all chat messages
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
var chatHead = _this.chatHead;
|
|
||||||
|
|
||||||
for(var i=0;i<=chatHead;i++)
|
|
||||||
{
|
|
||||||
db.remove("pad:"+padID+":chat:"+i);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
},
|
|
||||||
//delete all revisions
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
var revHead = _this.head;
|
|
||||||
|
|
||||||
for(var i=0;i<=revHead;i++)
|
|
||||||
{
|
|
||||||
db.remove("pad:"+padID+":revs:"+i);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
], callback);
|
|
||||||
},
|
|
||||||
//delete the pad entry and delete pad from padManager
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
db.remove("pad:"+padID);
|
|
||||||
padManager.unloadPad(padID);
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
], function(err)
|
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
entry.userName = authorName;
|
||||||
callback();
|
callback();
|
||||||
})
|
});
|
||||||
},
|
|
||||||
//set in db
|
|
||||||
setPublicStatus: function(publicStatus)
|
|
||||||
{
|
|
||||||
this.publicStatus = publicStatus;
|
|
||||||
db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
|
|
||||||
},
|
|
||||||
setPassword: function(password)
|
|
||||||
{
|
|
||||||
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
|
||||||
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
|
||||||
},
|
|
||||||
isCorrectPassword: function(password)
|
|
||||||
{
|
|
||||||
return compare(this.passwordHash, password)
|
|
||||||
},
|
|
||||||
isPasswordProtected: function()
|
|
||||||
{
|
|
||||||
return this.passwordHash != null;
|
|
||||||
}
|
}
|
||||||
}, // methods
|
], function(err)
|
||||||
});
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
callback(null, entry);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getLastChatMessages = function getLastChatMessages(count, callback) {
|
||||||
|
//return an empty array if there are no chat messages
|
||||||
|
if(this.chatHead == -1)
|
||||||
|
{
|
||||||
|
callback(null, []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
//works only if we decrement the amount, for some reason
|
||||||
|
count--;
|
||||||
|
|
||||||
|
//set the startpoint
|
||||||
|
var start = this.chatHead-count;
|
||||||
|
if(start < 0)
|
||||||
|
start = 0;
|
||||||
|
|
||||||
|
//set the endpoint
|
||||||
|
var end = this.chatHead;
|
||||||
|
|
||||||
|
//collect the numbers of chat entries and in which order we need them
|
||||||
|
var neededEntries = [];
|
||||||
|
var order = 0;
|
||||||
|
for(var i=start;i<=end; i++)
|
||||||
|
{
|
||||||
|
neededEntries.push({entryNum:i, order: order});
|
||||||
|
order++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//get all entries out of the database
|
||||||
|
var entries = [];
|
||||||
|
async.forEach(neededEntries, function(entryObject, callback)
|
||||||
|
{
|
||||||
|
_this.getChatMessage(entryObject.entryNum, function(err, entry)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
entries[entryObject.order] = entry;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}, function(err)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//sort out broken chat entries
|
||||||
|
//it looks like in happend in the past that the chat head was
|
||||||
|
//incremented, but the chat message wasn't added
|
||||||
|
var cleanedEntries = [];
|
||||||
|
for(var i=0;i<entries.length;i++)
|
||||||
|
{
|
||||||
|
if(entries[i]!=null)
|
||||||
|
cleanedEntries.push(entries[i]);
|
||||||
|
else
|
||||||
|
console.warn("WARNING: Found broken chat entry in pad " + _this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, cleanedEntries);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.init = function init(text, callback) {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
//replace text with default text if text isn't set
|
||||||
|
if(text == null)
|
||||||
|
{
|
||||||
|
text = settings.defaultPadText;
|
||||||
|
}
|
||||||
|
|
||||||
|
//try to load the pad
|
||||||
|
db.get("pad:"+this.id, function(err, value)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) 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);
|
||||||
|
|
||||||
|
//ensure we have a local chatHead variable
|
||||||
|
if(value.chatHead != null)
|
||||||
|
_this.chatHead = value.chatHead;
|
||||||
|
else
|
||||||
|
_this.chatHead = -1;
|
||||||
|
|
||||||
|
//ensure we have a local publicStatus variable
|
||||||
|
if(value.publicStatus != null)
|
||||||
|
_this.publicStatus = value.publicStatus;
|
||||||
|
else
|
||||||
|
_this.publicStatus = false;
|
||||||
|
|
||||||
|
//ensure we have a local passwordHash variable
|
||||||
|
if(value.passwordHash != null)
|
||||||
|
_this.passwordHash = value.passwordHash;
|
||||||
|
else
|
||||||
|
_this.passwordHash = null;
|
||||||
|
}
|
||||||
|
//this pad doesn't exist, so create it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text));
|
||||||
|
|
||||||
|
_this.appendRevision(firstChangeset, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.remove = function remove(callback) {
|
||||||
|
var padID = this.id;
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
//kick everyone from this pad
|
||||||
|
padMessageHandler.kickSessionsFromPad(padID);
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//delete all relations
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
async.parallel([
|
||||||
|
//is it a group pad? -> delete the entry of this pad in the group
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
//is it a group pad?
|
||||||
|
if(padID.indexOf("$")!=-1)
|
||||||
|
{
|
||||||
|
var groupID = padID.substring(0,padID.indexOf("$"));
|
||||||
|
|
||||||
|
db.get("group:" + groupID, function (err, group)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//remove the pad entry
|
||||||
|
delete group.pads[padID];
|
||||||
|
|
||||||
|
//set the new value
|
||||||
|
db.set("group:" + groupID, group);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//its no group pad, nothing to do here
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
//remove the readonly entries
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
db.remove("pad2readonly:" + padID);
|
||||||
|
db.remove("readonly2pad:" + readonlyID);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//delete all chat messages
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
var chatHead = _this.chatHead;
|
||||||
|
|
||||||
|
for(var i=0;i<=chatHead;i++)
|
||||||
|
{
|
||||||
|
db.remove("pad:"+padID+":chat:"+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
},
|
||||||
|
//delete all revisions
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
var revHead = _this.head;
|
||||||
|
|
||||||
|
for(var i=0;i<=revHead;i++)
|
||||||
|
{
|
||||||
|
db.remove("pad:"+padID+":revs:"+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
], callback);
|
||||||
|
},
|
||||||
|
//delete the pad entry and delete pad from padManager
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
db.remove("pad:"+padID);
|
||||||
|
padManager.unloadPad(padID);
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
], function(err)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
//set in db
|
||||||
|
Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
|
||||||
|
this.publicStatus = publicStatus;
|
||||||
|
db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.setPassword = function setPassword(password) {
|
||||||
|
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
||||||
|
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
|
||||||
|
return compare(this.passwordHash, password);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.isPasswordProtected = function isPasswordProtected() {
|
||||||
|
return this.passwordHash != null;
|
||||||
|
};
|
||||||
|
|
||||||
/* Crypto helper methods */
|
/* Crypto helper methods */
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
require("../db/Pad");
|
var Pad = require("../db/Pad").Pad;
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -326,7 +326,6 @@ function compressJS(values)
|
||||||
var complete = values.join("\n");
|
var complete = values.join("\n");
|
||||||
var ast = jsp.parse(complete); // parse code and get the initial AST
|
var ast = jsp.parse(complete); // parse code and get the initial AST
|
||||||
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
ast = pro.ast_mangle(ast); // get a new AST with mangled names
|
||||||
ast = pro.ast_squeeze(ast); // get an AST with compression optimizations
|
|
||||||
return pro.gen_code(ast); // compressed code here
|
return pro.gen_code(ast); // compressed code here
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
"socket.io" : "0.8.7",
|
"socket.io" : "0.8.7",
|
||||||
"ueberDB" : "0.1.3",
|
"ueberDB" : "0.1.3",
|
||||||
"async" : "0.1.15",
|
"async" : "0.1.15",
|
||||||
"joose" : "3.50.0",
|
|
||||||
"express" : "2.5.0",
|
"express" : "2.5.0",
|
||||||
"clean-css" : "0.2.4",
|
"clean-css" : "0.2.4",
|
||||||
"uglify-js" : "1.1.1",
|
"uglify-js" : "1.1.1",
|
||||||
|
|
Loading…
Reference in a new issue