mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-04-23 00:46:16 -04:00
Merge branch 'master' of git://github.com/Pita/etherpad-lite into newDesignTest
This commit is contained in:
commit
143369b387
7 changed files with 299 additions and 287 deletions
18
README.md
18
README.md
|
@ -29,8 +29,8 @@ Visit <http://pitapoison.de:9001> to test it live. You can find the same instanc
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
1. Download latest node.js version from <http://nodejs.org/> and build it with this instructions <https://github.com/joyent/node/wiki/Installation>. <br>
|
1. Download the latest 0.4.x node.js release from <http://nodejs.org/#download> and build it with this instructions <https://github.com/joyent/node/wiki/Installation>. <br>
|
||||||
The Node.js version of your Linux repository might be too old/new. Please compile from the source to get sure you have the correct version. We support node.js version 0.4.*
|
The Node.js version of your Linux repository might be too old/new. Please compile from the source to get sure you have the correct version.
|
||||||
2. Install npm `curl http://npmjs.org/install.sh | sh`
|
2. Install npm `curl http://npmjs.org/install.sh | sh`
|
||||||
3. Ensure you have installed the sqlite develob libraries, gzip and git `apt-get install libsqlite3-dev gzip git-core`
|
3. Ensure you have installed the sqlite develob libraries, gzip and git `apt-get install libsqlite3-dev gzip git-core`
|
||||||
4. Clone the git repository `git clone 'git://github.com/Pita/etherpad-lite.git'`
|
4. Clone the git repository `git clone 'git://github.com/Pita/etherpad-lite.git'`
|
||||||
|
@ -40,11 +40,11 @@ The Node.js version of your Linux repository might be too old/new. Please compil
|
||||||
# Next Steps
|
# Next Steps
|
||||||
You can modify the settings in the file settings.json
|
You can modify the settings in the file settings.json
|
||||||
|
|
||||||
You can update to the latest version with `git pull origin && npm install`
|
You can update to the latest version with `git pull origin`. The next start with bin/run.sh will update the dependencies
|
||||||
|
|
||||||
You can debug with `bin/runDebug.sh`
|
You can debug with `bin/debugRun.sh`
|
||||||
|
|
||||||
Look at the Wiki: [How to put Etherpad Lite behind a reverse Proxy](https://github.com/Pita/etherpad-lite/wiki/How-to-put-Etherpad-Lite-behind-a-reverse-Proxy), [How to deploy Etherpad Lite as a service](https://github.com/Pita/etherpad-lite/wiki/How-to-deploy-Etherpad-Lite-as-a-service). Feel free to improve these wiki pages
|
You can find more information in the [wiki](https://github.com/Pita/etherpad-lite/wiki). Feel free to improve these wiki pages
|
||||||
|
|
||||||
# Develop
|
# Develop
|
||||||
If you're new to git and github, start here <http://learn.github.com/p/intro.html>.
|
If you're new to git and github, start here <http://learn.github.com/p/intro.html>.
|
||||||
|
@ -58,5 +58,13 @@ You can join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev)
|
||||||
|
|
||||||
You also help the project, if you only host a ep-lite instance and share your experience with us.
|
You also help the project, if you only host a ep-lite instance and share your experience with us.
|
||||||
|
|
||||||
|
Look at our [FAQ Page](https://github.com/Pita/etherpad-lite/wiki/FAQ)
|
||||||
|
|
||||||
|
# Modules created for this project
|
||||||
|
|
||||||
|
* [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access
|
||||||
|
* [doc.md](https://github.com/Pita/doc.md) "A simple JSDoc documenation tool that creates markdown for node.js modules exports" - is used to generate the docs
|
||||||
|
* [channels](https://github.com/Pita/channels) "Event channels in node.js" - ensures that ueberDB operations are atomic and in series for each key
|
||||||
|
|
||||||
# License
|
# License
|
||||||
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)
|
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)
|
||||||
|
|
2
doc/easysync/README.md
Normal file
2
doc/easysync/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# About this folder
|
||||||
|
We put all documentations we found about the old Etherpad together in this folder. Most of this is still valid for Etherpad Lite
|
2
doc/jsdoc/README.md
Normal file
2
doc/jsdoc/README.md
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# About this folder
|
||||||
|
This is the documentation generated with doc.md. This documentation might be not up to date, you can generate the latest jsdocs with bin/generateJsDoc.sh
|
558
node/db/Pad.js
558
node/db/Pad.js
|
@ -19,150 +19,150 @@ exports.cleanText = function (txt) {
|
||||||
|
|
||||||
Class('Pad', {
|
Class('Pad', {
|
||||||
|
|
||||||
// these are the properties
|
// these are the properties
|
||||||
has : {
|
has : {
|
||||||
|
|
||||||
atext : {
|
atext : {
|
||||||
is : 'rw', // readwrite
|
is : 'rw', // readwrite
|
||||||
init : function() { return Changeset.makeAText("\n"); } // first value
|
init : function() { return Changeset.makeAText("\n"); } // first value
|
||||||
}, // atext
|
}, // atext
|
||||||
|
|
||||||
pool : {
|
pool : {
|
||||||
is: 'rw',
|
is: 'rw',
|
||||||
init : function() { return AttributePoolFactory.createAttributePool(); },
|
init : function() { return AttributePoolFactory.createAttributePool(); },
|
||||||
getterName : 'apool' // legacy
|
getterName : 'apool' // legacy
|
||||||
}, // pool
|
}, // pool
|
||||||
|
|
||||||
head : {
|
head : {
|
||||||
is : 'rw',
|
is : 'rw',
|
||||||
init : -1,
|
init : -1,
|
||||||
getterName : 'getHeadRevisionNumber'
|
getterName : 'getHeadRevisionNumber'
|
||||||
}, // head
|
}, // head
|
||||||
|
|
||||||
chatHead : {
|
chatHead : {
|
||||||
is: 'rw',
|
is: 'rw',
|
||||||
init: -1
|
init: -1
|
||||||
}, // chatHead
|
}, // chatHead
|
||||||
|
|
||||||
id : { is : 'r' }
|
id : { is : 'r' }
|
||||||
},
|
},
|
||||||
|
|
||||||
methods : {
|
methods : {
|
||||||
|
|
||||||
BUILD : function (id)
|
BUILD : function (id)
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
'id' : id,
|
'id' : id,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
appendRevision : function(aChangeset, author)
|
appendRevision : function(aChangeset, author)
|
||||||
{
|
{
|
||||||
if(!author)
|
if(!author)
|
||||||
author = '';
|
author = '';
|
||||||
|
|
||||||
var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
|
var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
|
||||||
Changeset.copyAText(newAText, this.atext);
|
Changeset.copyAText(newAText, this.atext);
|
||||||
|
|
||||||
var newRev = ++this.head;
|
var newRev = ++this.head;
|
||||||
|
|
||||||
var newRevData = {};
|
var newRevData = {};
|
||||||
newRevData.changeset = aChangeset;
|
newRevData.changeset = aChangeset;
|
||||||
newRevData.meta = {};
|
newRevData.meta = {};
|
||||||
newRevData.meta.author = author;
|
newRevData.meta.author = author;
|
||||||
newRevData.meta.timestamp = new Date().getTime();
|
newRevData.meta.timestamp = new Date().getTime();
|
||||||
|
|
||||||
//ex. getNumForAuthor
|
//ex. getNumForAuthor
|
||||||
if(author != '')
|
if(author != '')
|
||||||
this.pool.putAttrib(['author', author || '']);
|
this.pool.putAttrib(['author', author || '']);
|
||||||
|
|
||||||
if(newRev % 100 == 0)
|
if(newRev % 100 == 0)
|
||||||
{
|
{
|
||||||
newRevData.meta.atext = this.atext;
|
newRevData.meta.atext = this.atext;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
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});
|
db.set("pad:"+this.id, {atext: this.atext, pool: this.pool.toJsonable(), head: this.head, chatHead: this.chatHead});
|
||||||
}, //appendRevision
|
}, //appendRevision
|
||||||
|
|
||||||
getRevisionChangeset : function(revNum, callback)
|
getRevisionChangeset : function(revNum, callback)
|
||||||
{
|
{
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
||||||
}, // getRevisionChangeset
|
}, // getRevisionChangeset
|
||||||
|
|
||||||
getRevisionAuthor : function(revNum, callback)
|
getRevisionAuthor : function(revNum, callback)
|
||||||
{
|
{
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
|
||||||
}, // getRevisionAuthor
|
}, // getRevisionAuthor
|
||||||
|
|
||||||
getRevisionDate : function(revNum, callback)
|
getRevisionDate : function(revNum, callback)
|
||||||
{
|
{
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
|
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
|
||||||
}, // getRevisionAuthor
|
}, // getRevisionAuthor
|
||||||
|
|
||||||
getAllAuthors : function()
|
getAllAuthors : function()
|
||||||
{
|
{
|
||||||
var authors = [];
|
var authors = [];
|
||||||
|
|
||||||
for(key in this.pool.numToAttrib)
|
for(key in this.pool.numToAttrib)
|
||||||
{
|
{
|
||||||
if(this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "")
|
if(this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "")
|
||||||
{
|
{
|
||||||
authors.push(this.pool.numToAttrib[key][1]);
|
authors.push(this.pool.numToAttrib[key][1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return authors;
|
return authors;
|
||||||
},
|
},
|
||||||
|
|
||||||
getInternalRevisionAText : function(targetRev, callback)
|
getInternalRevisionAText : function(targetRev, callback)
|
||||||
{
|
{
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
var keyRev = this.getKeyRevisionNumber(targetRev);
|
var keyRev = this.getKeyRevisionNumber(targetRev);
|
||||||
var atext;
|
var atext;
|
||||||
var changesets = [];
|
var changesets = [];
|
||||||
|
|
||||||
//find out which changesets are needed
|
//find out which changesets are needed
|
||||||
var neededChangesets = [];
|
var neededChangesets = [];
|
||||||
var curRev = keyRev;
|
var curRev = keyRev;
|
||||||
while (curRev < targetRev)
|
while (curRev < targetRev)
|
||||||
{
|
{
|
||||||
curRev++;
|
curRev++;
|
||||||
neededChangesets.push(curRev);
|
neededChangesets.push(curRev);
|
||||||
}
|
}
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get all needed data out of the database
|
//get all needed data out of the database
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
async.parallel([
|
async.parallel([
|
||||||
//get the atext of the key revision
|
//get the atext of the key revision
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
|
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
|
||||||
{
|
{
|
||||||
atext = Changeset.cloneAText(_atext);
|
atext = Changeset.cloneAText(_atext);
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//get all needed changesets
|
//get all needed changesets
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
async.forEach(neededChangesets, function(item, callback)
|
async.forEach(neededChangesets, function(item, callback)
|
||||||
{
|
{
|
||||||
_this.getRevisionChangeset(item, function(err, changeset)
|
_this.getRevisionChangeset(item, function(err, changeset)
|
||||||
{
|
{
|
||||||
changesets[item] = changeset;
|
changesets[item] = changeset;
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
}, callback);
|
}, callback);
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
},
|
},
|
||||||
//apply all changesets to the key changeset
|
//apply all changesets to the key changeset
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var apool = _this.apool();
|
var apool = _this.apool();
|
||||||
var curRev = keyRev;
|
var curRev = keyRev;
|
||||||
|
|
||||||
|
@ -174,118 +174,118 @@ Class('Pad', {
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
callback(err, atext);
|
callback(err, atext);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getKeyRevisionNumber : function(revNum)
|
getKeyRevisionNumber : function(revNum)
|
||||||
{
|
{
|
||||||
return Math.floor(revNum / 100) * 100;
|
return Math.floor(revNum / 100) * 100;
|
||||||
},
|
},
|
||||||
|
|
||||||
text : function()
|
text : function()
|
||||||
{
|
{
|
||||||
return this.atext.text;
|
return this.atext.text;
|
||||||
},
|
},
|
||||||
|
|
||||||
setText : function(newText)
|
setText : function(newText)
|
||||||
{
|
{
|
||||||
//clean the new text
|
//clean the new text
|
||||||
newText = exports.cleanText(newText);
|
newText = exports.cleanText(newText);
|
||||||
|
|
||||||
var oldText = this.text();
|
var oldText = this.text();
|
||||||
|
|
||||||
//create the changeset
|
//create the changeset
|
||||||
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
||||||
|
|
||||||
//append the changeset
|
//append the changeset
|
||||||
this.appendRevision(changeset);
|
this.appendRevision(changeset);
|
||||||
},
|
},
|
||||||
|
|
||||||
appendChatMessage: function(text, userId, time)
|
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)
|
getChatMessage: function(entryNum, callback)
|
||||||
{
|
{
|
||||||
var _this = this;
|
var _this = this;
|
||||||
var entry;
|
var entry;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the chat entry
|
//get the chat entry
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
|
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
|
||||||
{
|
{
|
||||||
entry = _entry;
|
entry = _entry;
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//add the authorName
|
//add the authorName
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//this chat message doesn't exist, return null
|
//this chat message doesn't exist, return null
|
||||||
if(entry == null)
|
if(entry == null)
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the authorName
|
//get the authorName
|
||||||
authorManager.getAuthorName(entry.userId, function(err, authorName)
|
authorManager.getAuthorName(entry.userId, function(err, authorName)
|
||||||
{
|
{
|
||||||
entry.userName = authorName;
|
entry.userName = authorName;
|
||||||
callback(err);
|
callback(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
callback(err, entry);
|
callback(err, entry);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
getLastChatMessages: function(count, callback)
|
getLastChatMessages: function(count, callback)
|
||||||
{
|
{
|
||||||
//return an empty array if there are no chat messages
|
//return an empty array if there are no chat messages
|
||||||
if(this.chatHead == -1)
|
if(this.chatHead == -1)
|
||||||
{
|
{
|
||||||
callback(null, []);
|
callback(null, []);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
//works only if we decrement the amount, for some reason
|
//works only if we decrement the amount, for some reason
|
||||||
count--;
|
count--;
|
||||||
|
|
||||||
//set the startpoint
|
//set the startpoint
|
||||||
var start = this.chatHead-count;
|
var start = this.chatHead-count;
|
||||||
if(start < 0)
|
if(start < 0)
|
||||||
start = 0;
|
start = 0;
|
||||||
|
|
||||||
//set the endpoint
|
//set the endpoint
|
||||||
var end = this.chatHead;
|
var end = this.chatHead;
|
||||||
|
|
||||||
//collect the numbers of chat entries and in which order we need them
|
//collect the numbers of chat entries and in which order we need them
|
||||||
var neededEntries = [];
|
var neededEntries = [];
|
||||||
var order = 0;
|
var order = 0;
|
||||||
for(var i=start;i<=end; i++)
|
for(var i=start;i<=end; i++)
|
||||||
{
|
{
|
||||||
neededEntries.push({entryNum:i, order: order});
|
neededEntries.push({entryNum:i, order: order});
|
||||||
order++;
|
order++;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all entries out of the database
|
//get all entries out of the database
|
||||||
var entries = [];
|
var entries = [];
|
||||||
async.forEach(neededEntries, function(entryObject, callback)
|
async.forEach(neededEntries, function(entryObject, callback)
|
||||||
{
|
{
|
||||||
_this.getChatMessage(entryObject.entryNum, function(err, entry)
|
_this.getChatMessage(entryObject.entryNum, function(err, entry)
|
||||||
{
|
{
|
||||||
|
@ -308,44 +308,44 @@ Class('Pad', {
|
||||||
|
|
||||||
callback(err, cleanedEntries);
|
callback(err, cleanedEntries);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
init : function (callback)
|
init : function (callback)
|
||||||
{
|
{
|
||||||
var _this = this;
|
var _this = this;
|
||||||
|
|
||||||
//try to load the pad
|
//try to load the pad
|
||||||
db.get("pad:"+this.id, function(err, value)
|
db.get("pad:"+this.id, function(err, value)
|
||||||
{
|
{
|
||||||
if(err)
|
if(err)
|
||||||
{
|
{
|
||||||
callback(err, null);
|
callback(err, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if this pad exists, load it
|
//if this pad exists, load it
|
||||||
if(value != null)
|
if(value != null)
|
||||||
{
|
{
|
||||||
_this.head = value.head;
|
_this.head = value.head;
|
||||||
_this.atext = value.atext;
|
_this.atext = value.atext;
|
||||||
_this.pool = _this.pool.fromJsonable(value.pool);
|
_this.pool = _this.pool.fromJsonable(value.pool);
|
||||||
|
|
||||||
if(value.chatHead != null)
|
if(value.chatHead != null)
|
||||||
_this.chatHead = value.chatHead;
|
_this.chatHead = value.chatHead;
|
||||||
else
|
else
|
||||||
_this.chatHead = -1;
|
_this.chatHead = -1;
|
||||||
}
|
}
|
||||||
//this pad doesn't exist, so create it
|
//this pad doesn't exist, so create it
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(settings.defaultPadText));
|
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(settings.defaultPadText));
|
||||||
|
|
||||||
_this.appendRevision(firstChangeset, '');
|
_this.appendRevision(firstChangeset, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}, // methods
|
}, // methods
|
||||||
});
|
});
|
||||||
|
|
|
@ -125,6 +125,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
//start the costum js
|
//start the costum js
|
||||||
if(costumStart) costumStart();
|
if(typeof costumStart == "function") costumStart();
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -30,7 +30,7 @@ $(document).ready(function()
|
||||||
}
|
}
|
||||||
|
|
||||||
//start the costum js
|
//start the costum js
|
||||||
if(costumStart) costumStart();
|
if(typeof costumStart == "function") costumStart();
|
||||||
|
|
||||||
handshake();
|
handshake();
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
$(document).ready(function ()
|
$(document).ready(function ()
|
||||||
{
|
{
|
||||||
//start the costum js
|
//start the costum js
|
||||||
if(costumStart) costumStart();
|
if(typeof costumStart == "function") costumStart();
|
||||||
|
|
||||||
//get the padId out of the url
|
//get the padId out of the url
|
||||||
var urlParts= document.location.pathname.split("/");
|
var urlParts= document.location.pathname.split("/");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue