mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-08 08:01:02 -04:00
Merge f05fc7ba31
into 7232dff6db
This commit is contained in:
commit
f60a9cce17
27 changed files with 1353 additions and 1344 deletions
|
@ -111,6 +111,9 @@ You can join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev)
|
||||||
|
|
||||||
You also help the project, if you only host a Etherpad Lite instance and share your experience with us.
|
You also help the project, if you only host a Etherpad Lite instance and share your experience with us.
|
||||||
|
|
||||||
|
Please consider using [jshint](http://www.jshint.com/about/) if you plan to
|
||||||
|
contribute to Etherpad Lite.
|
||||||
|
|
||||||
# Modules created for this project
|
# Modules created for this project
|
||||||
|
|
||||||
* [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access
|
* [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access
|
||||||
|
|
9
bin/jshint.sh
Executable file
9
bin/jshint.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ -d "../bin" ]; then
|
||||||
|
cd "../"
|
||||||
|
fi
|
||||||
|
|
||||||
|
JSHINT=./node_modules/jshint/bin/hint
|
||||||
|
|
||||||
|
$JSHINT ./node/
|
134
node/db/API.js
134
node/db/API.js
|
@ -29,7 +29,7 @@ var sessionManager = require("./SessionManager");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var exportHtml = require("../utils/ExportHtml");
|
var exportHtml = require("../utils/ExportHtml");
|
||||||
var importHtml = require("../utils/ImportHtml");
|
var importHtml = require("../utils/ImportHtml");
|
||||||
var cleanText = require("./Pad").cleanText;
|
var cleanText = require("../utils/cleantext").cleanText;
|
||||||
|
|
||||||
/**********************/
|
/**********************/
|
||||||
/**GROUP FUNCTIONS*****/
|
/**GROUP FUNCTIONS*****/
|
||||||
|
@ -63,7 +63,7 @@ exports.listSessionsOfAuthor = sessionManager.listSessionsOfAuthor;
|
||||||
/************************/
|
/************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
getText(padID, [rev]) returns the text of a pad
|
getText(padID, [rev]) returns the text of a pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -78,14 +78,14 @@ exports.getText = function(padID, rev, callback)
|
||||||
callback = rev;
|
callback = rev;
|
||||||
rev = undefined;
|
rev = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if rev is a number
|
//check if rev is a number
|
||||||
if(rev !== undefined && typeof rev != "number")
|
if(rev !== undefined && typeof rev != "number")
|
||||||
{
|
{
|
||||||
//try to parse the number
|
//try to parse the number
|
||||||
if(!isNaN(parseInt(rev)))
|
if(!isNaN(parseInt(rev, 10)))
|
||||||
{
|
{
|
||||||
rev = parseInt(rev);
|
rev = parseInt(rev, 10);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -93,26 +93,26 @@ exports.getText = function(padID, rev, callback)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ensure this is not a negativ number
|
//ensure this is not a negativ number
|
||||||
if(rev !== undefined && rev < 0)
|
if(rev !== undefined && rev < 0)
|
||||||
{
|
{
|
||||||
callback(new customError("rev is a negativ number","apierror"));
|
callback(new customError("rev is a negativ number","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ensure this is not a float value
|
//ensure this is not a float value
|
||||||
if(rev !== undefined && !is_int(rev))
|
if(rev !== undefined && !is_int(rev))
|
||||||
{
|
{
|
||||||
callback(new customError("rev is a float value","apierror"));
|
callback(new customError("rev is a float value","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the pad
|
//get the pad
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//the client asked for a special revision
|
//the client asked for a special revision
|
||||||
if(rev !== undefined)
|
if(rev !== undefined)
|
||||||
{
|
{
|
||||||
|
@ -122,16 +122,16 @@ exports.getText = function(padID, rev, callback)
|
||||||
callback(new customError("rev is higher than the head revision of the pad","apierror"));
|
callback(new customError("rev is higher than the head revision of the pad","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the text of this revision
|
//get the text of this revision
|
||||||
pad.getInternalRevisionAText(rev, function(err, atext)
|
pad.getInternalRevisionAText(rev, function(err, atext)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
data = {text: atext.text};
|
data = {text: atext.text};
|
||||||
|
|
||||||
callback(null, data);
|
callback(null, data);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
//the client wants the latest text, lets return it to him
|
//the client wants the latest text, lets return it to him
|
||||||
else
|
else
|
||||||
|
@ -139,10 +139,10 @@ exports.getText = function(padID, rev, callback)
|
||||||
callback(null, {"text": pad.text()});
|
callback(null, {"text": pad.text()});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
setText(padID, text) sets the text of a pad
|
setText(padID, text) sets the text of a pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -151,22 +151,22 @@ Example returns:
|
||||||
{code: 1, message:"text too long", data: null}
|
{code: 1, message:"text too long", data: null}
|
||||||
*/
|
*/
|
||||||
exports.setText = function(padID, text, callback)
|
exports.setText = function(padID, text, callback)
|
||||||
{
|
{
|
||||||
//get the pad
|
//get the pad
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//set the text
|
//set the text
|
||||||
pad.setText(text);
|
pad.setText(text);
|
||||||
|
|
||||||
//update the clients on the pad
|
//update the clients on the pad
|
||||||
padMessageHandler.updatePadClients(pad, callback);
|
padMessageHandler.updatePadClients(pad, callback);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
getHTML(padID, [rev]) returns the html of a pad
|
getHTML(padID, [rev]) returns the html of a pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -178,14 +178,14 @@ exports.getHTML = function(padID, rev, callback)
|
||||||
if(typeof rev == "function")
|
if(typeof rev == "function")
|
||||||
{
|
{
|
||||||
callback = rev;
|
callback = rev;
|
||||||
rev = undefined;
|
rev = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rev !== undefined && typeof rev != "number")
|
if (rev !== undefined && typeof rev != "number")
|
||||||
{
|
{
|
||||||
if (!isNaN(parseInt(rev)))
|
if (!isNaN(parseInt(rev, 10)))
|
||||||
{
|
{
|
||||||
rev = parseInt(rev);
|
rev = parseInt(rev, 10);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -209,7 +209,7 @@ exports.getHTML = function(padID, rev, callback)
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//the client asked for a special revision
|
//the client asked for a special revision
|
||||||
if(rev !== undefined)
|
if(rev !== undefined)
|
||||||
{
|
{
|
||||||
|
@ -219,8 +219,8 @@ exports.getHTML = function(padID, rev, callback)
|
||||||
callback(new customError("rev is higher than the head revision of the pad","apierror"));
|
callback(new customError("rev is higher than the head revision of the pad","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the html of this revision
|
//get the html of this revision
|
||||||
exportHtml.getPadHTML(pad, rev, function(err, html)
|
exportHtml.getPadHTML(pad, rev, function(err, html)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
@ -234,14 +234,14 @@ exports.getHTML = function(padID, rev, callback)
|
||||||
exportHtml.getPadHTML(pad, undefined, function (err, html)
|
exportHtml.getPadHTML(pad, undefined, function (err, html)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
data = {html: html};
|
data = {html: html};
|
||||||
|
|
||||||
callback(null, data);
|
callback(null, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.setHTML = function(padID, html, callback)
|
exports.setHTML = function(padID, html, callback)
|
||||||
{
|
{
|
||||||
|
@ -257,14 +257,14 @@ exports.setHTML = function(padID, html, callback)
|
||||||
padMessageHandler.updatePadClients(pad, callback);
|
padMessageHandler.updatePadClients(pad, callback);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/*****************/
|
/*****************/
|
||||||
/**PAD FUNCTIONS */
|
/**PAD FUNCTIONS */
|
||||||
/*****************/
|
/*****************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
getRevisionsCount(padID) returns the number of revisions of this pad
|
getRevisionsCount(padID) returns the number of revisions of this pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -277,13 +277,13 @@ exports.getRevisionsCount = function(padID, callback)
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
callback(null, {revisions: pad.getHeadRevisionNumber()});
|
callback(null, {revisions: pad.getHeadRevisionNumber()});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
createPad(padName [, text]) creates a new pad in this group
|
createPad(padName [, text]) creates a new pad in this group
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -291,24 +291,24 @@ Example returns:
|
||||||
{code: 1, message:"pad does already exist", data: null}
|
{code: 1, message:"pad does already exist", data: null}
|
||||||
*/
|
*/
|
||||||
exports.createPad = function(padID, text, callback)
|
exports.createPad = function(padID, text, callback)
|
||||||
{
|
{
|
||||||
//ensure there is no $ in the padID
|
//ensure there is no $ in the padID
|
||||||
if(padID && padID.indexOf("$") != -1)
|
if(padID && padID.indexOf("$") != -1)
|
||||||
{
|
{
|
||||||
callback(new customError("createPad can't create group pads","apierror"));
|
callback(new customError("createPad can't create group pads","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//create pad
|
//create pad
|
||||||
getPadSafe(padID, false, text, function(err)
|
getPadSafe(padID, false, text, function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
deletePad(padID) deletes a pad
|
deletePad(padID) deletes a pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -320,13 +320,13 @@ exports.deletePad = function(padID, callback)
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
pad.remove(callback);
|
pad.remove(callback);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
getReadOnlyLink(padID) returns the read only link of a pad
|
getReadOnlyLink(padID) returns the read only link of a pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -339,7 +339,7 @@ exports.getReadOnlyID = function(padID, callback)
|
||||||
getPadSafe(padID, true, function(err)
|
getPadSafe(padID, true, function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//get the readonlyId
|
//get the readonlyId
|
||||||
readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId)
|
readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId)
|
||||||
{
|
{
|
||||||
|
@ -347,10 +347,10 @@ exports.getReadOnlyID = function(padID, callback)
|
||||||
callback(null, {readOnlyID: readOnlyId});
|
callback(null, {readOnlyID: readOnlyId});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
setPublicStatus(padID, publicStatus) sets a boolean for the public status of a pad
|
setPublicStatus(padID, publicStatus) sets a boolean for the public status of a pad
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -370,20 +370,20 @@ exports.setPublicStatus = function(padID, publicStatus, callback)
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//convert string to boolean
|
//convert string to boolean
|
||||||
if(typeof publicStatus == "string")
|
if(typeof publicStatus == "string")
|
||||||
publicStatus = publicStatus == "true" ? true : false;
|
publicStatus = publicStatus == "true" ? true : false;
|
||||||
|
|
||||||
//set the password
|
//set the password
|
||||||
pad.setPublicStatus(publicStatus);
|
pad.setPublicStatus(publicStatus);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
getPublicStatus(padID) return true of false
|
getPublicStatus(padID) return true of false
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -398,18 +398,18 @@ exports.getPublicStatus = function(padID, callback)
|
||||||
callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
|
callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the pad
|
//get the pad
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
callback(null, {publicStatus: pad.getPublicStatus()});
|
callback(null, {publicStatus: pad.getPublicStatus()});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
setPassword(padID, password) returns ok or a error message
|
setPassword(padID, password) returns ok or a error message
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -424,21 +424,21 @@ exports.setPassword = function(padID, password, callback)
|
||||||
callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
|
callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the pad
|
//get the pad
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//set the password
|
//set the password
|
||||||
pad.setPassword(password);
|
pad.setPassword(password);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
isPasswordProtected(padID) returns true or false
|
isPasswordProtected(padID) returns true or false
|
||||||
|
|
||||||
Example returns:
|
Example returns:
|
||||||
|
|
||||||
|
@ -458,10 +458,10 @@ exports.isPasswordProtected = function(padID, callback)
|
||||||
getPadSafe(padID, true, function(err, pad)
|
getPadSafe(padID, true, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
callback(null, {isPasswordProtected: pad.isPasswordProtected()});
|
callback(null, {isPasswordProtected: pad.isPasswordProtected()});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/******************************/
|
/******************************/
|
||||||
/** INTERNAL HELPER FUNCTIONS */
|
/** INTERNAL HELPER FUNCTIONS */
|
||||||
|
@ -469,8 +469,8 @@ exports.isPasswordProtected = function(padID, callback)
|
||||||
|
|
||||||
//checks if a number is an int
|
//checks if a number is an int
|
||||||
function is_int(value)
|
function is_int(value)
|
||||||
{
|
{
|
||||||
return (parseFloat(value) == parseInt(value)) && !isNaN(value)
|
return (parseFloat(value) == parseInt(value, 10)) && !isNaN(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//gets a pad safe
|
//gets a pad safe
|
||||||
|
@ -488,26 +488,26 @@ function getPadSafe(padID, shouldExist, text, callback)
|
||||||
callback(new customError("padID is not a string","apierror"));
|
callback(new customError("padID is not a string","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if the padID maches the requirements
|
//check if the padID maches the requirements
|
||||||
if(!padManager.isValidPadId(padID))
|
if(!padManager.isValidPadId(padID))
|
||||||
{
|
{
|
||||||
callback(new customError("padID did not match requirements","apierror"));
|
callback(new customError("padID did not match requirements","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if the pad exists
|
//check if the pad exists
|
||||||
padManager.doesPadExists(padID, function(err, exists)
|
padManager.doesPadExists(padID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//does not exist, but should
|
//does not exist, but should
|
||||||
if(exists == false && shouldExist == true)
|
if(!exists && shouldExist)
|
||||||
{
|
{
|
||||||
callback(new customError("padID does not exist","apierror"));
|
callback(new customError("padID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
//does exists, but shouldn't
|
//does exists, but shouldn't
|
||||||
else if(exists == true && shouldExist == false)
|
else if(exists && !shouldExist)
|
||||||
{
|
{
|
||||||
callback(new customError("padID does already exist","apierror"));
|
callback(new customError("padID does already exist","apierror"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,14 +31,14 @@ exports.doesAuthorExists = function (authorID, callback)
|
||||||
db.get("globalAuthor:" + authorID, function (err, author)
|
db.get("globalAuthor:" + authorID, function (err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, author != null);
|
callback(null, author ? true : false);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AuthorID for a token.
|
* Returns the AuthorID for a token.
|
||||||
* @param {String} token The token
|
* @param {String} token The token
|
||||||
* @param {Function} callback callback (err, author)
|
* @param {Function} callback callback (err, author)
|
||||||
*/
|
*/
|
||||||
exports.getAuthor4Token = function (token, callback)
|
exports.getAuthor4Token = function (token, callback)
|
||||||
{
|
{
|
||||||
|
@ -48,52 +48,54 @@ exports.getAuthor4Token = function (token, callback)
|
||||||
//return only the sub value authorID
|
//return only the sub value authorID
|
||||||
callback(null, author ? author.authorID : author);
|
callback(null, author ? author.authorID : author);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AuthorID for a mapper.
|
* Returns the AuthorID for a mapper.
|
||||||
* @param {String} token The mapper
|
* @param {String} token The mapper
|
||||||
* @param {Function} callback callback (err, author)
|
* @param {Function} callback callback (err, author)
|
||||||
*/
|
*/
|
||||||
exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
|
exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
|
||||||
{
|
{
|
||||||
mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author)
|
mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//set the name of this author
|
//set the name of this author
|
||||||
if(name)
|
if(name)
|
||||||
|
{
|
||||||
exports.setAuthorName(author.authorID, name);
|
exports.setAuthorName(author.authorID, name);
|
||||||
|
}
|
||||||
|
|
||||||
//return the authorID
|
//return the authorID
|
||||||
callback(null, author);
|
callback(null, author);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AuthorID for a mapper. We can map using a mapperkey,
|
* Returns the AuthorID for a mapper. We can map using a mapperkey,
|
||||||
* so far this is token2author and mapper2author
|
* so far this is token2author and mapper2author
|
||||||
* @param {String} mapperkey The database key name for this mapper
|
* @param {String} mapperkey The database key name for this mapper
|
||||||
* @param {String} mapper The mapper
|
* @param {String} mapper The mapper
|
||||||
* @param {Function} callback callback (err, author)
|
* @param {Function} callback callback (err, author)
|
||||||
*/
|
*/
|
||||||
function mapAuthorWithDBKey (mapperkey, mapper, callback)
|
function mapAuthorWithDBKey (mapperkey, mapper, callback)
|
||||||
{
|
{
|
||||||
//try to map to an author
|
//try to map to an author
|
||||||
db.get(mapperkey + ":" + mapper, function (err, author)
|
db.get(mapperkey + ":" + mapper, function (err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//there is no author with this mapper, so create one
|
//there is no author with this mapper, so create one
|
||||||
if(author == null)
|
if(!author)
|
||||||
{
|
{
|
||||||
exports.createAuthor(null, function(err, author)
|
exports.createAuthor(null, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//create the token2author relation
|
//create the token2author relation
|
||||||
db.set(mapperkey + ":" + mapper, author.authorID);
|
db.set(mapperkey + ":" + mapper, author.authorID);
|
||||||
|
|
||||||
//return the author
|
//return the author
|
||||||
callback(null, author);
|
callback(null, author);
|
||||||
});
|
});
|
||||||
|
@ -103,7 +105,7 @@ function mapAuthorWithDBKey (mapperkey, mapper, callback)
|
||||||
{
|
{
|
||||||
//update the timestamp of this author
|
//update the timestamp of this author
|
||||||
db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime());
|
db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime());
|
||||||
|
|
||||||
//return the author
|
//return the author
|
||||||
callback(null, {authorID: author});
|
callback(null, {authorID: author});
|
||||||
}
|
}
|
||||||
|
@ -111,22 +113,22 @@ function mapAuthorWithDBKey (mapperkey, mapper, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function that creates the database entry for an author
|
* Internal function that creates the database entry for an author
|
||||||
* @param {String} name The name of the author
|
* @param {String} name The name of the author
|
||||||
*/
|
*/
|
||||||
exports.createAuthor = function(name, callback)
|
exports.createAuthor = function(name, callback)
|
||||||
{
|
{
|
||||||
//create the new author name
|
//create the new author name
|
||||||
var author = "a." + randomString(16);
|
var author = "a." + randomString(16);
|
||||||
|
|
||||||
//create the globalAuthors db entry
|
//create the globalAuthors db entry
|
||||||
var authorObj = {"colorId" : Math.floor(Math.random()*32), "name": name, "timestamp": new Date().getTime()};
|
var authorObj = {"colorId" : Math.floor(Math.random()*32), "name": name, "timestamp": new Date().getTime()};
|
||||||
|
|
||||||
//set the global author db entry
|
//set the global author db entry
|
||||||
db.set("globalAuthor:" + author, authorObj);
|
db.set("globalAuthor:" + author, authorObj);
|
||||||
|
|
||||||
callback(null, {authorID: author});
|
callback(null, {authorID: author});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Author Obj of the author
|
* Returns the Author Obj of the author
|
||||||
|
@ -136,7 +138,7 @@ exports.createAuthor = function(name, callback)
|
||||||
exports.getAuthor = function (author, callback)
|
exports.getAuthor = function (author, callback)
|
||||||
{
|
{
|
||||||
db.get("globalAuthor:" + author, callback);
|
db.get("globalAuthor:" + author, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color Id of the author
|
* Returns the color Id of the author
|
||||||
|
@ -146,7 +148,7 @@ exports.getAuthor = function (author, callback)
|
||||||
exports.getAuthorColorId = function (author, callback)
|
exports.getAuthorColorId = function (author, callback)
|
||||||
{
|
{
|
||||||
db.getSub("globalAuthor:" + author, ["colorId"], callback);
|
db.getSub("globalAuthor:" + author, ["colorId"], callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the color Id of the author
|
* Sets the color Id of the author
|
||||||
|
@ -156,7 +158,7 @@ exports.getAuthorColorId = function (author, callback)
|
||||||
exports.setAuthorColorId = function (author, colorId, callback)
|
exports.setAuthorColorId = function (author, colorId, callback)
|
||||||
{
|
{
|
||||||
db.setSub("globalAuthor:" + author, ["colorId"], colorId, callback);
|
db.setSub("globalAuthor:" + author, ["colorId"], colorId, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the author
|
* Returns the name of the author
|
||||||
|
@ -166,7 +168,7 @@ exports.setAuthorColorId = function (author, colorId, callback)
|
||||||
exports.getAuthorName = function (author, callback)
|
exports.getAuthorName = function (author, callback)
|
||||||
{
|
{
|
||||||
db.getSub("globalAuthor:" + author, ["name"], callback);
|
db.getSub("globalAuthor:" + author, ["name"], callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the name of the author
|
* Sets the name of the author
|
||||||
|
@ -176,12 +178,12 @@ exports.getAuthorName = function (author, callback)
|
||||||
exports.setAuthorName = function (author, name, callback)
|
exports.setAuthorName = function (author, name, callback)
|
||||||
{
|
{
|
||||||
db.setSub("globalAuthor:" + author, ["name"], name, callback);
|
db.setSub("globalAuthor:" + author, ["name"], name, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random String with the given length. Is needed to generate the Author Ids
|
* Generates a random String with the given length. Is needed to generate the Author Ids
|
||||||
*/
|
*/
|
||||||
function randomString(len)
|
function randomString(len)
|
||||||
{
|
{
|
||||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
var randomstring = '';
|
var randomstring = '';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* The DB Module provides a database initalized with the settings
|
* The DB Module provides a database initalized with the settings
|
||||||
* provided by the settings module
|
* provided by the settings module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -33,14 +33,14 @@ exports.db = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initalizes the database with the settings provided by the settings module
|
* Initalizes the database with the settings provided by the settings module
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
exports.init = function(callback)
|
exports.init = function(callback)
|
||||||
{
|
{
|
||||||
//initalize the database async
|
//initalize the database async
|
||||||
db.init(function(err)
|
db.init(function(err)
|
||||||
{
|
{
|
||||||
//there was an error while initializing the database, output it and stop
|
//there was an error while initializing the database, output it and stop
|
||||||
if(err)
|
if(err)
|
||||||
{
|
{
|
||||||
console.error("ERROR: Problem while initalizing the database");
|
console.error("ERROR: Problem while initalizing the database");
|
||||||
|
@ -50,8 +50,8 @@ exports.init = function(callback)
|
||||||
//everything ok
|
//everything ok
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
exports.db = db;
|
exports.db = db;
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
|
@ -17,29 +17,29 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var padManager = require("./PadManager");
|
var padManager = require("./PadManager");
|
||||||
var sessionManager = require("./SessionManager");
|
var sessionManager = require("./SessionManager");
|
||||||
|
|
||||||
exports.deleteGroup = function(groupID, callback)
|
exports.deleteGroup = function(groupID, callback)
|
||||||
{
|
{
|
||||||
var group;
|
var group;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//ensure group exists
|
//ensure group exists
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
//try to get the group entry
|
//try to get the group entry
|
||||||
db.get("group:" + groupID, function (err, _group)
|
db.get("group:" + groupID, function (err, _group)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(_group == null)
|
if(!_group)
|
||||||
{
|
{
|
||||||
callback(new customError("groupID does not exist","apierror"));
|
callback(new customError("groupID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -60,14 +60,14 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
{
|
{
|
||||||
padIDs.push(i);
|
padIDs.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
//loop trough all pads and delete them
|
//loop trough all pads and delete them
|
||||||
async.forEach(padIDs, function(padID, callback)
|
async.forEach(padIDs, function(padID, callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padID, function(err, pad)
|
padManager.getPad(padID, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
pad.remove(callback);
|
pad.remove(callback);
|
||||||
});
|
});
|
||||||
}, callback);
|
}, callback);
|
||||||
|
@ -79,18 +79,21 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
db.get("group2sessions:" + groupID, function (err, group2sessions)
|
db.get("group2sessions:" + groupID, function (err, group2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//skip if there is no group2sessions entry
|
//skip if there is no group2sessions entry
|
||||||
if(group2sessions == null) {callback(); return}
|
if(!group2sessions) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//collect all sessions in an array, that allows us to use async.forEach
|
//collect all sessions in an array, that allows us to use async.forEach
|
||||||
var sessions = [];
|
var sessions = [];
|
||||||
for(var i in group2sessions.sessionsIDs)
|
for(var i in group2sessions.sessionsIDs)
|
||||||
{
|
{
|
||||||
sessions.push(i);
|
sessions.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
//loop trough all sessions and delete them
|
//loop trough all sessions and delete them
|
||||||
async.forEach(sessions, function(session, callback)
|
async.forEach(sessions, function(session, callback)
|
||||||
{
|
{
|
||||||
sessionManager.deleteSession(session, callback);
|
sessionManager.deleteSession(session, callback);
|
||||||
|
@ -109,27 +112,27 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.doesGroupExist = function(groupID, callback)
|
exports.doesGroupExist = function(groupID, callback)
|
||||||
{
|
{
|
||||||
//try to get the group entry
|
//try to get the group entry
|
||||||
db.get("group:" + groupID, function (err, group)
|
db.get("group:" + groupID, function (err, group)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, group != null);
|
callback(null, group ? true : false);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.createGroup = function(callback)
|
exports.createGroup = function(callback)
|
||||||
{
|
{
|
||||||
//search for non existing groupID
|
//search for non existing groupID
|
||||||
var groupID = "g." + randomString(16);
|
var groupID = "g." + randomString(16);
|
||||||
|
|
||||||
//create the group
|
//create the group
|
||||||
db.set("group:" + groupID, {pads: {}});
|
db.set("group:" + groupID, {pads: {}});
|
||||||
callback(null, {groupID: groupID});
|
callback(null, {groupID: groupID});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
||||||
{
|
{
|
||||||
|
@ -139,22 +142,22 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
||||||
callback(new customError("groupMapper is no string","apierror"));
|
callback(new customError("groupMapper is no string","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//try to get a group for this mapper
|
//try to get a group for this mapper
|
||||||
db.get("mapper2group:"+groupMapper, function(err, groupID)
|
db.get("mapper2group:"+groupMapper, function(err, groupID)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//there is no group for this mapper, let's create a group
|
//there is no group for this mapper, let's create a group
|
||||||
if(groupID == null)
|
if(!groupID)
|
||||||
{
|
{
|
||||||
exports.createGroup(function(err, responseObj)
|
exports.createGroup(function(err, responseObj)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//create the mapper entry for this group
|
//create the mapper entry for this group
|
||||||
db.set("mapper2group:"+groupMapper, responseObj.groupID);
|
db.set("mapper2group:"+groupMapper, responseObj.groupID);
|
||||||
|
|
||||||
callback(null, responseObj);
|
callback(null, responseObj);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -165,7 +168,7 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
||||||
callback(null, {groupID: groupID});
|
callback(null, {groupID: groupID});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.createGroupPad = function(groupID, padName, text, callback)
|
exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
{
|
{
|
||||||
|
@ -173,15 +176,15 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
var padID = groupID + "$" + padName;
|
var padID = groupID + "$" + padName;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//ensure group exists
|
//ensure group exists
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
exports.doesGroupExist(groupID, function(err, exists)
|
exports.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("groupID does not exist","apierror"));
|
callback(new customError("groupID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -198,9 +201,9 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
padManager.doesPadExists(padID, function(err, exists)
|
padManager.doesPadExists(padID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//pad exists already
|
//pad exists already
|
||||||
if(exists == true)
|
if(exists)
|
||||||
{
|
{
|
||||||
callback(new customError("padName does already exist","apierror"));
|
callback(new customError("padName does already exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -231,16 +234,16 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, {padID: padID});
|
callback(null, {padID: padID});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.listPads = function(groupID, callback)
|
exports.listPads = function(groupID, callback)
|
||||||
{
|
{
|
||||||
exports.doesGroupExist(groupID, function(err, exists)
|
exports.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("groupID does not exist","apierror"));
|
callback(new customError("groupID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -254,12 +257,12 @@ exports.listPads = function(groupID, callback)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random String with the given length. Is needed to generate the Author Ids
|
* Generates a random String with the given length. Is needed to generate the Author Ids
|
||||||
*/
|
*/
|
||||||
function randomString(len)
|
function randomString(len)
|
||||||
{
|
{
|
||||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
var randomstring = '';
|
var randomstring = '';
|
||||||
|
|
896
node/db/Pad.js
896
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");
|
||||||
|
@ -15,497 +13,463 @@ var padManager = require("./PadManager");
|
||||||
var padMessageHandler = require("../handler/PadMessageHandler");
|
var padMessageHandler = require("../handler/PadMessageHandler");
|
||||||
var readOnlyManager = require("./ReadOnlyManager");
|
var readOnlyManager = require("./ReadOnlyManager");
|
||||||
var crypto = require("crypto");
|
var crypto = require("crypto");
|
||||||
|
var cleanText = require("../utils/cleantext").cleanText;
|
||||||
|
|
||||||
/**
|
var Pad = function Pad(id) {
|
||||||
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
|
this.atext = Changeset.makeAText("\n");
|
||||||
* @param txt
|
this.pool = AttributePoolFactory.createAttributePool();
|
||||||
*/
|
this.head = -1;
|
||||||
exports.cleanText = function (txt) {
|
this.chatHead = -1;
|
||||||
return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' ');
|
this.publicStatus = false;
|
||||||
}
|
this.passwordHash = null;
|
||||||
|
this.id = id;
|
||||||
|
};
|
||||||
|
|
||||||
Class('Pad', {
|
exports.Pad = Pad;
|
||||||
|
|
||||||
// these are the properties
|
Pad.prototype.apool = function apool() {
|
||||||
has : {
|
return this.pool;
|
||||||
|
};
|
||||||
atext : {
|
|
||||||
is : 'rw', // readwrite
|
|
||||||
init : function() { return Changeset.makeAText("\n"); } // first value
|
|
||||||
}, // atext
|
|
||||||
|
|
||||||
pool : {
|
|
||||||
is: 'rw',
|
|
||||||
init : function() { return AttributePoolFactory.createAttributePool(); },
|
|
||||||
getterName : 'apool' // legacy
|
|
||||||
}, // pool
|
|
||||||
|
|
||||||
head : {
|
|
||||||
is : 'rw',
|
|
||||||
init : -1,
|
|
||||||
getterName : 'getHeadRevisionNumber'
|
|
||||||
}, // head
|
|
||||||
|
|
||||||
chatHead : {
|
|
||||||
is: 'rw',
|
|
||||||
init: -1
|
|
||||||
}, // chatHead
|
|
||||||
|
|
||||||
publicStatus : {
|
|
||||||
is: 'rw',
|
|
||||||
init: false,
|
|
||||||
getterName : 'getPublicStatus'
|
|
||||||
}, //publicStatus
|
|
||||||
|
|
||||||
passwordHash : {
|
|
||||||
is: 'rw',
|
|
||||||
init: null
|
|
||||||
}, // passwordHash
|
|
||||||
|
|
||||||
id : { is : 'r' }
|
|
||||||
},
|
|
||||||
|
|
||||||
methods : {
|
Pad.prototype.getHeadRevisionNumber = function getHeadRevisionNumber() {
|
||||||
|
return this.head;
|
||||||
BUILD : function (id)
|
};
|
||||||
|
|
||||||
|
Pad.prototype.getPublicStatus = function getPublicStatus() {
|
||||||
|
return this.publicStatus;
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
|
||||||
|
|
||||||
|
if(!author)
|
||||||
|
{
|
||||||
|
author = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
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});
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
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(var 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)
|
|
||||||
{
|
|
||||||
if(!author)
|
|
||||||
author = '';
|
|
||||||
|
|
||||||
var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
|
return authors;
|
||||||
Changeset.copyAText(newAText, this.atext);
|
};
|
||||||
|
|
||||||
var newRev = ++this.head;
|
Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targetRev, callback) {
|
||||||
|
var _this = this;
|
||||||
var newRevData = {};
|
|
||||||
newRevData.changeset = aChangeset;
|
var keyRev = this.getKeyRevisionNumber(targetRev);
|
||||||
newRevData.meta = {};
|
var atext;
|
||||||
newRevData.meta.author = author;
|
var changesets = [];
|
||||||
newRevData.meta.timestamp = new Date().getTime();
|
|
||||||
|
//find out which changesets are needed
|
||||||
//ex. getNumForAuthor
|
var neededChangesets = [];
|
||||||
if(author != '')
|
var curRev = keyRev;
|
||||||
this.pool.putAttrib(['author', author || '']);
|
while (curRev < targetRev)
|
||||||
|
{
|
||||||
if(newRev % 100 == 0)
|
curRev++;
|
||||||
{
|
neededChangesets.push(curRev);
|
||||||
newRevData.meta.atext = this.atext;
|
}
|
||||||
}
|
|
||||||
|
async.series([
|
||||||
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
//get all needed data out of the database
|
||||||
db.set("pad:"+this.id, {atext: this.atext,
|
function(callback)
|
||||||
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);
|
async.parallel([
|
||||||
}, // getRevisionChangeset
|
//get the atext of the key revision
|
||||||
|
function (callback)
|
||||||
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;
|
Pad.prototype.getKeyRevisionNumber = function getKeyRevisionNumber(revNum) {
|
||||||
atext = Changeset.cloneAText(_atext);
|
return Math.floor(revNum / 100) * 100;
|
||||||
callback();
|
};
|
||||||
});
|
|
||||||
},
|
Pad.prototype.text = function text() {
|
||||||
//get all needed changesets
|
return this.atext.text;
|
||||||
function (callback)
|
};
|
||||||
{
|
|
||||||
async.forEach(neededChangesets, function(item, callback)
|
Pad.prototype.setText = function setText(newText) {
|
||||||
{
|
//clean the new text
|
||||||
_this.getRevisionChangeset(item, function(err, changeset)
|
newText = cleanText(newText);
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
var oldText = this.text();
|
||||||
changesets[item] = changeset;
|
|
||||||
callback();
|
//create the changeset
|
||||||
});
|
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
||||||
}, callback);
|
|
||||||
}
|
//append the changeset
|
||||||
], callback);
|
this.appendRevision(changeset);
|
||||||
},
|
};
|
||||||
//apply all changesets to the key changeset
|
|
||||||
function(callback)
|
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||||
{
|
this.chatHead++;
|
||||||
var apool = _this.apool();
|
//save the chat entry in the database
|
||||||
var curRev = keyRev;
|
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
||||||
|
//save the new chat head
|
||||||
while (curRev < targetRev)
|
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
||||||
{
|
};
|
||||||
curRev++;
|
|
||||||
var cs = changesets[curRev];
|
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
||||||
atext = Changeset.applyToAText(cs, atext, apool);
|
var _this = this;
|
||||||
}
|
var entry;
|
||||||
|
|
||||||
callback(null);
|
async.series([
|
||||||
}
|
//get the chat entry
|
||||||
], function(err)
|
function(callback)
|
||||||
|
{
|
||||||
|
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, atext);
|
entry = _entry;
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
//add the authorName
|
||||||
getKeyRevisionNumber : function(revNum)
|
function(callback)
|
||||||
{
|
{
|
||||||
return Math.floor(revNum / 100) * 100;
|
//this chat message doesn't exist, return null
|
||||||
},
|
if(!entry)
|
||||||
|
|
||||||
text : function()
|
|
||||||
{
|
|
||||||
return this.atext.text;
|
|
||||||
},
|
|
||||||
|
|
||||||
setText : function(newText)
|
|
||||||
{
|
|
||||||
//clean the new text
|
|
||||||
newText = exports.cleanText(newText);
|
|
||||||
|
|
||||||
var oldText = this.text();
|
|
||||||
|
|
||||||
//create the changeset
|
|
||||||
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
|
||||||
|
|
||||||
//append the changeset
|
|
||||||
this.appendRevision(changeset);
|
|
||||||
},
|
|
||||||
|
|
||||||
appendChatMessage: function(text, userId, time)
|
|
||||||
{
|
|
||||||
this.chatHead++;
|
|
||||||
//save the chat entry in the database
|
|
||||||
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
|
||||||
//save the new chat head
|
|
||||||
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
|
||||||
},
|
|
||||||
|
|
||||||
getChatMessage: function(entryNum, callback)
|
|
||||||
{
|
|
||||||
var _this = this;
|
|
||||||
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;
|
callback();
|
||||||
callback(null, entry);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getLastChatMessages: function(count, callback)
|
|
||||||
{
|
|
||||||
//return an empty array if there are no chat messages
|
|
||||||
if(this.chatHead == -1)
|
|
||||||
{
|
|
||||||
callback(null, []);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
//works only if we decrement the amount, for some reason
|
|
||||||
count--;
|
|
||||||
|
|
||||||
//set the startpoint
|
//get the authorName
|
||||||
var start = this.chatHead-count;
|
authorManager.getAuthorName(entry.userId, function(err, authorName)
|
||||||
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]) {
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
_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) {
|
||||||
|
_this.chatHead = value.chatHead;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this.chatHead = -1;
|
||||||
|
}
|
||||||
|
//ensure we have a local publicStatus variable
|
||||||
|
if(value.publicStatus) {
|
||||||
|
_this.publicStatus = value.publicStatus;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this.publicStatus = false;
|
||||||
|
}
|
||||||
|
//ensure we have a local passwordHash variable
|
||||||
|
if(value.passwordHash) {
|
||||||
|
_this.passwordHash = value.passwordHash;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_this.passwordHash = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//this pad doesn't exist, so create it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var firstChangeset = Changeset.makeSplice("\n", 0, 0, 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 ? hash(password, generateSalt()) : null;
|
||||||
|
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.isCorrectPassword = function isCorrectPassword(password) {
|
||||||
|
return compare(this.passwordHash, password);
|
||||||
|
};
|
||||||
|
|
||||||
|
Pad.isPasswordProtected = function isPasswordProtected() {
|
||||||
|
return this.passwordHash ? true: false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Crypto helper methods */
|
/* Crypto helper methods */
|
||||||
|
|
||||||
|
@ -531,5 +495,5 @@ function generateSalt()
|
||||||
|
|
||||||
function compare(hashStr, password)
|
function compare(hashStr, password)
|
||||||
{
|
{
|
||||||
return hash(password, hashStr.split("$")[1]) === hashStr;
|
return hash(password, hashStr.split("$")[1]) === hashStr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Object containing all known Pads. Provides "get" and "set" functions,
|
* An Object containing all known Pads. Provides "get" and "set" functions,
|
||||||
* which should be used instead of indexing with brackets. These prepend a
|
* which should be used instead of indexing with brackets. These prepend a
|
||||||
* colon to the key, to avoid conflicting with built-in Object methods or with
|
* colon to the key, to avoid conflicting with built-in Object methods or with
|
||||||
|
@ -41,26 +41,26 @@ var globalPads = {
|
||||||
/**
|
/**
|
||||||
* Returns a Pad Object with the callback
|
* Returns a Pad Object with the callback
|
||||||
* @param id A String with the id of the pad
|
* @param id A String with the id of the pad
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
exports.getPad = function(id, text, callback)
|
exports.getPad = function(id, text, callback)
|
||||||
{
|
{
|
||||||
//check if this is a valid padId
|
//check if this is a valid padId
|
||||||
if(!exports.isValidPadId(id))
|
if(!exports.isValidPadId(id))
|
||||||
{
|
{
|
||||||
callback(new customError(id + " is not a valid padId","apierror"));
|
callback(new customError(id + " is not a valid padId","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//make text an optional parameter
|
//make text an optional parameter
|
||||||
if(typeof text == "function")
|
if(typeof text === "function")
|
||||||
{
|
{
|
||||||
callback = text;
|
callback = text;
|
||||||
text = null;
|
text = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if this is a valid text
|
//check if this is a valid text
|
||||||
if(text != null)
|
if(text)
|
||||||
{
|
{
|
||||||
//check if text is a string
|
//check if text is a string
|
||||||
if(typeof text != "string")
|
if(typeof text != "string")
|
||||||
|
@ -68,7 +68,7 @@ exports.getPad = function(id, text, callback)
|
||||||
callback(new customError("text is not a string","apierror"));
|
callback(new customError("text is not a string","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if text is less than 100k chars
|
//check if text is less than 100k chars
|
||||||
if(text.length > 100000)
|
if(text.length > 100000)
|
||||||
{
|
{
|
||||||
|
@ -76,11 +76,11 @@ exports.getPad = function(id, text, callback)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pad = globalPads.get(id);
|
var pad = globalPads.get(id);
|
||||||
|
|
||||||
//return pad if its already loaded
|
//return pad if its already loaded
|
||||||
if(pad != null)
|
if(pad)
|
||||||
{
|
{
|
||||||
callback(null, pad);
|
callback(null, pad);
|
||||||
}
|
}
|
||||||
|
@ -88,17 +88,17 @@ exports.getPad = function(id, text, callback)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pad = new Pad(id);
|
pad = new Pad(id);
|
||||||
|
|
||||||
//initalize the pad
|
//initalize the pad
|
||||||
pad.init(text, function(err)
|
pad.init(text, function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
globalPads.set(id, pad);
|
globalPads.set(id, pad);
|
||||||
callback(null, pad);
|
callback(null, pad);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
//checks if a pad exists
|
//checks if a pad exists
|
||||||
exports.doesPadExists = function(padId, callback)
|
exports.doesPadExists = function(padId, callback)
|
||||||
|
@ -106,18 +106,18 @@ exports.doesPadExists = function(padId, callback)
|
||||||
db.get("pad:"+padId, function(err, value)
|
db.get("pad:"+padId, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, value != null);
|
callback(null, value ? true : false);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.isValidPadId = function(padId)
|
exports.isValidPadId = function(padId)
|
||||||
{
|
{
|
||||||
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
return (/^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/).test(padId);
|
||||||
}
|
};
|
||||||
|
|
||||||
//removes a pad from the array
|
//removes a pad from the array
|
||||||
exports.unloadPad = function(padId)
|
exports.unloadPad = function(padId)
|
||||||
{
|
{
|
||||||
if(globalPads.get(padId))
|
if(globalPads.get(padId))
|
||||||
globalPads.remove(padId);
|
globalPads.remove(padId);
|
||||||
}
|
};
|
||||||
|
|
|
@ -27,9 +27,9 @@ var async = require("async");
|
||||||
* @param {String} padId the id of the pad
|
* @param {String} padId the id of the pad
|
||||||
*/
|
*/
|
||||||
exports.getReadOnlyId = function (padId, callback)
|
exports.getReadOnlyId = function (padId, callback)
|
||||||
{
|
{
|
||||||
var readOnlyId;
|
var readOnlyId;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
//check if there is a pad2readonly entry
|
//check if there is a pad2readonly entry
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -39,10 +39,10 @@ exports.getReadOnlyId = function (padId, callback)
|
||||||
function(dbReadOnlyId, callback)
|
function(dbReadOnlyId, callback)
|
||||||
{
|
{
|
||||||
//there is no readOnly Entry in the database, let's create one
|
//there is no readOnly Entry in the database, let's create one
|
||||||
if(dbReadOnlyId == null)
|
if(!dbReadOnlyId)
|
||||||
{
|
{
|
||||||
readOnlyId = "r." + randomString(16);
|
readOnlyId = "r." + randomString(16);
|
||||||
|
|
||||||
db.set("pad2readonly:" + padId, readOnlyId);
|
db.set("pad2readonly:" + padId, readOnlyId);
|
||||||
db.set("readonly2pad:" + readOnlyId, padId);
|
db.set("readonly2pad:" + readOnlyId, padId);
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ exports.getReadOnlyId = function (padId, callback)
|
||||||
{
|
{
|
||||||
readOnlyId = dbReadOnlyId;
|
readOnlyId = dbReadOnlyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -59,8 +59,8 @@ exports.getReadOnlyId = function (padId, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
//return the results
|
//return the results
|
||||||
callback(null, readOnlyId);
|
callback(null, readOnlyId);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a the padId for a read only id
|
* returns a the padId for a read only id
|
||||||
|
@ -69,12 +69,12 @@ exports.getReadOnlyId = function (padId, callback)
|
||||||
exports.getPadId = function(readOnlyId, callback)
|
exports.getPadId = function(readOnlyId, callback)
|
||||||
{
|
{
|
||||||
db.get("readonly2pad:" + readOnlyId, callback);
|
db.get("readonly2pad:" + readOnlyId, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random String with the given length. Is needed to generate the read only ids
|
* Generates a random String with the given length. Is needed to generate the read only ids
|
||||||
*/
|
*/
|
||||||
function randomString(len)
|
function randomString(len)
|
||||||
{
|
{
|
||||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
var randomstring = '';
|
var randomstring = '';
|
||||||
|
|
|
@ -24,18 +24,18 @@ var async = require("async");
|
||||||
var authorManager = require("./AuthorManager");
|
var authorManager = require("./AuthorManager");
|
||||||
var padManager = require("./PadManager");
|
var padManager = require("./PadManager");
|
||||||
var sessionManager = require("./SessionManager");
|
var sessionManager = require("./SessionManager");
|
||||||
var settings = require("../utils/Settings")
|
var settings = require("../utils/Settings");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function controlls the access to a pad, it checks if the user can access a pad.
|
* This function controlls the access to a pad, it checks if the user can access a pad.
|
||||||
* @param padID the pad the user wants to access
|
* @param padID the pad the user wants to access
|
||||||
* @param sesssionID the session the user has (set via api)
|
* @param sesssionID the session the user has (set via api)
|
||||||
* @param token the token of the author (randomly generated at client side, used for public pads)
|
* @param token the token of the author (randomly generated at client side, used for public pads)
|
||||||
* @param password the password the user has given to access this pad, can be null
|
* @param password the password the user has given to access this pad, can be null
|
||||||
* @param callback will be called with (err, {accessStatus: grant|deny|wrongPassword|needPassword, authorID: a.xxxxxx})
|
* @param callback will be called with (err, {accessStatus: grant|deny|wrongPassword|needPassword, authorID: a.xxxxxx})
|
||||||
*/
|
*/
|
||||||
exports.checkAccess = function (padID, sessionID, token, password, callback)
|
exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
{
|
{
|
||||||
var statusObject;
|
var statusObject;
|
||||||
|
|
||||||
// a valid session is required (api-only mode)
|
// a valid session is required (api-only mode)
|
||||||
|
@ -58,7 +58,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
authorManager.getAuthor4Token(token, function(err, author)
|
authorManager.getAuthor4Token(token, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
// assume user has access
|
// assume user has access
|
||||||
statusObject = {accessStatus: "grant", authorID: author};
|
statusObject = {accessStatus: "grant", authorID: author};
|
||||||
// user can't create pads
|
// user can't create pads
|
||||||
|
@ -68,7 +68,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
padManager.doesPadExists(padID, function(err, exists)
|
padManager.doesPadExists(padID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
// pad doesn't exist - user can't have access
|
// pad doesn't exist - user can't have access
|
||||||
if(!exists) statusObject.accessStatus = "deny";
|
if(!exists) statusObject.accessStatus = "deny";
|
||||||
// grant or deny access, with author of token
|
// grant or deny access, with author of token
|
||||||
|
@ -81,13 +81,13 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
// grant access, with author of token
|
// grant access, with author of token
|
||||||
callback(null, statusObject);
|
callback(null, statusObject);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
//don't continue
|
//don't continue
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var groupID = padID.split("$")[0];
|
var groupID = padID.split("$")[0];
|
||||||
var padExists = false;
|
var padExists = false;
|
||||||
var validSession = false;
|
var validSession = false;
|
||||||
|
@ -95,10 +95,10 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
var tokenAuthor;
|
var tokenAuthor;
|
||||||
var isPublic;
|
var isPublic;
|
||||||
var isPasswordProtected;
|
var isPasswordProtected;
|
||||||
var passwordStatus = password == null ? "notGiven" : "wrong"; // notGiven, correct, wrong
|
var passwordStatus = password === null ? "notGiven" : "wrong"; // notGiven, correct, wrong
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get basic informations from the database
|
//get basic informations from the database
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
async.parallel([
|
async.parallel([
|
||||||
|
@ -123,19 +123,19 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
var now = Math.floor(new Date().getTime()/1000);
|
var now = Math.floor(new Date().getTime()/1000);
|
||||||
|
|
||||||
//is it for this group? and is validUntil still ok? --> validSession
|
//is it for this group? and is validUntil still ok? --> validSession
|
||||||
if(sessionInfo.groupID == groupID && sessionInfo.validUntil > now)
|
if(sessionInfo.groupID == groupID && sessionInfo.validUntil > now)
|
||||||
{
|
{
|
||||||
validSession = true;
|
validSession = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionAuthor = sessionInfo.authorID;
|
sessionAuthor = sessionInfo.authorID;
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -156,28 +156,28 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//skip this if the pad doesn't exists
|
//skip this if the pad doesn't exists
|
||||||
if(padExists == false)
|
if(!padExists)
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
padManager.getPad(padID, function(err, pad)
|
padManager.getPad(padID, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//is it a public pad?
|
//is it a public pad?
|
||||||
isPublic = pad.getPublicStatus();
|
isPublic = pad.getPublicStatus();
|
||||||
|
|
||||||
//is it password protected?
|
//is it password protected?
|
||||||
isPasswordProtected = pad.isPasswordProtected();
|
isPasswordProtected = pad.isPasswordProtected();
|
||||||
|
|
||||||
//is password correct?
|
//is password correct?
|
||||||
if(isPasswordProtected && password && pad.isCorrectPassword(password))
|
if(isPasswordProtected && password && pad.isCorrectPassword(password))
|
||||||
{
|
{
|
||||||
passwordStatus = "correct";
|
passwordStatus = "correct";
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -214,7 +214,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
{
|
{
|
||||||
throw new Error("Ops, something wrong happend");
|
throw new Error("Ops, something wrong happend");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//- a valid session for this group avaible but pad doesn't exists
|
//- a valid session for this group avaible but pad doesn't exists
|
||||||
else if(validSession && !padExists)
|
else if(validSession && !padExists)
|
||||||
{
|
{
|
||||||
|
@ -238,7 +238,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
//--> grant access, with author of token
|
//--> grant access, with author of token
|
||||||
statusObject = {accessStatus: "grant", authorID: tokenAuthor};
|
statusObject = {accessStatus: "grant", authorID: tokenAuthor};
|
||||||
}
|
}
|
||||||
//- its public and the pad is password protected but wrong password given
|
//- its public and the pad is password protected but wrong password given
|
||||||
else if(isPublic && isPasswordProtected && passwordStatus == "wrong")
|
else if(isPublic && isPasswordProtected && passwordStatus == "wrong")
|
||||||
{
|
{
|
||||||
//--> deny access, ask for new password and tell them that the password is wrong
|
//--> deny access, ask for new password and tell them that the password is wrong
|
||||||
|
@ -260,14 +260,14 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
{
|
{
|
||||||
throw new Error("Ops, something wrong happend");
|
throw new Error("Ops, something wrong happend");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// there is no valid session avaiable AND pad doesn't exists
|
// there is no valid session avaiable AND pad doesn't exists
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//--> deny access
|
//--> deny access
|
||||||
statusObject = {accessStatus: "deny"};
|
statusObject = {accessStatus: "deny"};
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -275,4 +275,4 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, statusObject);
|
callback(null, statusObject);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
|
@ -17,24 +17,24 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var db = require("./DB").db;
|
var db = require("./DB").db;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var groupMangager = require("./GroupManager");
|
var groupMangager = require("./GroupManager");
|
||||||
var authorMangager = require("./AuthorManager");
|
var authorMangager = require("./AuthorManager");
|
||||||
|
|
||||||
exports.doesSessionExist = function(sessionID, callback)
|
exports.doesSessionExist = function(sessionID, callback)
|
||||||
{
|
{
|
||||||
//check if the database entry of this session exists
|
//check if the database entry of this session exists
|
||||||
db.get("session:" + sessionID, function (err, session)
|
db.get("session:" + sessionID, function (err, session)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, session != null);
|
callback(null, session ? true : false);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new session between an author and a group
|
* Creates a new session between an author and a group
|
||||||
*/
|
*/
|
||||||
|
@ -49,9 +49,9 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
groupMangager.doesGroupExist(groupID, function(err, exists)
|
groupMangager.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("groupID does not exist","apierror"));
|
callback(new customError("groupID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -68,9 +68,9 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
authorMangager.doesAuthorExists(authorID, function(err, exists)
|
authorMangager.doesAuthorExists(authorID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//author does not exist
|
//author does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("authorID does not exist","apierror"));
|
callback(new customError("authorID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -88,9 +88,9 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
if(typeof validUntil != "number")
|
if(typeof validUntil != "number")
|
||||||
{
|
{
|
||||||
//try to parse the number
|
//try to parse the number
|
||||||
if(!isNaN(parseInt(validUntil)))
|
if(!isNaN(parseInt(validUntil, 10)))
|
||||||
{
|
{
|
||||||
validUntil = parseInt(validUntil);
|
validUntil = parseInt(validUntil, 10);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -98,34 +98,34 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//ensure this is not a negativ number
|
//ensure this is not a negativ number
|
||||||
if(validUntil < 0)
|
if(validUntil < 0)
|
||||||
{
|
{
|
||||||
callback(new customError("validUntil is a negativ number","apierror"));
|
callback(new customError("validUntil is a negativ number","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ensure this is not a float value
|
//ensure this is not a float value
|
||||||
if(!is_int(validUntil))
|
if(!is_int(validUntil))
|
||||||
{
|
{
|
||||||
callback(new customError("validUntil is a float value","apierror"));
|
callback(new customError("validUntil is a float value","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if validUntil is in the future
|
//check if validUntil is in the future
|
||||||
if(Math.floor(new Date().getTime()/1000) > validUntil)
|
if(Math.floor(new Date().getTime()/1000) > validUntil)
|
||||||
{
|
{
|
||||||
callback(new customError("validUntil is in the past","apierror"));
|
callback(new customError("validUntil is in the past","apierror"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//generate sessionID
|
//generate sessionID
|
||||||
sessionID = "s." + randomString(16);
|
sessionID = "s." + randomString(16);
|
||||||
|
|
||||||
//set the session into the database
|
//set the session into the database
|
||||||
db.set("session:" + sessionID, {"groupID": groupID, "authorID": authorID, "validUntil": validUntil});
|
db.set("session:" + sessionID, {"groupID": groupID, "authorID": authorID, "validUntil": validUntil});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
},
|
},
|
||||||
//set the group2sessions entry
|
//set the group2sessions entry
|
||||||
|
@ -135,19 +135,19 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
db.get("group2sessions:" + groupID, function(err, group2sessions)
|
db.get("group2sessions:" + groupID, function(err, group2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//the entry doesn't exist so far, let's create it
|
//the entry doesn't exist so far, let's create it
|
||||||
if(group2sessions == null)
|
if(!group2sessions)
|
||||||
{
|
{
|
||||||
group2sessions = {sessionIDs : {}};
|
group2sessions = {sessionIDs : {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
//add the entry for this session
|
//add the entry for this session
|
||||||
group2sessions.sessionIDs[sessionID] = 1;
|
group2sessions.sessionIDs[sessionID] = 1;
|
||||||
|
|
||||||
//save the new element back
|
//save the new element back
|
||||||
db.set("group2sessions:" + groupID, group2sessions);
|
db.set("group2sessions:" + groupID, group2sessions);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -158,30 +158,30 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
db.get("author2sessions:" + authorID, function(err, author2sessions)
|
db.get("author2sessions:" + authorID, function(err, author2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//the entry doesn't exist so far, let's create it
|
//the entry doesn't exist so far, let's create it
|
||||||
if(author2sessions == null)
|
if(!author2sessions)
|
||||||
{
|
{
|
||||||
author2sessions = {sessionIDs : {}};
|
author2sessions = {sessionIDs : {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
//add the entry for this session
|
//add the entry for this session
|
||||||
author2sessions.sessionIDs[sessionID] = 1;
|
author2sessions.sessionIDs[sessionID] = 1;
|
||||||
|
|
||||||
//save the new element back
|
//save the new element back
|
||||||
db.set("author2sessions:" + authorID, author2sessions);
|
db.set("author2sessions:" + authorID, author2sessions);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//return error and sessionID
|
//return error and sessionID
|
||||||
callback(null, {sessionID: sessionID});
|
callback(null, {sessionID: sessionID});
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.getSessionInfo = function(sessionID, callback)
|
exports.getSessionInfo = function(sessionID, callback)
|
||||||
{
|
{
|
||||||
|
@ -189,11 +189,11 @@ exports.getSessionInfo = function(sessionID, callback)
|
||||||
db.get("session:" + sessionID, function (err, session)
|
db.get("session:" + sessionID, function (err, session)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//session does not exists
|
//session does not exists
|
||||||
if(session == null)
|
if(session === null)
|
||||||
{
|
{
|
||||||
callback(new customError("sessionID does not exist","apierror"))
|
callback(new customError("sessionID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
//everything is fine, return the sessioninfos
|
//everything is fine, return the sessioninfos
|
||||||
else
|
else
|
||||||
|
@ -201,7 +201,7 @@ exports.getSessionInfo = function(sessionID, callback)
|
||||||
callback(null, session);
|
callback(null, session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a session
|
* Deletes a session
|
||||||
|
@ -218,18 +218,18 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
db.get("session:" + sessionID, function (err, session)
|
db.get("session:" + sessionID, function (err, session)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//session does not exists
|
//session does not exists
|
||||||
if(session == null)
|
if(!session)
|
||||||
{
|
{
|
||||||
callback(new customError("sessionID does not exist","apierror"))
|
callback(new customError("sessionID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
//everything is fine, return the sessioninfos
|
//everything is fine, return the sessioninfos
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
authorID = session.authorID;
|
authorID = session.authorID;
|
||||||
groupID = session.groupID;
|
groupID = session.groupID;
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -259,32 +259,32 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
{
|
{
|
||||||
//remove the session
|
//remove the session
|
||||||
db.remove("session:" + sessionID);
|
db.remove("session:" + sessionID);
|
||||||
|
|
||||||
//remove session from group2sessions
|
//remove session from group2sessions
|
||||||
delete group2sessions.sessionIDs[sessionID];
|
delete group2sessions.sessionIDs[sessionID];
|
||||||
db.set("group2sessions:" + groupID, group2sessions);
|
db.set("group2sessions:" + groupID, group2sessions);
|
||||||
|
|
||||||
//remove session from author2sessions
|
//remove session from author2sessions
|
||||||
delete author2sessions.sessionIDs[sessionID];
|
delete author2sessions.sessionIDs[sessionID];
|
||||||
db.set("author2sessions:" + authorID, author2sessions);
|
db.set("author2sessions:" + authorID, author2sessions);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback();
|
callback();
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.listSessionsOfGroup = function(groupID, callback)
|
exports.listSessionsOfGroup = function(groupID, callback)
|
||||||
{
|
{
|
||||||
groupMangager.doesGroupExist(groupID, function(err, exists)
|
groupMangager.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("groupID does not exist","apierror"));
|
callback(new customError("groupID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -294,16 +294,16 @@ exports.listSessionsOfGroup = function(groupID, callback)
|
||||||
listSessionsWithDBKey("group2sessions:" + groupID, callback);
|
listSessionsWithDBKey("group2sessions:" + groupID, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.listSessionsOfAuthor = function(authorID, callback)
|
exports.listSessionsOfAuthor = function(authorID, callback)
|
||||||
{
|
{
|
||||||
authorMangager.doesAuthorExists(authorID, function(err, exists)
|
authorMangager.doesAuthorExists(authorID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("authorID does not exist","apierror"));
|
callback(new customError("authorID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -313,7 +313,7 @@ exports.listSessionsOfAuthor = function(authorID, callback)
|
||||||
listSessionsWithDBKey("author2sessions:" + authorID, callback);
|
listSessionsWithDBKey("author2sessions:" + authorID, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
//this function is basicly the code listSessionsOfAuthor and listSessionsOfGroup has in common
|
//this function is basicly the code listSessionsOfAuthor and listSessionsOfGroup has in common
|
||||||
function listSessionsWithDBKey (dbkey, callback)
|
function listSessionsWithDBKey (dbkey, callback)
|
||||||
|
@ -332,14 +332,14 @@ function listSessionsWithDBKey (dbkey, callback)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//collect all sessionIDs in an arrary
|
//collect all sessionIDs in an arrary
|
||||||
var sessionIDs = [];
|
var sessionIDs = [];
|
||||||
for (var i in sessions)
|
for (var i in sessions)
|
||||||
{
|
{
|
||||||
sessionIDs.push(i);
|
sessionIDs.push(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
//foreach trough the sessions and get the sessioninfos
|
//foreach trough the sessions and get the sessioninfos
|
||||||
async.forEach(sessionIDs, function(sessionID, callback)
|
async.forEach(sessionIDs, function(sessionID, callback)
|
||||||
{
|
{
|
||||||
|
@ -361,7 +361,7 @@ function listSessionsWithDBKey (dbkey, callback)
|
||||||
/**
|
/**
|
||||||
* Generates a random String with the given length. Is needed to generate the SessionIDs
|
* Generates a random String with the given length. Is needed to generate the SessionIDs
|
||||||
*/
|
*/
|
||||||
function randomString(len)
|
function randomString(len)
|
||||||
{
|
{
|
||||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
var randomstring = '';
|
var randomstring = '';
|
||||||
|
@ -375,6 +375,6 @@ function randomString(len)
|
||||||
|
|
||||||
//checks if a number is an int
|
//checks if a number is an int
|
||||||
function is_int(value)
|
function is_int(value)
|
||||||
{
|
{
|
||||||
return (parseFloat(value) == parseInt(value)) && !isNaN(value)
|
return (parseFloat(value) == parseInt(value, 10)) && !isNaN(value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ try
|
||||||
{
|
{
|
||||||
apikey = fs.readFileSync("../APIKEY.txt","utf8");
|
apikey = fs.readFileSync("../APIKEY.txt","utf8");
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
apikey = randomString(32);
|
apikey = randomString(32);
|
||||||
fs.writeFileSync("../APIKEY.txt",apikey,"utf8");
|
fs.writeFileSync("../APIKEY.txt",apikey,"utf8");
|
||||||
|
@ -37,28 +37,28 @@ catch(e)
|
||||||
//a list of all functions
|
//a list of all functions
|
||||||
var functions = {
|
var functions = {
|
||||||
"createGroup" : [],
|
"createGroup" : [],
|
||||||
"createGroupIfNotExistsFor" : ["groupMapper"],
|
"createGroupIfNotExistsFor" : ["groupMapper"],
|
||||||
"deleteGroup" : ["groupID"],
|
"deleteGroup" : ["groupID"],
|
||||||
"listPads" : ["groupID"],
|
"listPads" : ["groupID"],
|
||||||
"createPad" : ["padID", "text"],
|
"createPad" : ["padID", "text"],
|
||||||
"createGroupPad" : ["groupID", "padName", "text"],
|
"createGroupPad" : ["groupID", "padName", "text"],
|
||||||
"createAuthor" : ["name"],
|
"createAuthor" : ["name"],
|
||||||
"createAuthorIfNotExistsFor": ["authorMapper" , "name"],
|
"createAuthorIfNotExistsFor": ["authorMapper" , "name"],
|
||||||
"createSession" : ["groupID", "authorID", "validUntil"],
|
"createSession" : ["groupID", "authorID", "validUntil"],
|
||||||
"deleteSession" : ["sessionID"],
|
"deleteSession" : ["sessionID"],
|
||||||
"getSessionInfo" : ["sessionID"],
|
"getSessionInfo" : ["sessionID"],
|
||||||
"listSessionsOfGroup" : ["groupID"],
|
"listSessionsOfGroup" : ["groupID"],
|
||||||
"listSessionsOfAuthor" : ["authorID"],
|
"listSessionsOfAuthor" : ["authorID"],
|
||||||
"getText" : ["padID", "rev"],
|
"getText" : ["padID", "rev"],
|
||||||
"setText" : ["padID", "text"],
|
"setText" : ["padID", "text"],
|
||||||
"getHTML" : ["padID", "rev"],
|
"getHTML" : ["padID", "rev"],
|
||||||
"setHTML" : ["padID", "html"],
|
"setHTML" : ["padID", "html"],
|
||||||
"getRevisionsCount" : ["padID"],
|
"getRevisionsCount" : ["padID"],
|
||||||
"deletePad" : ["padID"],
|
"deletePad" : ["padID"],
|
||||||
"getReadOnlyID" : ["padID"],
|
"getReadOnlyID" : ["padID"],
|
||||||
"setPublicStatus" : ["padID", "publicStatus"],
|
"setPublicStatus" : ["padID", "publicStatus"],
|
||||||
"getPublicStatus" : ["padID"],
|
"getPublicStatus" : ["padID"],
|
||||||
"setPassword" : ["padID", "password"],
|
"setPassword" : ["padID", "password"],
|
||||||
"isPasswordProtected" : ["padID"]
|
"isPasswordProtected" : ["padID"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -72,12 +72,12 @@ var functions = {
|
||||||
exports.handle = function(functionName, fields, req, res)
|
exports.handle = function(functionName, fields, req, res)
|
||||||
{
|
{
|
||||||
//check the api key!
|
//check the api key!
|
||||||
if(fields["apikey"] != apikey.trim())
|
if(fields.apikey != apikey.trim())
|
||||||
{
|
{
|
||||||
res.send({code: 4, message: "no or wrong API Key", data: null});
|
res.send({code: 4, message: "no or wrong API Key", data: null});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if this is a valid function name
|
//check if this is a valid function name
|
||||||
var isKnownFunctionname = false;
|
var isKnownFunctionname = false;
|
||||||
for(var knownFunctionname in functions)
|
for(var knownFunctionname in functions)
|
||||||
|
@ -88,30 +88,30 @@ exports.handle = function(functionName, fields, req, res)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//say goodbye if this is a unkown function
|
//say goodbye if this is a unkown function
|
||||||
if(!isKnownFunctionname)
|
if(!isKnownFunctionname)
|
||||||
{
|
{
|
||||||
res.send({code: 3, message: "no such function", data: null});
|
res.send({code: 3, message: "no such function", data: null});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//put the function parameters in an array
|
//put the function parameters in an array
|
||||||
var functionParams = [];
|
var functionParams = [];
|
||||||
for(var i=0;i<functions[functionName].length;i++)
|
for(var i=0;i<functions[functionName].length;i++)
|
||||||
{
|
{
|
||||||
functionParams.push(fields[functions[functionName][i]]);
|
functionParams.push(fields[functions[functionName][i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//add a callback function to handle the response
|
//add a callback function to handle the response
|
||||||
functionParams.push(function(err, data)
|
functionParams.push(function(err, data)
|
||||||
{
|
{
|
||||||
// no error happend, everything is fine
|
// no error happend, everything is fine
|
||||||
if(err == null)
|
if(!err)
|
||||||
{
|
{
|
||||||
if(!data)
|
if(!data)
|
||||||
data = null;
|
data = null;
|
||||||
|
|
||||||
res.send({code: 0, message: "ok", data: data});
|
res.send({code: 0, message: "ok", data: data});
|
||||||
}
|
}
|
||||||
// parameters were wrong and the api stopped execution, pass the error
|
// parameters were wrong and the api stopped execution, pass the error
|
||||||
|
@ -126,15 +126,15 @@ exports.handle = function(functionName, fields, req, res)
|
||||||
ERR(err);
|
ERR(err);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//call the api function
|
//call the api function
|
||||||
api[functionName](functionParams[0],functionParams[1],functionParams[2],functionParams[3],functionParams[4]);
|
api[functionName](functionParams[0],functionParams[1],functionParams[2],functionParams[3],functionParams[4]);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a random String with the given length. Is needed to generate the Author Ids
|
* Generates a random String with the given length. Is needed to generate the Author Ids
|
||||||
*/
|
*/
|
||||||
function randomString(len)
|
function randomString(len)
|
||||||
{
|
{
|
||||||
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||||
var randomstring = '';
|
var randomstring = '';
|
||||||
|
|
|
@ -28,39 +28,43 @@ var settings = require('../utils/Settings');
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
|
|
||||||
//load abiword only if its enabled
|
//load abiword only if its enabled
|
||||||
if(settings.abiword != null)
|
if(settings.abiword)
|
||||||
|
{
|
||||||
var abiword = require("../utils/Abiword");
|
var abiword = require("../utils/Abiword");
|
||||||
|
}
|
||||||
|
|
||||||
var tempDirectory = "/tmp";
|
var tempDirectory = "/tmp";
|
||||||
|
|
||||||
//tempDirectory changes if the operating system is windows
|
//tempDirectory changes if the operating system is windows
|
||||||
if(os.type().indexOf("Windows") > -1)
|
if(os.type().indexOf("Windows") > -1)
|
||||||
{
|
{
|
||||||
tempDirectory = process.env.TEMP;
|
tempDirectory = process.env.TEMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do a requested export
|
* do a requested export
|
||||||
*/
|
*/
|
||||||
exports.doExport = function(req, res, padId, type)
|
exports.doExport = function(req, res, padId, type)
|
||||||
{
|
{
|
||||||
//tell the browser that this is a downloadable file
|
//tell the browser that this is a downloadable file
|
||||||
res.attachment(padId + "." + type);
|
res.attachment(padId + "." + type);
|
||||||
|
|
||||||
|
var randNum;
|
||||||
|
var srcFile;
|
||||||
|
var destFile;
|
||||||
|
|
||||||
//if this is a plain text export, we can do this directly
|
//if this is a plain text export, we can do this directly
|
||||||
if(type == "txt")
|
if(type == "txt")
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, pad)
|
padManager.getPad(padId, function(err, pad)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
res.send(pad.text());
|
res.send(pad.text());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if(type == 'dokuwiki')
|
else if(type == 'dokuwiki')
|
||||||
{
|
{
|
||||||
var randNum;
|
|
||||||
var srcFile, destFile;
|
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//render the dokuwiki document
|
//render the dokuwiki document
|
||||||
|
@ -71,7 +75,7 @@ exports.doExport = function(req, res, padId, type)
|
||||||
res.send(dokuwiki);
|
res.send(dokuwiki);
|
||||||
callback("stop");
|
callback("stop");
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(err && err != "stop") throw err;
|
if(err && err != "stop") throw err;
|
||||||
|
@ -80,8 +84,6 @@ exports.doExport = function(req, res, padId, type)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var html;
|
var html;
|
||||||
var randNum;
|
|
||||||
var srcFile, destFile;
|
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//render the html document
|
//render the html document
|
||||||
|
@ -92,7 +94,7 @@ exports.doExport = function(req, res, padId, type)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
html = _html;
|
html = _html;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//decide what to do with the html export
|
//decide what to do with the html export
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -101,13 +103,13 @@ exports.doExport = function(req, res, padId, type)
|
||||||
if(type == "html")
|
if(type == "html")
|
||||||
{
|
{
|
||||||
res.send(html);
|
res.send(html);
|
||||||
callback("stop");
|
callback("stop");
|
||||||
}
|
}
|
||||||
else //write the html export to a file
|
else //write the html export to a file
|
||||||
{
|
{
|
||||||
randNum = Math.floor(Math.random()*0xFFFFFFFF);
|
randNum = Math.floor(Math.random()*0xFFFFFFFF);
|
||||||
srcFile = tempDirectory + "/eplite_export_" + randNum + ".html";
|
srcFile = tempDirectory + "/eplite_export_" + randNum + ".html";
|
||||||
fs.writeFile(srcFile, html, callback);
|
fs.writeFile(srcFile, html, callback);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//send the convert job to abiword
|
//send the convert job to abiword
|
||||||
|
@ -115,7 +117,7 @@ exports.doExport = function(req, res, padId, type)
|
||||||
{
|
{
|
||||||
//ensure html can be collected by the garbage collector
|
//ensure html can be collected by the garbage collector
|
||||||
html = null;
|
html = null;
|
||||||
|
|
||||||
destFile = tempDirectory + "/eplite_export_" + randNum + "." + type;
|
destFile = tempDirectory + "/eplite_export_" + randNum + "." + type;
|
||||||
abiword.convertFile(srcFile, destFile, type, callback);
|
abiword.convertFile(srcFile, destFile, type, callback);
|
||||||
},
|
},
|
||||||
|
@ -137,7 +139,7 @@ exports.doExport = function(req, res, padId, type)
|
||||||
//100ms delay to accomidate for slow windows fs
|
//100ms delay to accomidate for slow windows fs
|
||||||
if(os.type().indexOf("Windows") > -1)
|
if(os.type().indexOf("Windows") > -1)
|
||||||
{
|
{
|
||||||
setTimeout(function()
|
setTimeout(function()
|
||||||
{
|
{
|
||||||
fs.unlink(destFile, callback);
|
fs.unlink(destFile, callback);
|
||||||
}, 100);
|
}, 100);
|
||||||
|
@ -152,6 +154,6 @@ exports.doExport = function(req, res, padId, type)
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(err && err != "stop") ERR(err);
|
if(err && err != "stop") ERR(err);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,8 +28,10 @@ var formidable = require('formidable');
|
||||||
var os = require("os");
|
var os = require("os");
|
||||||
|
|
||||||
//load abiword only if its enabled
|
//load abiword only if its enabled
|
||||||
if(settings.abiword != null)
|
if(settings.abiword)
|
||||||
|
{
|
||||||
var abiword = require("../utils/Abiword");
|
var abiword = require("../utils/Abiword");
|
||||||
|
}
|
||||||
|
|
||||||
var tempDirectory = "/tmp/";
|
var tempDirectory = "/tmp/";
|
||||||
|
|
||||||
|
@ -38,20 +40,20 @@ if(os.type().indexOf("Windows") > -1)
|
||||||
{
|
{
|
||||||
tempDirectory = process.env.TEMP;
|
tempDirectory = process.env.TEMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do a requested import
|
* do a requested import
|
||||||
*/
|
*/
|
||||||
exports.doImport = function(req, res, padId)
|
exports.doImport = function(req, res, padId)
|
||||||
{
|
{
|
||||||
//pipe to a file
|
//pipe to a file
|
||||||
//convert file to text via abiword
|
//convert file to text via abiword
|
||||||
//set text in the pad
|
//set text in the pad
|
||||||
|
|
||||||
var srcFile, destFile;
|
var srcFile, destFile;
|
||||||
var pad;
|
var pad;
|
||||||
var text;
|
var text;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//save the uploaded file to /tmp
|
//save the uploaded file to /tmp
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -59,9 +61,9 @@ exports.doImport = function(req, res, padId)
|
||||||
var form = new formidable.IncomingForm();
|
var form = new formidable.IncomingForm();
|
||||||
form.keepExtensions = true;
|
form.keepExtensions = true;
|
||||||
form.uploadDir = tempDirectory;
|
form.uploadDir = tempDirectory;
|
||||||
|
|
||||||
form.parse(req, function(err, fields, files)
|
form.parse(req, function(err, fields, files)
|
||||||
{
|
{
|
||||||
//the upload failed, stop at this point
|
//the upload failed, stop at this point
|
||||||
if(err || files.file === undefined)
|
if(err || files.file === undefined)
|
||||||
{
|
{
|
||||||
|
@ -69,7 +71,7 @@ exports.doImport = function(req, res, padId)
|
||||||
callback("uploadFailed");
|
callback("uploadFailed");
|
||||||
}
|
}
|
||||||
//everything ok, continue
|
//everything ok, continue
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//save the path of the uploaded file
|
//save the path of the uploaded file
|
||||||
srcFile = files.file.path;
|
srcFile = files.file.path;
|
||||||
|
@ -77,14 +79,14 @@ exports.doImport = function(req, res, padId)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
//ensure this is a file ending we know, else we change the file ending to .txt
|
//ensure this is a file ending we know, else we change the file ending to .txt
|
||||||
//this allows us to accept source code files like .c or .java
|
//this allows us to accept source code files like .c or .java
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var fileEnding = srcFile.split(".")[1].toLowerCase();
|
var fileEnding = srcFile.split(".")[1].toLowerCase();
|
||||||
var knownFileEndings = ["txt", "doc", "docx", "pdf", "odt", "html", "htm"];
|
var knownFileEndings = ["txt", "doc", "docx", "pdf", "odt", "html", "htm"];
|
||||||
|
|
||||||
//find out if this is a known file ending
|
//find out if this is a known file ending
|
||||||
var fileEndingKnown = false;
|
var fileEndingKnown = false;
|
||||||
for(var i in knownFileEndings)
|
for(var i in knownFileEndings)
|
||||||
|
@ -94,7 +96,7 @@ exports.doImport = function(req, res, padId)
|
||||||
fileEndingKnown = true;
|
fileEndingKnown = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//if the file ending is known, continue as normal
|
//if the file ending is known, continue as normal
|
||||||
if(fileEndingKnown)
|
if(fileEndingKnown)
|
||||||
{
|
{
|
||||||
|
@ -105,11 +107,11 @@ exports.doImport = function(req, res, padId)
|
||||||
{
|
{
|
||||||
var oldSrcFile = srcFile;
|
var oldSrcFile = srcFile;
|
||||||
srcFile = srcFile.split(".")[0] + ".txt";
|
srcFile = srcFile.split(".")[0] + ".txt";
|
||||||
|
|
||||||
fs.rename(oldSrcFile, srcFile, callback);
|
fs.rename(oldSrcFile, srcFile, callback);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
//convert file to text
|
//convert file to text
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -117,7 +119,7 @@ exports.doImport = function(req, res, padId)
|
||||||
destFile = tempDirectory + "eplite_import_" + randNum + ".txt";
|
destFile = tempDirectory + "eplite_import_" + randNum + ".txt";
|
||||||
abiword.convertFile(srcFile, destFile, "txt", callback);
|
abiword.convertFile(srcFile, destFile, "txt", callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
//get the pad object
|
//get the pad object
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +130,7 @@ exports.doImport = function(req, res, padId)
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
//read the text
|
//read the text
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -136,30 +138,30 @@ exports.doImport = function(req, res, padId)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
text = _text;
|
text = _text;
|
||||||
|
|
||||||
//node on windows has a delay on releasing of the file lock.
|
//node on windows has a delay on releasing of the file lock.
|
||||||
//We add a 100ms delay to work around this
|
//We add a 100ms delay to work around this
|
||||||
if(os.type().indexOf("Windows") > -1)
|
if(os.type().indexOf("Windows") > -1)
|
||||||
{
|
{
|
||||||
setTimeout(function()
|
setTimeout(function()
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
//change text of the pad and broadcast the changeset
|
//change text of the pad and broadcast the changeset
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
pad.setText(text);
|
pad.setText(text);
|
||||||
padMessageHandler.updatePadClients(pad, callback);
|
padMessageHandler.updatePadClients(pad, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
//clean up temporary files
|
//clean up temporary files
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -184,8 +186,8 @@ exports.doImport = function(req, res, padId)
|
||||||
}
|
}
|
||||||
|
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
//close the connection
|
//close the connection
|
||||||
res.send("ok");
|
res.send("ok");
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
@ -61,18 +61,18 @@ var socketio;
|
||||||
exports.setSocketIO = function(socket_io)
|
exports.setSocketIO = function(socket_io)
|
||||||
{
|
{
|
||||||
socketio=socket_io;
|
socketio=socket_io;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the connection of a new user
|
* Handles the connection of a new user
|
||||||
* @param client the new client
|
* @param client the new client
|
||||||
*/
|
*/
|
||||||
exports.handleConnect = function(client)
|
exports.handleConnect = function(client)
|
||||||
{
|
{
|
||||||
//Initalize session2pad and sessioninfos for this new session
|
//Initalize session2pad and sessioninfos for this new session
|
||||||
session2pad[client.id]=null;
|
session2pad[client.id]=null;
|
||||||
sessioninfos[client.id]={};
|
sessioninfos[client.id]={};
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kicks all sessions from a pad
|
* Kicks all sessions from a pad
|
||||||
|
@ -89,27 +89,27 @@ exports.kickSessionsFromPad = function(padID)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[padID][i]].json.send({disconnect:"deleted"});
|
socketio.sockets.sockets[pad2sessions[padID][i]].json.send({disconnect:"deleted"});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the disconnection of a user
|
* Handles the disconnection of a user
|
||||||
* @param client the client that leaves
|
* @param client the client that leaves
|
||||||
*/
|
*/
|
||||||
exports.handleDisconnect = function(client)
|
exports.handleDisconnect = function(client)
|
||||||
{
|
{
|
||||||
//save the padname of this session
|
//save the padname of this session
|
||||||
var sessionPad=session2pad[client.id];
|
var sessionPad=session2pad[client.id];
|
||||||
|
|
||||||
//if this connection was already etablished with a handshake, send a disconnect message to the others
|
//if this connection was already etablished with a handshake, send a disconnect message to the others
|
||||||
if(sessioninfos[client.id] && sessioninfos[client.id].author)
|
if(sessioninfos[client.id] && sessioninfos[client.id].author)
|
||||||
{
|
{
|
||||||
var author = sessioninfos[client.id].author;
|
var author = sessioninfos[client.id].author;
|
||||||
|
|
||||||
//get the author color out of the db
|
//get the author color out of the db
|
||||||
authorManager.getAuthorColorId(author, function(err, color)
|
authorManager.getAuthorColorId(author, function(err, color)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
//prepare the notification for the other users on the pad, that this user left
|
//prepare the notification for the other users on the pad, that this user left
|
||||||
var messageToTheOtherUsers = {
|
var messageToTheOtherUsers = {
|
||||||
"type": "COLLABROOM",
|
"type": "COLLABROOM",
|
||||||
|
@ -123,29 +123,29 @@ exports.handleDisconnect = function(client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
||||||
for(i in pad2sessions[sessionPad])
|
for(var i in pad2sessions[sessionPad])
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[sessionPad][i]].json.send(messageToTheOtherUsers);
|
socketio.sockets.sockets[pad2sessions[sessionPad][i]].json.send(messageToTheOtherUsers);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Go trough all sessions of this pad, search and destroy the entry of this client
|
//Go trough all sessions of this pad, search and destroy the entry of this client
|
||||||
for(i in pad2sessions[sessionPad])
|
for(var i in pad2sessions[sessionPad])
|
||||||
{
|
{
|
||||||
if(pad2sessions[sessionPad][i] == client.id)
|
if(pad2sessions[sessionPad][i] == client.id)
|
||||||
{
|
{
|
||||||
pad2sessions[sessionPad].splice(i, 1);
|
pad2sessions[sessionPad].splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete the session2pad and sessioninfos entrys of this session
|
//Delete the session2pad and sessioninfos entrys of this session
|
||||||
delete session2pad[client.id];
|
delete session2pad[client.id];
|
||||||
delete sessioninfos[client.id];
|
delete sessioninfos[client.id];
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a message from a user
|
* Handles a message from a user
|
||||||
|
@ -153,8 +153,8 @@ exports.handleDisconnect = function(client)
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
exports.handleMessage = function(client, message)
|
exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
if(message == null)
|
if(!message)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Message is null!");
|
messageLogger.warn("Message is null!");
|
||||||
return;
|
return;
|
||||||
|
@ -164,39 +164,50 @@ exports.handleMessage = function(client, message)
|
||||||
messageLogger.warn("Message has no type attribute!");
|
messageLogger.warn("Message has no type attribute!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check what type of message we get and delegate to the other methodes
|
//Check what type of message we get and delegate to the other methodes
|
||||||
if(message.type == "CLIENT_READY")
|
if(message.type == "CLIENT_READY")
|
||||||
{
|
{
|
||||||
handleClientReady(client, message);
|
handleClientReady(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "USER_CHANGES")
|
message.data.type == "USER_CHANGES")
|
||||||
{
|
{
|
||||||
handleUserChanges(client, message);
|
handleUserChanges(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "USERINFO_UPDATE")
|
message.data.type == "USERINFO_UPDATE")
|
||||||
{
|
{
|
||||||
handleUserInfoUpdate(client, message);
|
handleUserInfoUpdate(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "CHAT_MESSAGE")
|
message.data.type == "CHAT_MESSAGE")
|
||||||
{
|
{
|
||||||
handleChatMessage(client, message);
|
handleChatMessage(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "CLIENT_MESSAGE" &&
|
message.data.type == "CLIENT_MESSAGE")
|
||||||
message.data.payload.type == "suggestUserName")
|
|
||||||
{
|
{
|
||||||
handleSuggestUserName(client, message);
|
if(message.data.payload.type == "suggestUserName")
|
||||||
|
{
|
||||||
|
handleSuggestUserName(client, message);
|
||||||
|
}
|
||||||
|
else if (message.data.payload.type == "padoptions")
|
||||||
|
{
|
||||||
|
messageLogger.info("Handler not implemented for payload type: " + message.data.payload.type);
|
||||||
|
//handlePadOptions
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
messageLogger.warn("Dropped COLLABROOM CLIENT_MESSAGE message, unknown Message Type " + message.type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//if the message type is unknown, throw an exception
|
//if the message type is unknown, throw an exception
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, unknown Message Type " + message.type);
|
messageLogger.warn("Dropped message, unknown Message Type " + message.type);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Chat Message
|
* Handles a Chat Message
|
||||||
|
@ -209,10 +220,10 @@ function handleChatMessage(client, message)
|
||||||
var userId = sessioninfos[client.id].author;
|
var userId = sessioninfos[client.id].author;
|
||||||
var text = message.data.text;
|
var text = message.data.text;
|
||||||
var padId = session2pad[client.id];
|
var padId = session2pad[client.id];
|
||||||
|
|
||||||
var pad;
|
var pad;
|
||||||
var userName;
|
var userName;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -238,7 +249,7 @@ function handleChatMessage(client, message)
|
||||||
{
|
{
|
||||||
//save the chat message
|
//save the chat message
|
||||||
pad.appendChatMessage(text, userId, time);
|
pad.appendChatMessage(text, userId, time);
|
||||||
|
|
||||||
var msg = {
|
var msg = {
|
||||||
type: "COLLABROOM",
|
type: "COLLABROOM",
|
||||||
data: {
|
data: {
|
||||||
|
@ -249,13 +260,13 @@ function handleChatMessage(client, message)
|
||||||
text: text
|
text: text
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//broadcast the chat message to everyone on the pad
|
//broadcast the chat message to everyone on the pad
|
||||||
for(var i in pad2sessions[padId])
|
for(var i in pad2sessions[padId])
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(msg);
|
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -273,19 +284,19 @@ function handleChatMessage(client, message)
|
||||||
function handleSuggestUserName(client, message)
|
function handleSuggestUserName(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.payload.newName == null)
|
if(!message.data.payload.newName)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, suggestUserName Message has no newName!");
|
messageLogger.warn("Dropped message, suggestUserName Message has no newName!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.payload.unnamedId == null)
|
if(!message.data.payload.unnamedId)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!");
|
messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var padId = session2pad[client.id];
|
var padId = session2pad[client.id];
|
||||||
|
|
||||||
//search the author and send him this message
|
//search the author and send him this message
|
||||||
for(var i in pad2sessions[padId])
|
for(var i in pad2sessions[padId])
|
||||||
{
|
{
|
||||||
|
@ -305,30 +316,30 @@ function handleSuggestUserName(client, message)
|
||||||
function handleUserInfoUpdate(client, message)
|
function handleUserInfoUpdate(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.userInfo.colorId == null)
|
if(!message.data.userInfo.colorId)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!");
|
messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Find out the author name of this session
|
//Find out the author name of this session
|
||||||
var author = sessioninfos[client.id].author;
|
var author = sessioninfos[client.id].author;
|
||||||
|
|
||||||
//Tell the authorManager about the new attributes
|
//Tell the authorManager about the new attributes
|
||||||
authorManager.setAuthorColorId(author, message.data.userInfo.colorId);
|
authorManager.setAuthorColorId(author, message.data.userInfo.colorId);
|
||||||
authorManager.setAuthorName(author, message.data.userInfo.name);
|
authorManager.setAuthorName(author, message.data.userInfo.name);
|
||||||
|
|
||||||
var padId = session2pad[client.id];
|
var padId = session2pad[client.id];
|
||||||
|
|
||||||
//set a null name, when there is no name set. cause the client wants it null
|
//set a null name, when there is no name set. cause the client wants it null
|
||||||
if(message.data.userInfo.name == null)
|
if(!message.data.userInfo.name)
|
||||||
{
|
{
|
||||||
message.data.userInfo.name = null;
|
message.data.userInfo.name = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//The Client don't know about a USERINFO_UPDATE, it can handle only new user_newinfo, so change the message type
|
//The Client don't know about a USERINFO_UPDATE, it can handle only new user_newinfo, so change the message type
|
||||||
message.data.type = "USER_NEWINFO";
|
message.data.type = "USER_NEWINFO";
|
||||||
|
|
||||||
//Send the other clients on the pad the update message
|
//Send the other clients on the pad the update message
|
||||||
for(var i in pad2sessions[padId])
|
for(var i in pad2sessions[padId])
|
||||||
{
|
{
|
||||||
|
@ -349,29 +360,29 @@ function handleUserInfoUpdate(client, message)
|
||||||
function handleUserChanges(client, message)
|
function handleUserChanges(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.baseRev == null)
|
if(message.data.baseRev === null || message.data.baseRev === undefined)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, USER_CHANGES Message has no baseRev!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no baseRev!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.apool == null)
|
if(!message.data.apool)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, USER_CHANGES Message has no apool!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no apool!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.changeset == null)
|
if(!message.data.changeset)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all Vars we need
|
//get all Vars we need
|
||||||
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 r, apool, pad;
|
var r, apool, pad;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -387,13 +398,13 @@ function handleUserChanges(client, message)
|
||||||
function(callback)
|
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
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//this looks like a changeset check, it throws errors sometimes
|
//this looks like a changeset check, it throws errors sometimes
|
||||||
Changeset.checkRep(changeset);
|
Changeset.checkRep(changeset);
|
||||||
|
|
||||||
Changeset.eachAttribNumber(changeset, function(n) {
|
Changeset.eachAttribNumber(changeset, function(n) {
|
||||||
if (! wireApool.getAttrib(n)) {
|
if (! wireApool.getAttrib(n)) {
|
||||||
throw "Attribute pool is missing attribute "+n+" for changeset "+changeset;
|
throw "Attribute pool is missing attribute "+n+" for changeset "+changeset;
|
||||||
|
@ -407,27 +418,27 @@ function handleUserChanges(client, message)
|
||||||
client.json.send({disconnect:"badChangeset"});
|
client.json.send({disconnect:"badChangeset"});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//ex. adoptChangesetAttribs
|
//ex. adoptChangesetAttribs
|
||||||
|
|
||||||
//Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
|
//Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
|
||||||
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
|
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
|
||||||
|
|
||||||
//ex. applyUserChanges
|
//ex. applyUserChanges
|
||||||
apool = pad.pool;
|
apool = pad.pool;
|
||||||
r = baseRev;
|
r = baseRev;
|
||||||
|
|
||||||
//https://github.com/caolan/async#whilst
|
//https://github.com/caolan/async#whilst
|
||||||
async.whilst(
|
async.whilst(
|
||||||
function() { return r < pad.getHeadRevisionNumber(); },
|
function() { return r < pad.getHeadRevisionNumber(); },
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
r++;
|
r++;
|
||||||
|
|
||||||
pad.getRevisionChangeset(r, function(err, c)
|
pad.getRevisionChangeset(r, function(err, c)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
changeset = Changeset.follow(c, changeset, false, apool);
|
changeset = Changeset.follow(c, changeset, false, apool);
|
||||||
callback(null);
|
callback(null);
|
||||||
});
|
});
|
||||||
|
@ -440,29 +451,29 @@ function handleUserChanges(client, message)
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
var prevText = pad.text();
|
var prevText = pad.text();
|
||||||
|
|
||||||
if (Changeset.oldLen(changeset) != prevText.length)
|
if (Changeset.oldLen(changeset) != prevText.length)
|
||||||
{
|
{
|
||||||
console.warn("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length);
|
console.warn("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length);
|
||||||
client.json.send({disconnect:"badChangeset"});
|
client.json.send({disconnect:"badChangeset"});
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var thisAuthor = sessioninfos[client.id].author;
|
var thisAuthor = sessioninfos[client.id].author;
|
||||||
|
|
||||||
pad.appendRevision(changeset, thisAuthor);
|
pad.appendRevision(changeset, thisAuthor);
|
||||||
|
|
||||||
var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool);
|
var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool);
|
||||||
if (correctionChangeset) {
|
if (correctionChangeset) {
|
||||||
pad.appendRevision(correctionChangeset);
|
pad.appendRevision(correctionChangeset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pad.text().lastIndexOf("\n\n") != pad.text().length-2) {
|
if (pad.text().lastIndexOf("\n\n") != pad.text().length-2) {
|
||||||
var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n");
|
var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n");
|
||||||
pad.appendRevision(nlChangeset);
|
pad.appendRevision(nlChangeset);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updatePadClients(pad, callback);
|
exports.updatePadClients(pad, callback);
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -472,29 +483,29 @@ function handleUserChanges(client, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updatePadClients = function(pad, callback)
|
exports.updatePadClients = function(pad, callback)
|
||||||
{
|
{
|
||||||
//skip this step if noone is on this pad
|
//skip this step if noone is on this pad
|
||||||
if(!pad2sessions[pad.id])
|
if(!pad2sessions[pad.id])
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//go trough all sessions on this pad
|
//go trough all sessions on this pad
|
||||||
async.forEach(pad2sessions[pad.id], function(session, callback)
|
async.forEach(pad2sessions[pad.id], function(session, callback)
|
||||||
{
|
{
|
||||||
var lastRev = sessioninfos[session].rev;
|
var lastRev = sessioninfos[session].rev;
|
||||||
|
|
||||||
//https://github.com/caolan/async#whilst
|
//https://github.com/caolan/async#whilst
|
||||||
//send them all new changesets
|
//send them all new changesets
|
||||||
async.whilst(
|
async.whilst(
|
||||||
function (){ return lastRev < pad.getHeadRevisionNumber()},
|
function (){ return lastRev < pad.getHeadRevisionNumber(); },
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var author, revChangeset;
|
var author, revChangeset;
|
||||||
|
|
||||||
var r = ++lastRev;
|
var r = ++lastRev;
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
|
@ -517,7 +528,7 @@ exports.updatePadClients = function(pad, callback)
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
if(author == sessioninfos[session].author)
|
if(author == sessioninfos[session].author)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
||||||
|
@ -528,20 +539,20 @@ exports.updatePadClients = function(pad, callback)
|
||||||
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,
|
||||||
author: author}};
|
author: author}};
|
||||||
|
|
||||||
socketio.sockets.sockets[session].json.send(wireMsg);
|
socketio.sockets.sockets[session].json.send(wireMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
|
|
||||||
sessioninfos[session].rev = pad.getHeadRevisionNumber();
|
sessioninfos[session].rev = pad.getHeadRevisionNumber();
|
||||||
},callback);
|
},callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copied from the Etherpad Source Code. Don't know what this methode does excatly...
|
* Copied from the Etherpad Source Code. Don't know what this methode does excatly...
|
||||||
|
@ -570,7 +581,7 @@ function _correctMarkersInPad(atext, apool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (badMarkers.length == 0) {
|
if (badMarkers.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -586,7 +597,7 @@ function _correctMarkersInPad(atext, apool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token
|
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token
|
||||||
* and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad
|
* and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad
|
||||||
* @param client the client that send this message
|
* @param client the client that send this message
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
|
@ -630,7 +641,7 @@ function handleClientReady(client, message)
|
||||||
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//access was granted
|
//access was granted
|
||||||
if(statusObject.accessStatus == "grant")
|
if(statusObject.accessStatus == "grant")
|
||||||
{
|
{
|
||||||
|
@ -640,10 +651,10 @@ function handleClientReady(client, message)
|
||||||
//no access, send the client a message that tell him why
|
//no access, send the client a message that tell him why
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
client.json.send({accessStatus: statusObject.accessStatus})
|
client.json.send({accessStatus: statusObject.accessStatus});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//get all authordata of this new user
|
//get all authordata of this new user
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -692,7 +703,7 @@ function handleClientReady(client, message)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var authors = pad.getAllAuthors();
|
var authors = pad.getAllAuthors();
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
//get all author data out of the database
|
//get all author data out of the database
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -719,8 +730,8 @@ function handleClientReady(client, message)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -735,33 +746,33 @@ function handleClientReady(client, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save in session2pad that this session belonges to this pad
|
//Save in session2pad that this session belonges to this pad
|
||||||
var sessionId=String(client.id);
|
var sessionId=String(client.id);
|
||||||
session2pad[sessionId] = message.padId;
|
session2pad[sessionId] = message.padId;
|
||||||
|
|
||||||
//check if there is already a pad2sessions entry, if not, create one
|
//check if there is already a pad2sessions entry, if not, create one
|
||||||
if(!pad2sessions[message.padId])
|
if(!pad2sessions[message.padId])
|
||||||
{
|
{
|
||||||
pad2sessions[message.padId] = [];
|
pad2sessions[message.padId] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
//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);
|
||||||
|
|
||||||
//prepare all values for the wire
|
//prepare all values for the wire
|
||||||
var atext = Changeset.cloneAText(pad.atext);
|
var atext = Changeset.cloneAText(pad.atext);
|
||||||
var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);
|
var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);
|
||||||
var apool = attribsForWire.pool.toJsonable();
|
var apool = attribsForWire.pool.toJsonable();
|
||||||
atext.attribs = attribsForWire.translated;
|
atext.attribs = attribsForWire.translated;
|
||||||
|
|
||||||
//check if abiword is avaiable
|
//check if abiword is avaiable
|
||||||
var abiwordAvailable = settings.abiword != null ? "yes" : "no";
|
var abiwordAvailable = settings.abiword ? "yes" : "no";
|
||||||
if(settings.abiword != null && os.type().indexOf("Windows") != -1)
|
if(settings.abiword && os.type().indexOf("Windows") != -1)
|
||||||
{
|
{
|
||||||
abiwordAvailable = "withoutPDF";
|
abiwordAvailable = "withoutPDF";
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientVars = {
|
var clientVars = {
|
||||||
"accountPrivs": {
|
"accountPrivs": {
|
||||||
"maxRevisions": 100
|
"maxRevisions": 100
|
||||||
|
@ -798,20 +809,20 @@ function handleClientReady(client, message)
|
||||||
"fullWidth": false,
|
"fullWidth": false,
|
||||||
"hideSidebar": false
|
"hideSidebar": false
|
||||||
},
|
},
|
||||||
"abiwordAvailable": abiwordAvailable,
|
"abiwordAvailable": abiwordAvailable,
|
||||||
"hooks": {}
|
"hooks": {}
|
||||||
}
|
};
|
||||||
|
|
||||||
//Add a username to the clientVars if one avaiable
|
//Add a username to the clientVars if one avaiable
|
||||||
if(authorName != null)
|
if(authorName)
|
||||||
{
|
{
|
||||||
clientVars.userName = authorName;
|
clientVars.userName = authorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sessioninfos[client.id] !== undefined)
|
if(sessioninfos[client.id] !== undefined)
|
||||||
{
|
{
|
||||||
//This is a reconnect, so we don't have to send the client the ClientVars again
|
//This is a reconnect, so we don't have to send the client the ClientVars again
|
||||||
if(message.reconnect == true)
|
if(message.reconnect)
|
||||||
{
|
{
|
||||||
//Save the revision in sessioninfos, we take the revision from the info the client send to us
|
//Save the revision in sessioninfos, we take the revision from the info the client send to us
|
||||||
sessioninfos[client.id].rev = message.client_rev;
|
sessioninfos[client.id].rev = message.client_rev;
|
||||||
|
@ -824,11 +835,11 @@ function handleClientReady(client, message)
|
||||||
//Save the revision in sessioninfos
|
//Save the revision in sessioninfos
|
||||||
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save the revision and the author id in sessioninfos
|
//Save the revision and the author id in sessioninfos
|
||||||
sessioninfos[client.id].author = author;
|
sessioninfos[client.id].author = author;
|
||||||
}
|
}
|
||||||
|
|
||||||
//prepare the notification for the other users on the pad, that this user joined
|
//prepare the notification for the other users on the pad, that this user joined
|
||||||
var messageToTheOtherUsers = {
|
var messageToTheOtherUsers = {
|
||||||
"type": "COLLABROOM",
|
"type": "COLLABROOM",
|
||||||
|
@ -842,18 +853,18 @@ function handleClientReady(client, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Add the authorname of this new User, if avaiable
|
//Add the authorname of this new User, if avaiable
|
||||||
if(authorName != null)
|
if(authorName)
|
||||||
{
|
{
|
||||||
messageToTheOtherUsers.data.userInfo.name = authorName;
|
messageToTheOtherUsers.data.userInfo.name = authorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Run trough all sessions of this pad
|
//Run trough all sessions of this pad
|
||||||
async.forEach(pad2sessions[message.padId], function(sessionID, callback)
|
async.forEach(pad2sessions[message.padId], function(sessionID, callback)
|
||||||
{
|
{
|
||||||
var sessionAuthorName, sessionAuthorColorId;
|
var sessionAuthorName, sessionAuthorColorId;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the authorname & colorId
|
//get the authorname & colorId
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -866,7 +877,7 @@ function handleClientReady(client, message)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
sessionAuthorColorId = value;
|
sessionAuthorColorId = value;
|
||||||
callback();
|
callback();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -875,10 +886,10 @@ function handleClientReady(client, message)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
sessionAuthorName = value;
|
sessionAuthorName = value;
|
||||||
callback();
|
callback();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
],callback);
|
],callback);
|
||||||
},
|
},
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
//Jump over, if this session is the connection session
|
//Jump over, if this session is the connection session
|
||||||
|
@ -886,7 +897,7 @@ function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
//Send this Session the Notification about the new user
|
//Send this Session the Notification about the new user
|
||||||
socketio.sockets.sockets[sessionID].json.send(messageToTheOtherUsers);
|
socketio.sockets.sockets[sessionID].json.send(messageToTheOtherUsers);
|
||||||
|
|
||||||
//Send the new User a Notification about this other user
|
//Send the new User a Notification about this other user
|
||||||
var messageToNotifyTheClientAboutTheOthers = {
|
var messageToNotifyTheClientAboutTheOthers = {
|
||||||
"type": "COLLABROOM",
|
"type": "COLLABROOM",
|
||||||
|
@ -904,7 +915,7 @@ function handleClientReady(client, message)
|
||||||
client.json.send(messageToNotifyTheClientAboutTheOthers);
|
client.json.send(messageToNotifyTheClientAboutTheOthers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
}, callback);
|
}, callback);
|
||||||
}
|
}
|
||||||
],function(err)
|
],function(err)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* This is the Socket.IO Router. It routes the Messages between the
|
* This is the Socket.IO Router. It routes the Messages between the
|
||||||
* components of the Server. The components are at the moment: pad and timeslider
|
* components of the Server. The components are at the moment: pad and timeslider
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -28,11 +28,11 @@ var securityManager = require("../db/SecurityManager");
|
||||||
* Saves all components
|
* Saves all components
|
||||||
* key is the component name
|
* key is the component name
|
||||||
* value is the component module
|
* value is the component module
|
||||||
*/
|
*/
|
||||||
var components = {};
|
var components = {};
|
||||||
|
|
||||||
var socket;
|
var socket;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adds a component
|
* adds a component
|
||||||
*/
|
*/
|
||||||
|
@ -40,10 +40,10 @@ exports.addComponent = function(moduleName, module)
|
||||||
{
|
{
|
||||||
//save the component
|
//save the component
|
||||||
components[moduleName] = module;
|
components[moduleName] = module;
|
||||||
|
|
||||||
//give the module the socket
|
//give the module the socket
|
||||||
module.setSocketIO(socket);
|
module.setSocketIO(socket);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the socket.io and adds event functions for routing
|
* sets the socket.io and adds event functions for routing
|
||||||
|
@ -52,31 +52,31 @@ exports.setSocketIO = function(_socket)
|
||||||
{
|
{
|
||||||
//save this socket internaly
|
//save this socket internaly
|
||||||
socket = _socket;
|
socket = _socket;
|
||||||
|
|
||||||
socket.sockets.on('connection', function(client)
|
socket.sockets.on('connection', function(client)
|
||||||
{
|
{
|
||||||
var clientAuthorized = false;
|
var clientAuthorized = false;
|
||||||
|
|
||||||
//wrap the original send function to log the messages
|
//wrap the original send function to log the messages
|
||||||
client._send = client.send;
|
client._send = client.send;
|
||||||
client.send = function(message)
|
client.send = function(message)
|
||||||
{
|
{
|
||||||
messageLogger.info("to " + client.id + ": " + stringifyWithoutPassword(message));
|
messageLogger.info("to " + client.id + ": " + stringifyWithoutPassword(message));
|
||||||
client._send(message);
|
client._send(message);
|
||||||
}
|
};
|
||||||
|
|
||||||
//tell all components about this connect
|
//tell all components about this connect
|
||||||
for(var i in components)
|
for(var i in components)
|
||||||
{
|
{
|
||||||
components[i].handleConnect(client);
|
components[i].handleConnect(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
//try to handle the message of this client
|
//try to handle the message of this client
|
||||||
function handleMessage(message)
|
function handleMessage(message)
|
||||||
{
|
{
|
||||||
if(message.component && components[message.component])
|
if(message.component && components[message.component])
|
||||||
{
|
{
|
||||||
//check if component is registered in the components array
|
//check if component is registered in the components array
|
||||||
if(components[message.component])
|
if(components[message.component])
|
||||||
{
|
{
|
||||||
messageLogger.info("from " + client.id + ": " + stringifyWithoutPassword(message));
|
messageLogger.info("from " + client.id + ": " + stringifyWithoutPassword(message));
|
||||||
|
@ -87,8 +87,8 @@ exports.setSocketIO = function(_socket)
|
||||||
{
|
{
|
||||||
messageLogger.error("Can't route the message:" + stringifyWithoutPassword(message));
|
messageLogger.error("Can't route the message:" + stringifyWithoutPassword(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.on('message', function(message)
|
client.on('message', function(message)
|
||||||
{
|
{
|
||||||
if(message.protocolVersion && message.protocolVersion != 2)
|
if(message.protocolVersion && message.protocolVersion != 2)
|
||||||
|
@ -111,7 +111,7 @@ exports.setSocketIO = function(_socket)
|
||||||
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
//access was granted, mark the client as authorized and handle the message
|
//access was granted, mark the client as authorized and handle the message
|
||||||
if(statusObject.accessStatus == "grant")
|
if(statusObject.accessStatus == "grant")
|
||||||
{
|
{
|
||||||
|
@ -143,21 +143,21 @@ exports.setSocketIO = function(_socket)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
//returns a stringified representation of a message, removes the password
|
//returns a stringified representation of a message, removes the password
|
||||||
//this ensures there are no passwords in the log
|
//this ensures there are no passwords in the log
|
||||||
function stringifyWithoutPassword(message)
|
function stringifyWithoutPassword(message)
|
||||||
{
|
{
|
||||||
var newMessage = {};
|
var newMessage = {};
|
||||||
|
|
||||||
for(var i in message)
|
for(var i in message)
|
||||||
{
|
{
|
||||||
if(i == "password" && message[i] != null)
|
if(i == "password" && message[i])
|
||||||
newMessage["password"] = "xxx";
|
newMessage.password = "xxx";
|
||||||
else
|
else
|
||||||
newMessage[i]=message[i];
|
newMessage[i]=message[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return JSON.stringify(newMessage);
|
return JSON.stringify(newMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
@ -39,7 +39,7 @@ var socketio;
|
||||||
exports.setSocketIO = function(socket_io)
|
exports.setSocketIO = function(socket_io)
|
||||||
{
|
{
|
||||||
socketio=socket_io;
|
socketio=socket_io;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the connection of a new user
|
* Handles the connection of a new user
|
||||||
|
@ -48,7 +48,7 @@ exports.setSocketIO = function(socket_io)
|
||||||
exports.handleConnect = function(client)
|
exports.handleConnect = function(client)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the disconnection of a user
|
* Handles the disconnection of a user
|
||||||
|
@ -56,8 +56,8 @@ exports.handleConnect = function(client)
|
||||||
*/
|
*/
|
||||||
exports.handleDisconnect = function(client)
|
exports.handleDisconnect = function(client)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a message from a user
|
* Handles a message from a user
|
||||||
|
@ -65,7 +65,7 @@ exports.handleDisconnect = function(client)
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
exports.handleMessage = function(client, message)
|
exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
//Check what type of message we get and delegate to the other methodes
|
//Check what type of message we get and delegate to the other methodes
|
||||||
if(message.type == "CLIENT_READY")
|
if(message.type == "CLIENT_READY")
|
||||||
{
|
{
|
||||||
|
@ -80,70 +80,70 @@ exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, unknown Message Type: '" + message.type + "'");
|
messageLogger.warn("Dropped message, unknown Message Type: '" + message.type + "'");
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function handleClientReady(client, message)
|
function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
if(message.padId == null)
|
if(!message.padId)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, changeset request has no padId!");
|
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//send the timeslider client the clientVars, with this values its able to start
|
//send the timeslider client the clientVars, with this values its able to start
|
||||||
createTimesliderClientVars (message.padId, function(err, clientVars)
|
createTimesliderClientVars (message.padId, function(err, clientVars)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
client.json.send({type: "CLIENT_VARS", data: clientVars});
|
client.json.send({type: "CLIENT_VARS", data: clientVars});
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a request for a rough changeset, the timeslider client needs it
|
* Handles a request for a rough changeset, the timeslider client needs it
|
||||||
*/
|
*/
|
||||||
function handleChangesetRequest(client, message)
|
function handleChangesetRequest(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data == null)
|
if(!message.data)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, changeset request has no data!");
|
messageLogger.warn("Dropped message, changeset request has no data!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.padId == null)
|
if(!message.padId)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, changeset request has no padId!");
|
messageLogger.warn("Dropped message, changeset request has no padId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.granularity == null)
|
if(!message.data.granularity)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
messageLogger.warn("Dropped message, changeset request has no granularity!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.start == null)
|
if(!message.data.start)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, changeset request has no start!");
|
messageLogger.warn("Dropped message, changeset request has no start!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(message.data.requestID == null)
|
if(!message.data.requestID)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var granularity = message.data.granularity;
|
var granularity = message.data.granularity;
|
||||||
var start = message.data.start;
|
var start = message.data.start;
|
||||||
var end = start + (100 * granularity);
|
var end = start + (100 * granularity);
|
||||||
var padId = message.padId;
|
var padId = message.padId;
|
||||||
|
|
||||||
//build the requested rough changesets and send them back
|
//build the requested rough changesets and send them back
|
||||||
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
|
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
var data = changesetInfo;
|
var data = changesetInfo;
|
||||||
data.requestID = message.data.requestID;
|
data.requestID = message.data.requestID;
|
||||||
|
|
||||||
client.json.send({type: "CHANGESET_REQ", data: data});
|
client.json.send({type: "CHANGESET_REQ", data: data});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -171,19 +171,19 @@ function createTimesliderClientVars (padId, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//get all authors and add them to
|
//get all authors and add them to
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var historicalAuthorData = {};
|
var historicalAuthorData = {};
|
||||||
//get all authors out of the attribut pool
|
//get all authors out of the attribut pool
|
||||||
var authors = pad.getAllAuthors();
|
var authors = pad.getAllAuthors();
|
||||||
|
|
||||||
//get all author data out of the database
|
//get all author data out of the database
|
||||||
async.forEach(authors, function(authorId, callback)
|
async.forEach(authors, function(authorId, callback)
|
||||||
{
|
{
|
||||||
|
@ -216,28 +216,28 @@ function createTimesliderClientVars (padId, callback)
|
||||||
{
|
{
|
||||||
//get the head revision Number
|
//get the head revision Number
|
||||||
var lastRev = pad.getHeadRevisionNumber();
|
var lastRev = pad.getHeadRevisionNumber();
|
||||||
|
|
||||||
//add the revNum to the client Vars
|
//add the revNum to the client Vars
|
||||||
clientVars.revNum = lastRev;
|
clientVars.revNum = lastRev;
|
||||||
clientVars.totalRevs = lastRev;
|
clientVars.totalRevs = lastRev;
|
||||||
|
|
||||||
var atext = Changeset.cloneAText(pad.atext);
|
var atext = Changeset.cloneAText(pad.atext);
|
||||||
var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);
|
var attribsForWire = Changeset.prepareForWire(atext.attribs, pad.pool);
|
||||||
var apool = attribsForWire.pool.toJsonable();
|
var apool = attribsForWire.pool.toJsonable();
|
||||||
atext.attribs = attribsForWire.translated;
|
atext.attribs = attribsForWire.translated;
|
||||||
|
|
||||||
clientVars.initialStyledContents.apool = apool;
|
clientVars.initialStyledContents.apool = apool;
|
||||||
clientVars.initialStyledContents.atext = atext;
|
clientVars.initialStyledContents.atext = atext;
|
||||||
|
|
||||||
var granularities = [100, 10, 1];
|
var granularities = [100, 10, 1];
|
||||||
|
|
||||||
//get the latest rough changesets
|
//get the latest rough changesets
|
||||||
async.forEach(granularities, function(granularity, callback)
|
async.forEach(granularities, function(granularity, callback)
|
||||||
{
|
{
|
||||||
var topGranularity = granularity*10;
|
var topGranularity = granularity*10;
|
||||||
|
|
||||||
getChangesetInfo(padId, Math.floor(lastRev / topGranularity)*topGranularity,
|
getChangesetInfo(padId, Math.floor(lastRev / topGranularity)*topGranularity,
|
||||||
Math.floor(lastRev / topGranularity)*topGranularity+topGranularity, granularity,
|
Math.floor(lastRev / topGranularity)*topGranularity+topGranularity, granularity,
|
||||||
function(err, changeset)
|
function(err, changeset)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
@ -267,47 +267,47 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
var composedChangesets = {};
|
var composedChangesets = {};
|
||||||
var revisionDate = [];
|
var revisionDate = [];
|
||||||
var lines;
|
var lines;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad from the database
|
//get the pad from the database
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//calculate the last full endnum
|
//calculate the last full endnum
|
||||||
var lastRev = pad.getHeadRevisionNumber();
|
var lastRev = pad.getHeadRevisionNumber();
|
||||||
if (endNum > lastRev+1) {
|
if (endNum > lastRev+1) {
|
||||||
endNum = lastRev+1;
|
endNum = lastRev+1;
|
||||||
}
|
}
|
||||||
endNum = Math.floor(endNum / granularity)*granularity;
|
endNum = Math.floor(endNum / granularity)*granularity;
|
||||||
|
|
||||||
var compositesChangesetNeeded = [];
|
var compositesChangesetNeeded = [];
|
||||||
var revTimesNeeded = [];
|
var revTimesNeeded = [];
|
||||||
|
|
||||||
//figure out which composite Changeset and revTimes we need, to load them in bulk
|
//figure out which composite Changeset and revTimes we need, to load them in bulk
|
||||||
var compositeStart = startNum;
|
var compositeStart = startNum;
|
||||||
while (compositeStart < endNum)
|
while (compositeStart < endNum)
|
||||||
{
|
{
|
||||||
var compositeEnd = compositeStart + granularity;
|
var compositeEnd = compositeStart + granularity;
|
||||||
|
|
||||||
//add the composite Changeset we needed
|
//add the composite Changeset we needed
|
||||||
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
|
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
|
||||||
|
|
||||||
//add the t1 time we need
|
//add the t1 time we need
|
||||||
revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1);
|
revTimesNeeded.push(compositeStart === 0 ? 0 : compositeStart - 1);
|
||||||
//add the t2 time we need
|
//add the t2 time we need
|
||||||
revTimesNeeded.push(compositeEnd - 1);
|
revTimesNeeded.push(compositeEnd - 1);
|
||||||
|
|
||||||
compositeStart += granularity;
|
compositeStart += granularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all needed db values parallel
|
//get all needed db values parallel
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -344,58 +344,58 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
lines = _lines;
|
lines = _lines;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
},
|
},
|
||||||
//doesn't know what happens here excatly :/
|
//doesn't know what happens here excatly :/
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var compositeStart = startNum;
|
var compositeStart = startNum;
|
||||||
|
|
||||||
while (compositeStart < endNum)
|
while (compositeStart < endNum)
|
||||||
{
|
{
|
||||||
if (compositeStart + granularity > endNum)
|
if (compositeStart + granularity > endNum)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var compositeEnd = compositeStart + granularity;
|
var compositeEnd = compositeStart + granularity;
|
||||||
|
|
||||||
var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
|
var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
|
||||||
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
|
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
|
||||||
|
|
||||||
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
|
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
|
||||||
Changeset.mutateTextLines(forwards, lines.textlines);
|
Changeset.mutateTextLines(forwards, lines.textlines);
|
||||||
|
|
||||||
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
|
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
|
||||||
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
|
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
|
||||||
|
|
||||||
var t1, t2;
|
var t1, t2;
|
||||||
if (compositeStart == 0)
|
if (compositeStart === 0)
|
||||||
{
|
{
|
||||||
t1 = revisionDate[0];
|
t1 = revisionDate[0];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
t1 = revisionDate[compositeStart - 1];
|
t1 = revisionDate[compositeStart - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
t2 = revisionDate[compositeEnd - 1];
|
t2 = revisionDate[compositeEnd - 1];
|
||||||
|
|
||||||
timeDeltas.push(t2 - t1);
|
timeDeltas.push(t2 - t1);
|
||||||
forwardsChangesets.push(forwards2);
|
forwardsChangesets.push(forwards2);
|
||||||
backwardsChangesets.push(backwards2);
|
backwardsChangesets.push(backwards2);
|
||||||
|
|
||||||
compositeStart += granularity;
|
compositeStart += granularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
callback(null, {forwardsChangesets: forwardsChangesets,
|
callback(null, {forwardsChangesets: forwardsChangesets,
|
||||||
backwardsChangesets: backwardsChangesets,
|
backwardsChangesets: backwardsChangesets,
|
||||||
apool: apool.toJsonable(),
|
apool: apool.toJsonable(),
|
||||||
|
@ -410,7 +410,7 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
* Tries to rebuild the getPadLines function of the original Etherpad
|
* Tries to rebuild the getPadLines function of the original Etherpad
|
||||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
||||||
*/
|
*/
|
||||||
function getPadLines(padId, revNum, callback)
|
function getPadLines(padId, revNum, callback)
|
||||||
{
|
{
|
||||||
var atext;
|
var atext;
|
||||||
var result = {};
|
var result = {};
|
||||||
|
@ -421,7 +421,7 @@ function getPadLines(padId, revNum, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
callback();
|
callback();
|
||||||
|
@ -473,7 +473,7 @@ function composePadChangesets(padId, startNum, endNum, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
callback();
|
callback();
|
||||||
|
@ -483,14 +483,14 @@ function composePadChangesets(padId, startNum, endNum, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var changesetsNeeded=[];
|
var changesetsNeeded=[];
|
||||||
|
|
||||||
//create a array for all changesets, we will
|
//create a array for all changesets, we will
|
||||||
//replace the values with the changeset later
|
//replace the values with the changeset later
|
||||||
for(var r=startNum;r<endNum;r++)
|
for(var r=startNum;r<endNum;r++)
|
||||||
{
|
{
|
||||||
changesetsNeeded.push(r);
|
changesetsNeeded.push(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all changesets
|
//get all changesets
|
||||||
async.forEach(changesetsNeeded, function(revNum,callback)
|
async.forEach(changesetsNeeded, function(revNum,callback)
|
||||||
{
|
{
|
||||||
|
@ -507,13 +507,13 @@ function composePadChangesets(padId, startNum, endNum, callback)
|
||||||
{
|
{
|
||||||
changeset = changesets[startNum];
|
changeset = changesets[startNum];
|
||||||
var pool = pad.apool();
|
var pool = pad.apool();
|
||||||
|
|
||||||
for(var r=startNum+1;r<endNum;r++)
|
for(var r=startNum+1;r<endNum;r++)
|
||||||
{
|
{
|
||||||
var cs = changesets[r];
|
var cs = changesets[r];
|
||||||
changeset = Changeset.compose(changeset, cs, pool);
|
changeset = Changeset.compose(changeset, cs, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
181
node/server.js
181
node/server.js
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
|
* This module is started with bin/run.sh. It sets up a Express HTTP and a Socket.IO Server.
|
||||||
* Static file Requests are answered directly from this module, Socket.IO messages are passed
|
* Static file Requests are answered directly from this module, Socket.IO messages are passed
|
||||||
* to MessageHandler and minfied requests are passed to minified.
|
* to MessageHandler and minfied requests are passed to minified.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -51,12 +51,12 @@ try
|
||||||
version = version.substring(0, 7);
|
version = version.substring(0, 7);
|
||||||
console.log("Your Etherpad Lite git version is " + version);
|
console.log("Your Etherpad Lite git version is " + version);
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
console.warn("Can't get git version for server header\n" + e.message)
|
console.warn("Can't get git version for server header\n" + e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues")
|
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues");
|
||||||
|
|
||||||
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ async.waterfall([
|
||||||
{
|
{
|
||||||
//create server
|
//create server
|
||||||
var app = express.createServer();
|
var app = express.createServer();
|
||||||
|
|
||||||
//load modules that needs a initalized db
|
//load modules that needs a initalized db
|
||||||
readOnlyManager = require("./db/ReadOnlyManager");
|
readOnlyManager = require("./db/ReadOnlyManager");
|
||||||
exporthtml = require("./utils/ExportHtml");
|
exporthtml = require("./utils/ExportHtml");
|
||||||
|
@ -87,43 +87,46 @@ async.waterfall([
|
||||||
padManager = require('./db/PadManager');
|
padManager = require('./db/PadManager');
|
||||||
securityManager = require('./db/SecurityManager');
|
securityManager = require('./db/SecurityManager');
|
||||||
socketIORouter = require("./handler/SocketIORouter");
|
socketIORouter = require("./handler/SocketIORouter");
|
||||||
|
|
||||||
//install logging
|
//install logging
|
||||||
var httpLogger = log4js.getLogger("http");
|
var httpLogger = log4js.getLogger("http");
|
||||||
app.configure(function()
|
app.configure(function()
|
||||||
{
|
{
|
||||||
// Activate http basic auth if it has been defined in settings.json
|
// Activate http basic auth if it has been defined in settings.json
|
||||||
if(settings.httpAuth != null) app.use(basic_auth);
|
if(settings.httpAuth) {
|
||||||
|
app.use(basic_auth);
|
||||||
|
}
|
||||||
|
|
||||||
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
|
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
|
||||||
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
|
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
|
||||||
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR"))
|
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR")) {
|
||||||
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
||||||
|
}
|
||||||
app.use(express.cookieParser());
|
app.use(express.cookieParser());
|
||||||
});
|
});
|
||||||
|
|
||||||
app.error(function(err, req, res, next){
|
app.error(function(err, req, res, next){
|
||||||
res.send(500);
|
res.send(500);
|
||||||
console.error(err.stack ? err.stack : err.toString());
|
console.error(err.stack ? err.stack : err.toString());
|
||||||
gracefulShutdown();
|
gracefulShutdown();
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve static files
|
//serve static files
|
||||||
app.get('/static/*', function(req, res)
|
app.get('/static/*', function(req, res)
|
||||||
{
|
{
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
var filePath = path.normalize(__dirname + "/.." +
|
var filePath = path.normalize(__dirname + "/.." +
|
||||||
req.url.replace(/\.\./g, '').split("?")[0]);
|
req.url.replace(/\.\./g, '').split("?")[0]);
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve minified files
|
//serve minified files
|
||||||
app.get('/minified/:id', function(req, res, next)
|
app.get('/minified/:id', function(req, res, next)
|
||||||
{
|
{
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
|
|
||||||
var id = req.params.id;
|
var id = req.params.id;
|
||||||
|
|
||||||
if(id == "pad.js" || id == "timeslider.js")
|
if(id == "pad.js" || id == "timeslider.js")
|
||||||
{
|
{
|
||||||
minify.minifyJS(req,res,id);
|
minify.minifyJS(req,res,id);
|
||||||
|
@ -133,14 +136,15 @@ async.waterfall([
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//checks for padAccess
|
//checks for padAccess
|
||||||
function hasPadAccess(req, res, callback)
|
function hasPadAccess(req, res, callback)
|
||||||
{
|
{
|
||||||
|
console.log(req.params);
|
||||||
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
|
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//there is access, continue
|
//there is access, continue
|
||||||
if(accessObj.accessStatus == "grant")
|
if(accessObj.accessStatus == "grant")
|
||||||
{
|
{
|
||||||
|
@ -163,7 +167,7 @@ async.waterfall([
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
|
||||||
if (req.headers.authorization) {
|
if (req.headers.authorization) {
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
|
@ -173,16 +177,16 @@ async.waterfall([
|
||||||
res.send('Authentication required', 401);
|
res.send('Authentication required', 401);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//serve read only pad
|
//serve read only pad
|
||||||
app.get('/ro/:id', function(req, res)
|
app.get('/ro/:id', function(req, res)
|
||||||
{
|
{
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
|
|
||||||
var html;
|
var html;
|
||||||
var padId;
|
var padId;
|
||||||
var pad;
|
var pad;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//translate the read only pad to a padId
|
//translate the read only pad to a padId
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -190,12 +194,12 @@ async.waterfall([
|
||||||
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
padId = _padId;
|
padId = _padId;
|
||||||
|
|
||||||
//we need that to tell hasPadAcess about the pad
|
//we need that to tell hasPadAcess about the pad
|
||||||
req.params.pad = padId;
|
req.params.pad = padId;
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -203,12 +207,12 @@ async.waterfall([
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//return if the there is no padId
|
//return if the there is no padId
|
||||||
if(padId == null)
|
if(!padId)
|
||||||
{
|
{
|
||||||
callback("notfound");
|
callback("notfound");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
hasPadAccess(req, res, function()
|
||||||
{
|
{
|
||||||
//render the html document
|
//render the html document
|
||||||
|
@ -225,29 +229,29 @@ async.waterfall([
|
||||||
//throw any unexpected error
|
//throw any unexpected error
|
||||||
if(err && err != "notfound")
|
if(err && err != "notfound")
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
if(err == "notfound")
|
if(err == "notfound")
|
||||||
res.send('404 - Not Found', 404);
|
res.send('404 - Not Found', 404);
|
||||||
else
|
else
|
||||||
res.send(html);
|
res.send(html);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve pad.html under /p
|
//serve pad.html under /p
|
||||||
app.get('/p/:pad', function(req, res, next)
|
app.get('/p/:pad', function(req, res, next)
|
||||||
{
|
{
|
||||||
//ensure the padname is valid and the url doesn't end with a /
|
//ensure the padname is valid and the url doesn't end with a /
|
||||||
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
|
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
|
||||||
{
|
{
|
||||||
res.send('Such a padname is forbidden', 404);
|
res.send('Such a padname is forbidden', 404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
var filePath = path.normalize(__dirname + "/../static/pad.html");
|
var filePath = path.normalize(__dirname + "/../static/pad.html");
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve timeslider.html under /p/$padname/timeslider
|
//serve timeslider.html under /p/$padname/timeslider
|
||||||
app.get('/p/:pad/timeslider', function(req, res, next)
|
app.get('/p/:pad/timeslider', function(req, res, next)
|
||||||
{
|
{
|
||||||
|
@ -257,12 +261,12 @@ async.waterfall([
|
||||||
res.send('Such a padname is forbidden', 404);
|
res.send('Such a padname is forbidden', 404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
|
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve timeslider.html under /p/$padname/timeslider
|
//serve timeslider.html under /p/$padname/timeslider
|
||||||
app.get('/p/:pad/export/:type', function(req, res, next)
|
app.get('/p/:pad/export/:type', function(req, res, next)
|
||||||
{
|
{
|
||||||
|
@ -272,7 +276,7 @@ async.waterfall([
|
||||||
res.send('Such a padname is forbidden', 404);
|
res.send('Such a padname is forbidden', 404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
||||||
//send a 404 if we don't support this filetype
|
//send a 404 if we don't support this filetype
|
||||||
if(types.indexOf(req.params.type) == -1)
|
if(types.indexOf(req.params.type) == -1)
|
||||||
|
@ -280,24 +284,23 @@ async.waterfall([
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
||||||
if(settings.abiword == null &&
|
if(!settings.abiword && ["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
||||||
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
|
||||||
{
|
{
|
||||||
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.header("Access-Control-Allow-Origin", "*");
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
hasPadAccess(req, res, function()
|
||||||
{
|
{
|
||||||
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//handle import requests
|
//handle import requests
|
||||||
app.post('/p/:pad/import', function(req, res, next)
|
app.post('/p/:pad/import', function(req, res, next)
|
||||||
{
|
{
|
||||||
|
@ -307,84 +310,84 @@ async.waterfall([
|
||||||
res.send('Such a padname is forbidden', 404);
|
res.send('Such a padname is forbidden', 404);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if abiword is disabled, skip handling this request
|
//if abiword is disabled, skip handling this request
|
||||||
if(settings.abiword == null)
|
if(!settings.abiword)
|
||||||
{
|
{
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
hasPadAccess(req, res, function()
|
||||||
{
|
{
|
||||||
importHandler.doImport(req, res, req.params.pad);
|
importHandler.doImport(req, res, req.params.pad);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var apiLogger = log4js.getLogger("API");
|
var apiLogger = log4js.getLogger("API");
|
||||||
|
|
||||||
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
||||||
var apiCaller = function(req, res, fields) {
|
var apiCaller = function(req, res, fields) {
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
res.header("Content-Type", "application/json; charset=utf-8");
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
||||||
|
|
||||||
//wrap the send function so we can log the response
|
//wrap the send function so we can log the response
|
||||||
res._send = res.send;
|
res._send = res.send;
|
||||||
res.send = function(response)
|
res.send = function(response)
|
||||||
{
|
{
|
||||||
response = JSON.stringify(response);
|
response = JSON.stringify(response);
|
||||||
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
|
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
|
||||||
|
|
||||||
//is this a jsonp call, if yes, add the function call
|
//is this a jsonp call, if yes, add the function call
|
||||||
if(req.query.jsonp)
|
if(req.query.jsonp)
|
||||||
response = req.query.jsonp + "(" + response + ")";
|
response = req.query.jsonp + "(" + response + ")";
|
||||||
|
|
||||||
res._send(response);
|
res._send(response);
|
||||||
}
|
};
|
||||||
|
|
||||||
//call the api handler
|
//call the api handler
|
||||||
apiHandler.handle(req.params.func, fields, req, res);
|
apiHandler.handle(req.params.func, fields, req, res);
|
||||||
}
|
};
|
||||||
|
|
||||||
//This is a api GET call, collect all post informations and pass it to the apiHandler
|
//This is a api GET call, collect all post informations and pass it to the apiHandler
|
||||||
app.get('/api/1/:func', function(req, res)
|
app.get('/api/1/:func', function(req, res)
|
||||||
{
|
{
|
||||||
apiCaller(req, res, req.query)
|
apiCaller(req, res, req.query);
|
||||||
});
|
});
|
||||||
|
|
||||||
//This is a api POST call, collect all post informations and pass it to the apiHandler
|
//This is a api POST call, collect all post informations and pass it to the apiHandler
|
||||||
app.post('/api/1/:func', function(req, res)
|
app.post('/api/1/:func', function(req, res)
|
||||||
{
|
{
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
||||||
{
|
{
|
||||||
apiCaller(req, res, fields)
|
apiCaller(req, res, fields);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//The Etherpad client side sends information about how a disconnect happen
|
//The Etherpad client side sends information about how a disconnect happen
|
||||||
app.post('/ep/pad/connection-diagnostic-info', function(req, res)
|
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)
|
||||||
{
|
{
|
||||||
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
|
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
|
||||||
res.end("OK");
|
res.end("OK");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//The Etherpad client side sends information about client side javscript errors
|
//The Etherpad client side sends information about client side javscript errors
|
||||||
app.post('/jserror', function(req, res)
|
app.post('/jserror', function(req, res)
|
||||||
{
|
{
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
||||||
{
|
{
|
||||||
console.error("CLIENT SIDE JAVASCRIPT ERROR: " + fields.errorInfo);
|
console.error("CLIENT SIDE JAVASCRIPT ERROR: " + fields.errorInfo);
|
||||||
res.end("OK");
|
res.end("OK");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve index.html under /
|
//serve index.html under /
|
||||||
app.get('/', function(req, res)
|
app.get('/', function(req, res)
|
||||||
{
|
{
|
||||||
|
@ -392,7 +395,7 @@ async.waterfall([
|
||||||
var filePath = path.normalize(__dirname + "/../static/index.html");
|
var filePath = path.normalize(__dirname + "/../static/index.html");
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve robots.txt
|
//serve robots.txt
|
||||||
app.get('/robots.txt', function(req, res)
|
app.get('/robots.txt', function(req, res)
|
||||||
{
|
{
|
||||||
|
@ -400,7 +403,7 @@ async.waterfall([
|
||||||
var filePath = path.normalize(__dirname + "/../static/robots.txt");
|
var filePath = path.normalize(__dirname + "/../static/robots.txt");
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
res.sendfile(filePath, { maxAge: exports.maxAge });
|
||||||
});
|
});
|
||||||
|
|
||||||
//serve favicon.ico
|
//serve favicon.ico
|
||||||
app.get('/favicon.ico', function(req, res)
|
app.get('/favicon.ico', function(req, res)
|
||||||
{
|
{
|
||||||
|
@ -416,7 +419,7 @@ async.waterfall([
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//let the server listen
|
//let the server listen
|
||||||
app.listen(settings.port, settings.ip);
|
app.listen(settings.port, settings.ip);
|
||||||
console.log("Server is listening at " + settings.ip + ":" + settings.port);
|
console.log("Server is listening at " + settings.ip + ":" + settings.port);
|
||||||
|
@ -432,13 +435,13 @@ async.waterfall([
|
||||||
{
|
{
|
||||||
console.error(err);
|
console.error(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ensure there is only one graceful shutdown running
|
//ensure there is only one graceful shutdown running
|
||||||
if(onShutdown) return;
|
if(onShutdown) return;
|
||||||
onShutdown = true;
|
onShutdown = true;
|
||||||
|
|
||||||
console.log("graceful shutdown...");
|
console.log("graceful shutdown...");
|
||||||
|
|
||||||
//stop the http server
|
//stop the http server
|
||||||
app.close();
|
app.close();
|
||||||
|
|
||||||
|
@ -446,14 +449,14 @@ async.waterfall([
|
||||||
db.db.doShutdown(function()
|
db.db.doShutdown(function()
|
||||||
{
|
{
|
||||||
console.log("db sucessfully closed.");
|
console.log("db sucessfully closed.");
|
||||||
|
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
setTimeout(function(){
|
setTimeout(function(){
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
};
|
||||||
|
|
||||||
//connect graceful shutdown with sigint and uncaughtexception
|
//connect graceful shutdown with sigint and uncaughtexception
|
||||||
if(os.type().indexOf("Windows") == -1)
|
if(os.type().indexOf("Windows") == -1)
|
||||||
|
@ -462,22 +465,22 @@ async.waterfall([
|
||||||
//https://github.com/joyent/node/issues/1553
|
//https://github.com/joyent/node/issues/1553
|
||||||
process.on('SIGINT', gracefulShutdown);
|
process.on('SIGINT', gracefulShutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on('uncaughtException', gracefulShutdown);
|
process.on('uncaughtException', gracefulShutdown);
|
||||||
|
|
||||||
//init socket.io and redirect all requests to the MessageHandler
|
//init socket.io and redirect all requests to the MessageHandler
|
||||||
var io = socketio.listen(app);
|
var io = socketio.listen(app);
|
||||||
|
|
||||||
//this is only a workaround to ensure it works with all browers behind a proxy
|
//this is only a workaround to ensure it works with all browers behind a proxy
|
||||||
//we should remove this when the new socket.io version is more stable
|
//we should remove this when the new socket.io version is more stable
|
||||||
io.set('transports', ['xhr-polling']);
|
io.set('transports', ['xhr-polling']);
|
||||||
|
|
||||||
var socketIOLogger = log4js.getLogger("socket.io");
|
var socketIOLogger = log4js.getLogger("socket.io");
|
||||||
io.set('logger', {
|
io.set('logger', {
|
||||||
debug: function (str)
|
debug: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.debug.apply(socketIOLogger, arguments);
|
socketIOLogger.debug.apply(socketIOLogger, arguments);
|
||||||
},
|
},
|
||||||
info: function (str)
|
info: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.info.apply(socketIOLogger, arguments);
|
socketIOLogger.info.apply(socketIOLogger, arguments);
|
||||||
|
@ -489,21 +492,21 @@ async.waterfall([
|
||||||
error: function (str)
|
error: function (str)
|
||||||
{
|
{
|
||||||
socketIOLogger.error.apply(socketIOLogger, arguments);
|
socketIOLogger.error.apply(socketIOLogger, arguments);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//minify socket.io javascript
|
//minify socket.io javascript
|
||||||
if(settings.minify)
|
if(settings.minify)
|
||||||
io.enable('browser client minification');
|
io.enable('browser client minification');
|
||||||
|
|
||||||
var padMessageHandler = require("./handler/PadMessageHandler");
|
var padMessageHandler = require("./handler/PadMessageHandler");
|
||||||
var timesliderMessageHandler = require("./handler/TimesliderMessageHandler");
|
var timesliderMessageHandler = require("./handler/TimesliderMessageHandler");
|
||||||
|
|
||||||
//Initalize the Socket.IO Router
|
//Initalize the Socket.IO Router
|
||||||
socketIORouter.setSocketIO(io);
|
socketIORouter.setSocketIO(io);
|
||||||
socketIORouter.addComponent("pad", padMessageHandler);
|
socketIORouter.addComponent("pad", padMessageHandler);
|
||||||
socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var spawn = require('child_process').spawn;
|
var spawn = require('child_process').spawn;
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
|
@ -35,7 +35,7 @@ if(os.type().indexOf("Windows") > -1)
|
||||||
{
|
{
|
||||||
//span an abiword process to perform the conversion
|
//span an abiword process to perform the conversion
|
||||||
var abiword = spawn(settings.abiword, ["--to=" + task.destFile, task.srcFile]);
|
var abiword = spawn(settings.abiword, ["--to=" + task.destFile, task.srcFile]);
|
||||||
|
|
||||||
//delegate the processing of stdout to another function
|
//delegate the processing of stdout to another function
|
||||||
abiword.stdout.on('data', function (data)
|
abiword.stdout.on('data', function (data)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,7 @@ if(os.type().indexOf("Windows") > -1)
|
||||||
});
|
});
|
||||||
|
|
||||||
//append error messages to the buffer
|
//append error messages to the buffer
|
||||||
abiword.stderr.on('data', function (data)
|
abiword.stderr.on('data', function (data)
|
||||||
{
|
{
|
||||||
stdoutBuffer += data.toString();
|
stdoutBuffer += data.toString();
|
||||||
});
|
});
|
||||||
|
@ -52,19 +52,19 @@ if(os.type().indexOf("Windows") > -1)
|
||||||
//throw exceptions if abiword is dieing
|
//throw exceptions if abiword is dieing
|
||||||
abiword.on('exit', function (code)
|
abiword.on('exit', function (code)
|
||||||
{
|
{
|
||||||
if(code != 0) {
|
if(code !== 0) {
|
||||||
throw "Abiword died with exit code " + code;
|
throw "Abiword died with exit code " + code;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(stdoutBuffer != "")
|
if(stdoutBuffer !== "")
|
||||||
{
|
{
|
||||||
console.log(stdoutBuffer);
|
console.log(stdoutBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.convertFile = function(srcFile, destFile, type, callback)
|
exports.convertFile = function(srcFile, destFile, type, callback)
|
||||||
{
|
{
|
||||||
doConvertTask({"srcFile": srcFile, "destFile": destFile, "type": type}, callback);
|
doConvertTask({"srcFile": srcFile, "destFile": destFile, "type": type}, callback);
|
||||||
|
@ -78,67 +78,68 @@ else
|
||||||
var abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]);
|
var abiword = spawn(settings.abiword, ["--plugin", "AbiCommand"]);
|
||||||
|
|
||||||
//append error messages to the buffer
|
//append error messages to the buffer
|
||||||
abiword.stderr.on('data', function (data)
|
abiword.stderr.on('data', function (data)
|
||||||
{
|
{
|
||||||
stdoutBuffer += data.toString();
|
stdoutBuffer += data.toString();
|
||||||
});
|
});
|
||||||
|
|
||||||
//throw exceptions if abiword is dieing
|
//throw exceptions if abiword is dieing
|
||||||
abiword.on('exit', function (code)
|
abiword.on('exit', function (code)
|
||||||
{
|
{
|
||||||
throw "Abiword died with exit code " + code;
|
throw "Abiword died with exit code " + code;
|
||||||
});
|
});
|
||||||
|
|
||||||
//delegate the processing of stdout to a other function
|
|
||||||
abiword.stdout.on('data',onAbiwordStdout);
|
|
||||||
|
|
||||||
var stdoutCallback = null;
|
var stdoutCallback = null;
|
||||||
var stdoutBuffer = "";
|
var stdoutBuffer = "";
|
||||||
var firstPrompt = true;
|
var firstPrompt = true;
|
||||||
|
|
||||||
function onAbiwordStdout(data)
|
var onAbiwordStdout = function onAbiwordStdout(data)
|
||||||
{
|
{
|
||||||
//add data to buffer
|
//add data to buffer
|
||||||
stdoutBuffer+=data.toString();
|
stdoutBuffer+=data.toString();
|
||||||
|
|
||||||
//we're searching for the prompt, cause this means everything we need is in the buffer
|
//we're searching for the prompt, cause this means everything we need is in the buffer
|
||||||
if(stdoutBuffer.search("AbiWord:>") != -1)
|
if(stdoutBuffer.search("AbiWord:>") != -1)
|
||||||
{
|
{
|
||||||
//filter the feedback message
|
//filter the feedback message
|
||||||
var err = stdoutBuffer.search("OK") != -1 ? null : stdoutBuffer;
|
var err = stdoutBuffer.search("OK") != -1 ? null : stdoutBuffer;
|
||||||
|
|
||||||
//reset the buffer
|
//reset the buffer
|
||||||
stdoutBuffer = "";
|
stdoutBuffer = "";
|
||||||
|
|
||||||
//call the callback with the error message
|
//call the callback with the error message
|
||||||
//skip the first prompt
|
//skip the first prompt
|
||||||
if(stdoutCallback != null && !firstPrompt)
|
if(stdoutCallback && !firstPrompt)
|
||||||
{
|
{
|
||||||
stdoutCallback(err);
|
stdoutCallback(err);
|
||||||
stdoutCallback = null;
|
stdoutCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
firstPrompt = false;
|
firstPrompt = false;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
//delegate the processing of stdout to a other function
|
||||||
|
abiword.stdout.on('data',onAbiwordStdout);
|
||||||
|
|
||||||
doConvertTask = function(task, callback)
|
doConvertTask = function(task, callback)
|
||||||
{
|
{
|
||||||
abiword.stdin.write("convert " + task.srcFile + " " + task.destFile + " " + task.type + "\n");
|
abiword.stdin.write("convert " + task.srcFile + " " + task.destFile + " " + task.type + "\n");
|
||||||
|
|
||||||
//create a callback that calls the task callback and the caller callback
|
//create a callback that calls the task callback and the caller callback
|
||||||
stdoutCallback = function (err)
|
stdoutCallback = function (err)
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
task.callback(err);
|
task.callback(err);
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
//Queue with the converts we have to do
|
//Queue with the converts we have to do
|
||||||
var queue = async.queue(doConvertTask, 1);
|
var queue = async.queue(doConvertTask, 1);
|
||||||
|
|
||||||
exports.convertFile = function(srcFile, destFile, type, callback)
|
exports.convertFile = function(srcFile, destFile, type, callback)
|
||||||
{
|
{
|
||||||
queue.push({"srcFile": srcFile, "destFile": destFile, "type": type, "callback": callback});
|
queue.push({"srcFile": srcFile, "destFile": destFile, "type": type, "callback": callback});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/**
|
/**
|
||||||
* This code represents the Attribute Pool Object of the original Etherpad.
|
* This code represents the Attribute Pool Object of the original Etherpad.
|
||||||
* 90% of the code is still like in the original Etherpad
|
* 90% of the code is still like in the original Etherpad
|
||||||
* Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
* Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||||
* You can find a explanation what a attribute pool is here:
|
* You can find a explanation what a attribute pool is here:
|
||||||
* https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
|
* https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -87,4 +87,4 @@ exports.createAttributePool = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
};
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
/*
|
/*
|
||||||
* This is the Changeset library copied from the old Etherpad with some modifications to use it in node.js
|
* This is the Changeset library copied from the old Etherpad with some modifications to use it in node.js
|
||||||
* Can be found in https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
* Can be found in https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This code is mostly from the old Etherpad. Please help us to comment this code.
|
* This code is mostly from the old Etherpad. Please help us to comment this code.
|
||||||
* This helps other people to understand this code better and helps them to improve it.
|
* This helps other people to understand this code better and helps them to improve it.
|
||||||
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
|
||||||
*/
|
*/
|
||||||
|
@ -66,7 +66,7 @@ exports.newLen = function (cs) {
|
||||||
|
|
||||||
exports.opIterator = function (opsStr, optStartIndex) {
|
exports.opIterator = function (opsStr, optStartIndex) {
|
||||||
//print(opsStr);
|
//print(opsStr);
|
||||||
var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g;
|
var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([\-+=])([0-9a-z]+)|\?|/g;
|
||||||
var startIndex = (optStartIndex || 0);
|
var startIndex = (optStartIndex || 0);
|
||||||
var curIndex = startIndex;
|
var curIndex = startIndex;
|
||||||
var prevIndex = curIndex;
|
var prevIndex = curIndex;
|
||||||
|
@ -195,12 +195,10 @@ exports.checkRep = function (cs) {
|
||||||
exports.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs);
|
exports.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs);
|
||||||
break;
|
break;
|
||||||
case '+':
|
case '+':
|
||||||
{
|
calcNewLen += o.chars;
|
||||||
calcNewLen += o.chars;
|
numInserted += o.chars;
|
||||||
numInserted += o.chars;
|
exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs);
|
||||||
exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs);
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
assem.append(o);
|
assem.append(o);
|
||||||
}
|
}
|
||||||
|
@ -216,7 +214,7 @@ exports.checkRep = function (cs) {
|
||||||
exports.assert(normalized == cs, normalized, ' != ', cs);
|
exports.assert(normalized == cs, normalized, ' != ', cs);
|
||||||
|
|
||||||
return cs;
|
return cs;
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.smartOpAssembler = function () {
|
exports.smartOpAssembler = function () {
|
||||||
// Like opAssembler but able to produce conforming exportss
|
// Like opAssembler but able to produce conforming exportss
|
||||||
|
@ -387,7 +385,7 @@ if (_opt) {
|
||||||
bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars;
|
bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars;
|
||||||
bufOp.lines += op.lines;
|
bufOp.lines += op.lines;
|
||||||
bufOpAdditionalCharsAfterNewline = 0;
|
bufOpAdditionalCharsAfterNewline = 0;
|
||||||
} else if (bufOp.lines == 0) {
|
} else if (bufOp.lines === 0) {
|
||||||
// both bufOp and op are in-line
|
// both bufOp and op are in-line
|
||||||
bufOp.chars += op.chars;
|
bufOp.chars += op.chars;
|
||||||
} else {
|
} else {
|
||||||
|
@ -635,9 +633,9 @@ exports.textLinesMutator = function (lines) {
|
||||||
}
|
}
|
||||||
//print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length);
|
//print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length);
|
||||||
/*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) {
|
/*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) {
|
||||||
print("BLAH");
|
print("BLAH");
|
||||||
putCurLineInSplice();
|
putCurLineInSplice();
|
||||||
}*/
|
}*/
|
||||||
// tests case foo in remove(), which isn't otherwise covered in current impl
|
// tests case foo in remove(), which isn't otherwise covered in current impl
|
||||||
}
|
}
|
||||||
//debugPrint("skip");
|
//debugPrint("skip");
|
||||||
|
@ -667,13 +665,13 @@ exports.textLinesMutator = function (lines) {
|
||||||
enterSplice();
|
enterSplice();
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextKLinesText(k) {
|
var nextKLinesText = function nextKLinesText(k) {
|
||||||
var m = curSplice[0] + curSplice[1];
|
var m = curSplice[0] + curSplice[1];
|
||||||
return lines_slice(m, m + k).join('');
|
return lines_slice(m, m + k).join('');
|
||||||
}
|
};
|
||||||
if (isCurLineInSplice()) {
|
if (isCurLineInSplice()) {
|
||||||
//print(curCol);
|
//print(curCol);
|
||||||
if (curCol == 0) {
|
if (curCol === 0) {
|
||||||
removed = curSplice[curSplice.length - 1];
|
removed = curSplice[curSplice.length - 1];
|
||||||
// print("FOO"); // case foo
|
// print("FOO"); // case foo
|
||||||
curSplice.length--;
|
curSplice.length--;
|
||||||
|
@ -719,6 +717,7 @@ exports.textLinesMutator = function (lines) {
|
||||||
if (!inSplice) {
|
if (!inSplice) {
|
||||||
enterSplice();
|
enterSplice();
|
||||||
}
|
}
|
||||||
|
var sline;
|
||||||
if (L) {
|
if (L) {
|
||||||
var newLines = exports.splitTextLines(text);
|
var newLines = exports.splitTextLines(text);
|
||||||
if (isCurLineInSplice()) {
|
if (isCurLineInSplice()) {
|
||||||
|
@ -729,7 +728,7 @@ exports.textLinesMutator = function (lines) {
|
||||||
//curLine += newLines.length;
|
//curLine += newLines.length;
|
||||||
//}
|
//}
|
||||||
//else {
|
//else {
|
||||||
var sline = curSplice.length - 1;
|
sline = curSplice.length - 1;
|
||||||
var theLine = curSplice[sline];
|
var theLine = curSplice[sline];
|
||||||
var lineCol = curCol;
|
var lineCol = curCol;
|
||||||
curSplice[sline] = theLine.substring(0, lineCol) + newLines[0];
|
curSplice[sline] = theLine.substring(0, lineCol) + newLines[0];
|
||||||
|
@ -745,7 +744,7 @@ exports.textLinesMutator = function (lines) {
|
||||||
curLine += newLines.length;
|
curLine += newLines.length;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var sline = putCurLineInSplice();
|
sline = putCurLineInSplice();
|
||||||
curSplice[sline] = curSplice[sline].substring(0, curCol) + text + curSplice[sline].substring(curCol);
|
curSplice[sline] = curSplice[sline].substring(0, curCol) + text + curSplice[sline].substring(curCol);
|
||||||
curCol += text.length;
|
curCol += text.length;
|
||||||
}
|
}
|
||||||
|
@ -949,74 +948,66 @@ exports._slicerZipperFunc = function (attOp, csOp, opOut, pool) {
|
||||||
} else {
|
} else {
|
||||||
switch (csOp.opcode) {
|
switch (csOp.opcode) {
|
||||||
case '-':
|
case '-':
|
||||||
{
|
if (csOp.chars <= attOp.chars) {
|
||||||
if (csOp.chars <= attOp.chars) {
|
// delete or delete part
|
||||||
// delete or delete part
|
if (attOp.opcode == '=') {
|
||||||
if (attOp.opcode == '=') {
|
opOut.opcode = '-';
|
||||||
opOut.opcode = '-';
|
|
||||||
opOut.chars = csOp.chars;
|
|
||||||
opOut.lines = csOp.lines;
|
|
||||||
opOut.attribs = '';
|
|
||||||
}
|
|
||||||
attOp.chars -= csOp.chars;
|
|
||||||
attOp.lines -= csOp.lines;
|
|
||||||
csOp.opcode = '';
|
|
||||||
if (!attOp.chars) {
|
|
||||||
attOp.opcode = '';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// delete and keep going
|
|
||||||
if (attOp.opcode == '=') {
|
|
||||||
opOut.opcode = '-';
|
|
||||||
opOut.chars = attOp.chars;
|
|
||||||
opOut.lines = attOp.lines;
|
|
||||||
opOut.attribs = '';
|
|
||||||
}
|
|
||||||
csOp.chars -= attOp.chars;
|
|
||||||
csOp.lines -= attOp.lines;
|
|
||||||
attOp.opcode = '';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '+':
|
|
||||||
{
|
|
||||||
// insert
|
|
||||||
exports.copyOp(csOp, opOut);
|
|
||||||
csOp.opcode = '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case '=':
|
|
||||||
{
|
|
||||||
if (csOp.chars <= attOp.chars) {
|
|
||||||
// keep or keep part
|
|
||||||
opOut.opcode = attOp.opcode;
|
|
||||||
opOut.chars = csOp.chars;
|
opOut.chars = csOp.chars;
|
||||||
opOut.lines = csOp.lines;
|
opOut.lines = csOp.lines;
|
||||||
opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool);
|
opOut.attribs = '';
|
||||||
csOp.opcode = '';
|
}
|
||||||
attOp.chars -= csOp.chars;
|
attOp.chars -= csOp.chars;
|
||||||
attOp.lines -= csOp.lines;
|
attOp.lines -= csOp.lines;
|
||||||
if (!attOp.chars) {
|
csOp.opcode = '';
|
||||||
attOp.opcode = '';
|
if (!attOp.chars) {
|
||||||
}
|
attOp.opcode = '';
|
||||||
} else {
|
}
|
||||||
// keep and keep going
|
} else {
|
||||||
opOut.opcode = attOp.opcode;
|
// delete and keep going
|
||||||
|
if (attOp.opcode == '=') {
|
||||||
|
opOut.opcode = '-';
|
||||||
opOut.chars = attOp.chars;
|
opOut.chars = attOp.chars;
|
||||||
opOut.lines = attOp.lines;
|
opOut.lines = attOp.lines;
|
||||||
opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool);
|
opOut.attribs = '';
|
||||||
attOp.opcode = '';
|
|
||||||
csOp.chars -= attOp.chars;
|
|
||||||
csOp.lines -= attOp.lines;
|
|
||||||
}
|
}
|
||||||
break;
|
csOp.chars -= attOp.chars;
|
||||||
}
|
csOp.lines -= attOp.lines;
|
||||||
case '':
|
|
||||||
{
|
|
||||||
exports.copyOp(attOp, opOut);
|
|
||||||
attOp.opcode = '';
|
attOp.opcode = '';
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
// insert
|
||||||
|
exports.copyOp(csOp, opOut);
|
||||||
|
csOp.opcode = '';
|
||||||
|
break;
|
||||||
|
case '=':
|
||||||
|
if (csOp.chars <= attOp.chars) {
|
||||||
|
// keep or keep part
|
||||||
|
opOut.opcode = attOp.opcode;
|
||||||
|
opOut.chars = csOp.chars;
|
||||||
|
opOut.lines = csOp.lines;
|
||||||
|
opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool);
|
||||||
|
csOp.opcode = '';
|
||||||
|
attOp.chars -= csOp.chars;
|
||||||
|
attOp.lines -= csOp.lines;
|
||||||
|
if (!attOp.chars) {
|
||||||
|
attOp.opcode = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// keep and keep going
|
||||||
|
opOut.opcode = attOp.opcode;
|
||||||
|
opOut.chars = attOp.chars;
|
||||||
|
opOut.lines = attOp.lines;
|
||||||
|
opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool);
|
||||||
|
attOp.opcode = '';
|
||||||
|
csOp.chars -= attOp.chars;
|
||||||
|
csOp.lines -= attOp.lines;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '':
|
||||||
|
exports.copyOp(attOp, opOut);
|
||||||
|
attOp.opcode = '';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1470,7 +1461,7 @@ exports.appendATextToAssembler = function (atext, assem) {
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.prepareForWire = function (cs, pool) {
|
exports.prepareForWire = function (cs, pool) {
|
||||||
var newPool = AttributePoolFactory.createAttributePool();;
|
var newPool = AttributePoolFactory.createAttributePool();
|
||||||
var newCs = exports.moveOpsToNewPool(cs, pool, newPool);
|
var newCs = exports.moveOpsToNewPool(cs, pool, newPool);
|
||||||
return {
|
return {
|
||||||
translated: newCs,
|
translated: newCs,
|
||||||
|
@ -1480,7 +1471,7 @@ exports.prepareForWire = function (cs, pool) {
|
||||||
|
|
||||||
exports.isIdentity = function (cs) {
|
exports.isIdentity = function (cs) {
|
||||||
var unpacked = exports.unpack(cs);
|
var unpacked = exports.unpack(cs);
|
||||||
return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen;
|
return unpacked.ops === "" && unpacked.oldLen == unpacked.newLen;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.opAttributeValue = function (op, key, pool) {
|
exports.opAttributeValue = function (op, key, pool) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2011 Adrian Lang
|
* Copyright 2011 Adrian Lang
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -28,7 +28,7 @@ function getPadDokuWiki(pad, revNum, callback)
|
||||||
|
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
if (revNum != undefined)
|
if (revNum)
|
||||||
{
|
{
|
||||||
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
|
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
|
||||||
{
|
{
|
||||||
|
@ -122,6 +122,8 @@ function getDokuWikiFromAtext(pad, atext)
|
||||||
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
||||||
idx += numChars;
|
idx += numChars;
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
var o = iter.next();
|
var o = iter.next();
|
||||||
|
@ -142,7 +144,7 @@ function getDokuWikiFromAtext(pad, atext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (var i = 0; i < propVals.length; i++)
|
for (i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
if (propVals[i] === true)
|
if (propVals[i] === true)
|
||||||
{
|
{
|
||||||
|
@ -160,7 +162,7 @@ function getDokuWikiFromAtext(pad, atext)
|
||||||
{
|
{
|
||||||
// leaving bold (e.g.) also leaves italics, etc.
|
// leaving bold (e.g.) also leaves italics, etc.
|
||||||
var left = false;
|
var left = false;
|
||||||
for (var i = 0; i < propVals.length; i++)
|
for (i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
var v = propVals[i];
|
var v = propVals[i];
|
||||||
if (!left)
|
if (!left)
|
||||||
|
@ -179,7 +181,7 @@ function getDokuWikiFromAtext(pad, atext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = propVals.length - 1; i >= 0; i--)
|
for (i = propVals.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (propVals[i] === LEAVE)
|
if (propVals[i] === LEAVE)
|
||||||
{
|
{
|
||||||
|
@ -191,7 +193,7 @@ function getDokuWikiFromAtext(pad, atext)
|
||||||
emitCloseTag(i);
|
emitCloseTag(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var i = 0; i < propVals.length; i++)
|
for (i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
if (propVals[i] === ENTER || propVals[i] === STAY)
|
if (propVals[i] === ENTER || propVals[i] === STAY)
|
||||||
{
|
{
|
||||||
|
@ -210,7 +212,7 @@ function getDokuWikiFromAtext(pad, atext)
|
||||||
|
|
||||||
assem.append(_escapeDokuWiki(s));
|
assem.append(_escapeDokuWiki(s));
|
||||||
} // end iteration over spans in line
|
} // end iteration over spans in line
|
||||||
for (var i = propVals.length - 1; i >= 0; i--)
|
for (i = propVals.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (propVals[i])
|
if (propVals[i])
|
||||||
{
|
{
|
||||||
|
@ -310,7 +312,7 @@ exports.getPadDokuWikiDocument = function (padId, revNum, callback)
|
||||||
|
|
||||||
getPadDokuWiki(pad, revNum, callback);
|
getPadDokuWiki(pad, revNum, callback);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function _escapeDokuWiki(s)
|
function _escapeDokuWiki(s)
|
||||||
{
|
{
|
||||||
|
@ -321,7 +323,7 @@ function _escapeDokuWiki(s)
|
||||||
// copied from ACE
|
// copied from ACE
|
||||||
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
||||||
var _REGEX_SPACE = /\s/;
|
var _REGEX_SPACE = /\s/;
|
||||||
var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')');
|
var _REGEX_URLCHAR = new RegExp('(' + (/[\-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/).source + '|' + _REGEX_WORDCHAR.source + ')');
|
||||||
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
|
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
|
||||||
|
|
||||||
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2009 Google Inc.
|
* Copyright 2009 Google Inc.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS-IS" BASIS,
|
* distributed under the License is distributed on an "AS-IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@ -55,7 +55,7 @@ function getPadHTML(pad, revNum, callback)
|
||||||
|
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
if (revNum != undefined)
|
if (revNum)
|
||||||
{
|
{
|
||||||
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
|
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
|
||||||
{
|
{
|
||||||
|
@ -140,7 +140,7 @@ function getHTMLFromAtext(pad, atext)
|
||||||
assem.append(tags[i]);
|
assem.append(tags[i]);
|
||||||
assem.append('>');
|
assem.append('>');
|
||||||
}
|
}
|
||||||
|
|
||||||
function orderdCloseTags(tags2close)
|
function orderdCloseTags(tags2close)
|
||||||
{
|
{
|
||||||
for(var i=0;i<openTags.length;i++)
|
for(var i=0;i<openTags.length;i++)
|
||||||
|
@ -171,6 +171,8 @@ function getHTMLFromAtext(pad, atext)
|
||||||
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
|
||||||
idx += numChars;
|
idx += numChars;
|
||||||
|
|
||||||
|
var tags2close;
|
||||||
|
|
||||||
while (iter.hasNext())
|
while (iter.hasNext())
|
||||||
{
|
{
|
||||||
var o = iter.next();
|
var o = iter.next();
|
||||||
|
@ -191,7 +193,9 @@ function getHTMLFromAtext(pad, atext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
for (var i = 0; i < propVals.length; i++)
|
|
||||||
|
var i;
|
||||||
|
for (i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
if (propVals[i] === true)
|
if (propVals[i] === true)
|
||||||
{
|
{
|
||||||
|
@ -209,7 +213,7 @@ function getHTMLFromAtext(pad, atext)
|
||||||
{
|
{
|
||||||
// leaving bold (e.g.) also leaves italics, etc.
|
// leaving bold (e.g.) also leaves italics, etc.
|
||||||
var left = false;
|
var left = false;
|
||||||
for (var i = 0; i < propVals.length; i++)
|
for (i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
var v = propVals[i];
|
var v = propVals[i];
|
||||||
if (!left)
|
if (!left)
|
||||||
|
@ -228,9 +232,9 @@ function getHTMLFromAtext(pad, atext)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags2close = [];
|
tags2close = [];
|
||||||
|
|
||||||
for (var i = propVals.length - 1; i >= 0; i--)
|
for (i = propVals.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (propVals[i] === LEAVE)
|
if (propVals[i] === LEAVE)
|
||||||
{
|
{
|
||||||
|
@ -244,10 +248,10 @@ function getHTMLFromAtext(pad, atext)
|
||||||
tags2close.push(i);
|
tags2close.push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
orderdCloseTags(tags2close);
|
orderdCloseTags(tags2close);
|
||||||
|
|
||||||
for (var i = 0; i < propVals.length; i++)
|
for (i = 0; i < propVals.length; i++)
|
||||||
{
|
{
|
||||||
if (propVals[i] === ENTER || propVals[i] === STAY)
|
if (propVals[i] === ENTER || propVals[i] === STAY)
|
||||||
{
|
{
|
||||||
|
@ -262,26 +266,26 @@ function getHTMLFromAtext(pad, atext)
|
||||||
{
|
{
|
||||||
chars--; // exclude newline at end of line, if present
|
chars--; // exclude newline at end of line, if present
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = taker.take(chars);
|
var s = taker.take(chars);
|
||||||
|
|
||||||
//removes the characters with the code 12. Don't know where they come
|
//removes the characters with the code 12. Don't know where they come
|
||||||
//from but they break the abiword parser and are completly useless
|
//from but they break the abiword parser and are completly useless
|
||||||
s = s.replace(String.fromCharCode(12), "");
|
s = s.replace(String.fromCharCode(12), "");
|
||||||
|
|
||||||
assem.append(_escapeHTML(s));
|
assem.append(_escapeHTML(s));
|
||||||
} // end iteration over spans in line
|
} // end iteration over spans in line
|
||||||
|
|
||||||
var tags2close = [];
|
tags2close = [];
|
||||||
for (var i = propVals.length - 1; i >= 0; i--)
|
for (var x = propVals.length - 1; x >= 0; x--)
|
||||||
{
|
{
|
||||||
if (propVals[i])
|
if (propVals[x])
|
||||||
{
|
{
|
||||||
tags2close.push(i);
|
tags2close.push(x);
|
||||||
propVals[i] = false;
|
propVals[x] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
orderdCloseTags(tags2close);
|
orderdCloseTags(tags2close);
|
||||||
} // end processNextChars
|
} // end processNextChars
|
||||||
if (urls)
|
if (urls)
|
||||||
|
@ -425,7 +429,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
|
||||||
callback(null, head + html + foot);
|
callback(null, head + html + foot);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
function _escapeHTML(s)
|
function _escapeHTML(s)
|
||||||
{
|
{
|
||||||
|
@ -436,18 +440,18 @@ function _escapeHTML(s)
|
||||||
re.MAP = {
|
re.MAP = {
|
||||||
'&': '&',
|
'&': '&',
|
||||||
'<': '<',
|
'<': '<',
|
||||||
'>': '>',
|
'>': '>'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
s = s.replace(re, function (c)
|
s = s.replace(re, function (c)
|
||||||
{
|
{
|
||||||
return re.MAP[c];
|
return re.MAP[c];
|
||||||
});
|
});
|
||||||
|
|
||||||
return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c)
|
return s.replace(/[^\x21-\x7E\s\t\n\r]/g, function(c)
|
||||||
{
|
{
|
||||||
return "&#" +c.charCodeAt(0) + ";"
|
return "&#" +c.charCodeAt(0) + ";";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,15 +471,19 @@ function _processSpaces(s)
|
||||||
{
|
{
|
||||||
parts.push(m);
|
parts.push(m);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var i;
|
||||||
|
var p;
|
||||||
|
|
||||||
if (doesWrap)
|
if (doesWrap)
|
||||||
{
|
{
|
||||||
var endOfLine = true;
|
var endOfLine = true;
|
||||||
var beforeSpace = false;
|
var beforeSpace = false;
|
||||||
// last space in a run is normal, others are nbsp,
|
// last space in a run is normal, others are nbsp,
|
||||||
// end of line is nbsp
|
// end of line is nbsp
|
||||||
for (var i = parts.length - 1; i >= 0; i--)
|
for (i = parts.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var p = parts[i];
|
p = parts[i];
|
||||||
if (p == " ")
|
if (p == " ")
|
||||||
{
|
{
|
||||||
if (endOfLine || beforeSpace) parts[i] = ' ';
|
if (endOfLine || beforeSpace) parts[i] = ' ';
|
||||||
|
@ -489,9 +497,9 @@ function _processSpaces(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// beginning of line is nbsp
|
// beginning of line is nbsp
|
||||||
for (var i = 0; i < parts.length; i++)
|
for (i = 0; i < parts.length; i++)
|
||||||
{
|
{
|
||||||
var p = parts[i];
|
p = parts[i];
|
||||||
if (p == " ")
|
if (p == " ")
|
||||||
{
|
{
|
||||||
parts[i] = ' ';
|
parts[i] = ' ';
|
||||||
|
@ -505,9 +513,9 @@ function _processSpaces(s)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (var i = 0; i < parts.length; i++)
|
for (i = 0; i < parts.length; i++)
|
||||||
{
|
{
|
||||||
var p = parts[i];
|
p = parts[i];
|
||||||
if (p == " ")
|
if (p == " ")
|
||||||
{
|
{
|
||||||
parts[i] = ' ';
|
parts[i] = ' ';
|
||||||
|
@ -521,7 +529,7 @@ function _processSpaces(s)
|
||||||
// copied from ACE
|
// copied from ACE
|
||||||
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
|
||||||
var _REGEX_SPACE = /\s/;
|
var _REGEX_SPACE = /\s/;
|
||||||
var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')');
|
var _REGEX_URLCHAR = new RegExp('(' + (/[\-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/).source + '|' + _REGEX_WORDCHAR.source + ')');
|
||||||
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
|
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
|
||||||
|
|
||||||
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* This Module manages all /minified/* requests. It controls the
|
* This Module manages all /minified/\* requests. It controls the
|
||||||
* minification && compression of Javascript and CSS.
|
* minification && compression of Javascript and CSS.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
@ -28,7 +28,7 @@ var jsp = require("uglify-js").parser;
|
||||||
var pro = require("uglify-js").uglify;
|
var pro = require("uglify-js").uglify;
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var Buffer = require('buffer').Buffer;
|
var Buffer = require('buffer').Buffer;
|
||||||
var gzip = require('gzip');
|
var zlib = require('zlib');
|
||||||
var server = require('../server');
|
var server = require('../server');
|
||||||
var os = require('os');
|
var os = require('os');
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js",
|
||||||
exports.minifyJS = function(req, res, jsFilename)
|
exports.minifyJS = function(req, res, jsFilename)
|
||||||
{
|
{
|
||||||
res.header("Content-Type","text/javascript");
|
res.header("Content-Type","text/javascript");
|
||||||
|
|
||||||
//choose the js files we need
|
//choose the js files we need
|
||||||
if(jsFilename == "pad.js")
|
if(jsFilename == "pad.js")
|
||||||
{
|
{
|
||||||
|
@ -58,20 +58,22 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
{
|
{
|
||||||
throw new Error("there is no profile for creating " + name);
|
throw new Error("there is no profile for creating " + name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fileValues;
|
||||||
|
|
||||||
//minifying is enabled
|
//minifying is enabled
|
||||||
if(settings.minify)
|
if(settings.minify)
|
||||||
{
|
{
|
||||||
var fileValues = {};
|
fileValues = {};
|
||||||
var embeds = {};
|
var embeds = {};
|
||||||
var latestModification = 0;
|
var latestModification = 0;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//find out the highest modification date
|
//find out the highest modification date
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var folders2check = ["../static/css","../static/js"];
|
var folders2check = ["../static/css","../static/js"];
|
||||||
|
|
||||||
//go trough this two folders
|
//go trough this two folders
|
||||||
async.forEach(folders2check, function(path, callback)
|
async.forEach(folders2check, function(path, callback)
|
||||||
{
|
{
|
||||||
|
@ -79,27 +81,27 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
fs.readdir(path, function(err, files)
|
fs.readdir(path, function(err, files)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//we wanna check the directory itself for changes too
|
//we wanna check the directory itself for changes too
|
||||||
files.push(".");
|
files.push(".");
|
||||||
|
|
||||||
//go trough all files in this folder
|
//go trough all files in this folder
|
||||||
async.forEach(files, function(filename, callback)
|
async.forEach(files, function(filename, callback)
|
||||||
{
|
{
|
||||||
//get the stat data of this file
|
//get the stat data of this file
|
||||||
fs.stat(path + "/" + filename, function(err, stats)
|
fs.stat(path + "/" + filename, function(err, stats)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//get the modification time
|
//get the modification time
|
||||||
var modificationTime = stats.mtime.getTime();
|
var modificationTime = stats.mtime.getTime();
|
||||||
|
|
||||||
//compare the modification time to the highest found
|
//compare the modification time to the highest found
|
||||||
if(modificationTime > latestModification)
|
if(modificationTime > latestModification)
|
||||||
{
|
{
|
||||||
latestModification = modificationTime;
|
latestModification = modificationTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}, callback);
|
}, callback);
|
||||||
|
@ -116,7 +118,7 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
ERR(err, callback);
|
ERR(err, callback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//there is no minfied file or there new changes since this file was generated, so continue generating this file
|
//there is no minfied file or there new changes since this file was generated, so continue generating this file
|
||||||
if((err && err.code == "ENOENT") || stats.mtime.getTime() < latestModification)
|
if((err && err.code == "ENOENT") || stats.mtime.getTime() < latestModification)
|
||||||
{
|
{
|
||||||
|
@ -128,48 +130,48 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
callback("stop");
|
callback("stop");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//load all js files
|
//load all js files
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
async.forEach(jsFiles, function (item, callback)
|
async.forEach(jsFiles, function (item, callback)
|
||||||
{
|
{
|
||||||
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
|
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
fileValues[item] = data;
|
fileValues[item] = data;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}, callback);
|
}, callback);
|
||||||
},
|
},
|
||||||
//find all includes in ace.js and embed them
|
//find all includes in ace.js and embed them
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//if this is not the creation of pad.js, skip this part
|
//if this is not the creation of pad.js, skip this part
|
||||||
if(jsFilename != "pad.js")
|
if(jsFilename != "pad.js")
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var founds = fileValues["ace.js"].match(/\$\$INCLUDE_[a-zA-Z_]+\([a-zA-Z0-9.\/_"]+\)/gi);
|
var founds = fileValues["ace.js"].match(/\$\$INCLUDE_[a-zA-Z_]+\([a-zA-Z0-9.\/_"]+\)/gi);
|
||||||
|
|
||||||
//go trough all includes
|
//go trough all includes
|
||||||
async.forEach(founds, function (item, callback)
|
async.forEach(founds, function (item, callback)
|
||||||
{
|
{
|
||||||
var filename = item.match(/"[^"]*"/g)[0].substr(1);
|
var filename = item.match(/"[^"]*"/g)[0].substr(1);
|
||||||
filename = filename.substr(0,filename.length-1);
|
filename = filename.substr(0,filename.length-1);
|
||||||
|
|
||||||
var type = item.match(/INCLUDE_[A-Z]+/g)[0].substr("INCLUDE_".length);
|
var type = item.match(/INCLUDE_[A-Z]+/g)[0].substr("INCLUDE_".length);
|
||||||
|
|
||||||
var quote = item.search("_Q") != -1;
|
var quote = item.search("_Q") != -1;
|
||||||
|
|
||||||
//read the included file
|
//read the included file
|
||||||
fs.readFile(filename, "utf-8", function(err, data)
|
fs.readFile(filename, "utf-8", function(err, data)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//compress the file
|
//compress the file
|
||||||
if(type == "JS")
|
if(type == "JS")
|
||||||
{
|
{
|
||||||
embeds[item] = "<script>\n" + compressJS([data])+ "\n\\x3c/script>";
|
embeds[item] = "<script>\n" + compressJS([data])+ "\n\\x3c/script>";
|
||||||
|
@ -178,36 +180,36 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
{
|
{
|
||||||
embeds[item] = "<style>" + compressCSS([data])+ "</style>";
|
embeds[item] = "<style>" + compressCSS([data])+ "</style>";
|
||||||
}
|
}
|
||||||
|
|
||||||
//do the first escape
|
//do the first escape
|
||||||
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\\"/g, "\"");
|
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\\"/g, "\"");
|
||||||
embeds[item] = embeds[item].substr(1);
|
embeds[item] = embeds[item].substr(1);
|
||||||
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
|
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
|
||||||
|
|
||||||
//add quotes, if wished
|
//add quotes, if wished
|
||||||
if(quote)
|
if(quote)
|
||||||
{
|
{
|
||||||
embeds[item] = "'" + embeds[item] + "'";
|
embeds[item] = "'" + embeds[item] + "'";
|
||||||
}
|
}
|
||||||
|
|
||||||
//do the second escape
|
//do the second escape
|
||||||
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\"/g, "\"");
|
embeds[item] = JSON.stringify(embeds[item]).replace(/'/g, "\\'").replace(/\"/g, "\"");
|
||||||
embeds[item] = embeds[item].substr(1);
|
embeds[item] = embeds[item].substr(1);
|
||||||
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
|
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
|
||||||
embeds[item] = "'" + embeds[item] + "'";
|
embeds[item] = "'" + embeds[item] + "'";
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}, function(err)
|
}, function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//replace the include command with the include
|
//replace the include command with the include
|
||||||
for(var i in embeds)
|
for(var i in embeds)
|
||||||
{
|
{
|
||||||
fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]);
|
fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -220,36 +222,25 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
{
|
{
|
||||||
values.push(fileValues[jsFiles[i]]);
|
values.push(fileValues[jsFiles[i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
//minify all javascript files to one
|
//minify all javascript files to one
|
||||||
var result = compressJS(values);
|
var result = compressJS(values);
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
//write the results plain in a file
|
//write the results plain in a file
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
fs.writeFile("../var/minified_" + jsFilename, result, "utf8", callback);
|
fs.writeFile("../var/minified_" + jsFilename, result, "utf8", callback);
|
||||||
},
|
},
|
||||||
//write the results compressed in a file
|
//write the results compressed in a file
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//spawn a gzip process if we're on a unix system
|
zlib.gzip(result, function(err, compressedResult){
|
||||||
if(os.type().indexOf("Windows") == -1)
|
|
||||||
{
|
if(ERR(err, callback)) return;
|
||||||
gzip(result, 9, function(err, compressedResult){
|
|
||||||
//weird gzip bug that returns 0 instead of null if everything is ok
|
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
|
||||||
err = err === 0 ? null : err;
|
});
|
||||||
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
//skip this step on windows
|
|
||||||
else
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],callback);
|
],callback);
|
||||||
}
|
}
|
||||||
|
@ -259,10 +250,10 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
{
|
{
|
||||||
if(ERR(err)) return;
|
if(ERR(err)) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//check if gzip is supported by this browser
|
//check if gzip is supported by this browser
|
||||||
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1;
|
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1;
|
||||||
|
|
||||||
var pathStr;
|
var pathStr;
|
||||||
if(gzipSupport && os.type().indexOf("Windows") == -1)
|
if(gzipSupport && os.type().indexOf("Windows") == -1)
|
||||||
{
|
{
|
||||||
|
@ -273,41 +264,41 @@ exports.minifyJS = function(req, res, jsFilename)
|
||||||
{
|
{
|
||||||
pathStr = path.normalize(__dirname + "/../../var/minified_" + jsFilename );
|
pathStr = path.normalize(__dirname + "/../../var/minified_" + jsFilename );
|
||||||
}
|
}
|
||||||
|
|
||||||
res.sendfile(pathStr, { maxAge: server.maxAge });
|
res.sendfile(pathStr, { maxAge: server.maxAge });
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
//minifying is disabled, so put the files together in one file
|
//minifying is disabled, so put the files together in one file
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var fileValues = {};
|
fileValues = {};
|
||||||
|
|
||||||
//read all js files
|
//read all js files
|
||||||
async.forEach(jsFiles, function (item, callback)
|
async.forEach(jsFiles, function (item, callback)
|
||||||
{
|
{
|
||||||
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
|
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
fileValues[item] = data;
|
fileValues[item] = data;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//send all files together
|
//send all files together
|
||||||
function(err)
|
function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err)) return;
|
if(ERR(err)) return;
|
||||||
|
|
||||||
for(var i=0;i<jsFiles.length;i++)
|
for(var i=0;i<jsFiles.length;i++)
|
||||||
{
|
{
|
||||||
var fileName = jsFiles[i];
|
var fileName = jsFiles[i];
|
||||||
res.write("\n\n\n/*** File: static/js/" + fileName + " ***/\n\n\n");
|
res.write("\n\n\n/*** File: static/js/" + fileName + " ***/\n\n\n");
|
||||||
res.write(fileValues[fileName]);
|
res.write(fileValues[fileName]);
|
||||||
}
|
}
|
||||||
|
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function compressJS(values)
|
function compressJS(values)
|
||||||
{
|
{
|
||||||
|
|
7
node/utils/cleantext.js
Normal file
7
node/utils/cleantext.js
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
/**
|
||||||
|
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
|
||||||
|
* @param txt
|
||||||
|
*/
|
||||||
|
exports.cleanText = function (txt) {
|
||||||
|
return txt.replace(/\r\n/g,'\n').replace(/\r/g,'\n').replace(/\t/g, ' ').replace(/\xa0/g, ' ');
|
||||||
|
};
|
|
@ -173,7 +173,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
function _isEmpty(node, state)
|
function _isEmpty(node, state)
|
||||||
{
|
{
|
||||||
// consider clean blank lines pasted in IE to be empty
|
// consider clean blank lines pasted in IE to be empty
|
||||||
if (dom.nodeNumChildren(node) == 0) return true;
|
if (dom.nodeNumChildren(node) === 0) return true;
|
||||||
if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " " && !getAssoc(node, "unpasted"))
|
if (dom.nodeNumChildren(node) == 1 && getAssoc(node, "shouldBeEmpty") && dom.optNodeInnerHTML(node) == " " && !getAssoc(node, "unpasted"))
|
||||||
{
|
{
|
||||||
if (state)
|
if (state)
|
||||||
|
@ -191,7 +191,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
{
|
{
|
||||||
var ln = lines.length() - 1;
|
var ln = lines.length() - 1;
|
||||||
var chr = lines.textOfLine(ln).length;
|
var chr = lines.textOfLine(ln).length;
|
||||||
if (chr == 0 && state.listType && state.listType != 'none')
|
if (chr === 0 && state.listType && state.listType != 'none')
|
||||||
{
|
{
|
||||||
chr += 1; // listMarker
|
chr += 1; // listMarker
|
||||||
}
|
}
|
||||||
|
@ -218,11 +218,11 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
cc.incrementFlag = function(state, flagName)
|
cc.incrementFlag = function(state, flagName)
|
||||||
{
|
{
|
||||||
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
state.flags[flagName] = (state.flags[flagName] || 0) + 1;
|
||||||
}
|
};
|
||||||
cc.decrementFlag = function(state, flagName)
|
cc.decrementFlag = function(state, flagName)
|
||||||
{
|
{
|
||||||
state.flags[flagName]--;
|
state.flags[flagName]--;
|
||||||
}
|
};
|
||||||
cc.incrementAttrib = function(state, attribName)
|
cc.incrementAttrib = function(state, attribName)
|
||||||
{
|
{
|
||||||
if (!state.attribs[attribName])
|
if (!state.attribs[attribName])
|
||||||
|
@ -234,12 +234,12 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
state.attribs[attribName]++;
|
state.attribs[attribName]++;
|
||||||
}
|
}
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
};
|
||||||
cc.decrementAttrib = function(state, attribName)
|
cc.decrementAttrib = function(state, attribName)
|
||||||
{
|
{
|
||||||
state.attribs[attribName]--;
|
state.attribs[attribName]--;
|
||||||
_recalcAttribString(state);
|
_recalcAttribString(state);
|
||||||
}
|
};
|
||||||
|
|
||||||
function _enterList(state, listType)
|
function _enterList(state, listType)
|
||||||
{
|
{
|
||||||
|
@ -315,14 +315,14 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
{
|
{
|
||||||
if (state)
|
if (state)
|
||||||
{
|
{
|
||||||
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0;
|
||||||
if (atBeginningOfLine && state.listType && state.listType != 'none')
|
if (atBeginningOfLine && state.listType && state.listType != 'none')
|
||||||
{
|
{
|
||||||
_produceListMarker(state);
|
_produceListMarker(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.startNew();
|
lines.startNew();
|
||||||
}
|
};
|
||||||
cc.notifySelection = function(sel)
|
cc.notifySelection = function(sel)
|
||||||
{
|
{
|
||||||
if (sel)
|
if (sel)
|
||||||
|
@ -358,12 +358,15 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
if (isBlock) _ensureColumnZero(state);
|
if (isBlock) _ensureColumnZero(state);
|
||||||
var startLine = lines.length() - 1;
|
var startLine = lines.length() - 1;
|
||||||
_reachBlockPoint(node, 0, state);
|
_reachBlockPoint(node, 0, state);
|
||||||
|
|
||||||
|
var i;
|
||||||
|
|
||||||
if (dom.isNodeText(node))
|
if (dom.isNodeText(node))
|
||||||
{
|
{
|
||||||
var txt = dom.nodeValue(node);
|
var txt = dom.nodeValue(node);
|
||||||
var rest = '';
|
var rest = '';
|
||||||
var x = 0; // offset into original text
|
var x = 0; // offset into original text
|
||||||
if (txt.length == 0)
|
if (txt.length === 0)
|
||||||
{
|
{
|
||||||
if (startPoint && node == startPoint.node)
|
if (startPoint && node == startPoint.node)
|
||||||
{
|
{
|
||||||
|
@ -404,7 +407,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
// removing "\n" from pasted HTML will collapse words together.
|
// removing "\n" from pasted HTML will collapse words together.
|
||||||
txt2 = "";
|
txt2 = "";
|
||||||
}
|
}
|
||||||
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length == 0;
|
var atBeginningOfLine = lines.textOfLine(lines.length() - 1).length === 0;
|
||||||
if (atBeginningOfLine)
|
if (atBeginningOfLine)
|
||||||
{
|
{
|
||||||
// newlines in the source mustn't become spaces at beginning of line box
|
// newlines in the source mustn't become spaces at beginning of line box
|
||||||
|
@ -425,6 +428,8 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var c;
|
||||||
|
|
||||||
var tname = (dom.nodeTagName(node) || "").toLowerCase();
|
var tname = (dom.nodeTagName(node) || "").toLowerCase();
|
||||||
if (tname == "br")
|
if (tname == "br")
|
||||||
{
|
{
|
||||||
|
@ -472,9 +477,9 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
{
|
{
|
||||||
cc.doAttrib(state, "strikethrough");
|
cc.doAttrib(state, "strikethrough");
|
||||||
}
|
}
|
||||||
|
var type;
|
||||||
if (tname == "ul")
|
if (tname == "ul")
|
||||||
{
|
{
|
||||||
var type;
|
|
||||||
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
|
var rr = cls && /(?:^| )list-(bullet[12345678])\b/.exec(cls);
|
||||||
type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
|
type = rr && rr[1] || "bullet" + String(Math.min(_MAX_LIST_LEVEL, (state.listNesting || 0) + 1));
|
||||||
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
oldListTypeOrNull = (_enterList(state, type) || 'none');
|
||||||
|
@ -488,9 +493,9 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
var classes = cls.match(/\S+/g);
|
var classes = cls.match(/\S+/g);
|
||||||
if (classes && classes.length > 0)
|
if (classes && classes.length > 0)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < classes.length; i++)
|
for (i = 0; i < classes.length; i++)
|
||||||
{
|
{
|
||||||
var c = classes[i];
|
c = classes[i];
|
||||||
var a = className2Author(c);
|
var a = className2Author(c);
|
||||||
if (a)
|
if (a)
|
||||||
{
|
{
|
||||||
|
@ -503,9 +508,9 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
}
|
}
|
||||||
|
|
||||||
var nc = dom.nodeNumChildren(node);
|
var nc = dom.nodeNumChildren(node);
|
||||||
for (var i = 0; i < nc; i++)
|
for (i = 0; i < nc; i++)
|
||||||
{
|
{
|
||||||
var c = dom.nodeChild(node, i);
|
c = dom.nodeChild(node, i);
|
||||||
cc.collectContent(c, state);
|
cc.collectContent(c, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,7 +528,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
if (isPre) cc.decrementFlag(state, 'preMode');
|
if (isPre) cc.decrementFlag(state, 'preMode');
|
||||||
if (state.localAttribs)
|
if (state.localAttribs)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < state.localAttribs.length; i++)
|
for (i = 0; i < state.localAttribs.length; i++)
|
||||||
{
|
{
|
||||||
cc.decrementAttrib(state, state.localAttribs[i]);
|
cc.decrementAttrib(state, state.localAttribs[i]);
|
||||||
}
|
}
|
||||||
|
@ -612,6 +617,30 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
var buffer = 10; // chars allowed over before wrapping
|
var buffer = 10; // chars allowed over before wrapping
|
||||||
var linesWrapped = 0;
|
var linesWrapped = 0;
|
||||||
var numLinesAfter = 0;
|
var numLinesAfter = 0;
|
||||||
|
|
||||||
|
var fixLineNumber = function fixLineNumber(lineChar)
|
||||||
|
{
|
||||||
|
if (lineChar[0] < 0) return;
|
||||||
|
var n = lineChar[0];
|
||||||
|
var c = lineChar[1];
|
||||||
|
if (n > i)
|
||||||
|
{
|
||||||
|
n += (newStrings.length - 1);
|
||||||
|
}
|
||||||
|
else if (n == i)
|
||||||
|
{
|
||||||
|
var a = 0;
|
||||||
|
while (c > newStrings[a].length)
|
||||||
|
{
|
||||||
|
c -= newStrings[a].length;
|
||||||
|
a++;
|
||||||
|
}
|
||||||
|
n += a;
|
||||||
|
}
|
||||||
|
lineChar[0] = n;
|
||||||
|
lineChar[1] = c;
|
||||||
|
};
|
||||||
|
|
||||||
for (var i = lineStrings.length - 1; i >= 0; i--)
|
for (var i = lineStrings.length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var oldString = lineStrings[i];
|
var oldString = lineStrings[i];
|
||||||
|
@ -620,6 +649,8 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
{
|
{
|
||||||
var newStrings = [];
|
var newStrings = [];
|
||||||
var newAttribStrings = [];
|
var newAttribStrings = [];
|
||||||
|
|
||||||
|
|
||||||
while (oldString.length > lineLimit)
|
while (oldString.length > lineLimit)
|
||||||
{
|
{
|
||||||
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
//var semiloc = oldString.lastIndexOf(';', lineLimit-1);
|
||||||
|
@ -636,28 +667,6 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
newAttribStrings.push(oldAttribString);
|
newAttribStrings.push(oldAttribString);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fixLineNumber(lineChar)
|
|
||||||
{
|
|
||||||
if (lineChar[0] < 0) return;
|
|
||||||
var n = lineChar[0];
|
|
||||||
var c = lineChar[1];
|
|
||||||
if (n > i)
|
|
||||||
{
|
|
||||||
n += (newStrings.length - 1);
|
|
||||||
}
|
|
||||||
else if (n == i)
|
|
||||||
{
|
|
||||||
var a = 0;
|
|
||||||
while (c > newStrings[a].length)
|
|
||||||
{
|
|
||||||
c -= newStrings[a].length;
|
|
||||||
a++;
|
|
||||||
}
|
|
||||||
n += a;
|
|
||||||
}
|
|
||||||
lineChar[0] = n;
|
|
||||||
lineChar[1] = c;
|
|
||||||
}
|
|
||||||
fixLineNumber(ss);
|
fixLineNumber(ss);
|
||||||
fixLineNumber(se);
|
fixLineNumber(se);
|
||||||
linesWrapped++;
|
linesWrapped++;
|
||||||
|
@ -684,7 +693,7 @@ function makeContentCollector(collectStyles, browser, apool, domInterface, class
|
||||||
lines: lineStrings,
|
lines: lineStrings,
|
||||||
lineAttribs: lineAttribs
|
lineAttribs: lineAttribs
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
return cc;
|
return cc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
||||||
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com> - Primary Technology Ltd",
|
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com> - Primary Technology Ltd",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{ "name": "John McLear",
|
{ "name": "John McLear",
|
||||||
"name": "Hans Pinckaers",
|
"name": "Hans Pinckaers",
|
||||||
"name": "Robin Buse"}
|
"name": "Robin Buse"}
|
||||||
],
|
],
|
||||||
|
@ -13,15 +13,16 @@
|
||||||
"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",
|
||||||
"gzip" : "0.1.0",
|
|
||||||
"formidable" : "1.0.7",
|
"formidable" : "1.0.7",
|
||||||
"log4js" : "0.3.9",
|
"log4js" : "0.4.1",
|
||||||
"jsdom-nocontextifiy" : "0.2.10",
|
"jsdom-nocontextifiy" : "0.2.10",
|
||||||
"async-stacktrace" : "0.0.2"
|
"async-stacktrace" : "0.0.2"
|
||||||
},
|
},
|
||||||
|
"devDependencies" : {
|
||||||
|
"jshint" : "0.5.5"
|
||||||
|
},
|
||||||
"version" : "1.0.0"
|
"version" : "1.0.0"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue