Commit f95e2793 authored by John McLear's avatar John McLear
Browse files

Merge pull request #841 from Pita/releases-1.1.1

Releases 1.1.1
parents 4195e11a 34fdfcb2
......@@ -41,7 +41,7 @@ echo "do a normal unix install first..."
bin/installDeps.sh || exit 1
echo "copy the windows settings template..."
cp settings.json.template_windows settings.json
cp settings.json.template settings.json
echo "resolve symbolic links..."
cp -rL node_modules node_modules_resolved
......
......@@ -35,8 +35,9 @@ fi
#check node version
NODE_VERSION=$(node --version)
if [ ! $(echo $NODE_VERSION | cut -d "." -f 1-2) = "v0.6" ]; then
echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.6.x" >&2
NODE_V_MINOR=$(echo $NODE_VERSION | cut -d "." -f 1-2)
if [ ! $NODE_V_MINOR = "v0.8" ] && [ ! $NODE_V_MINOR = "v0.6" ]; then
echo "You're running a wrong version of node, you're using $NODE_VERSION, we need v0.6.x or v0.8.x" >&2
exit 1
fi
......
@echo off
set NODE_VERSION=0.8.1
set JQUERY_VERSION=1.7
:: change directory to etherpad-lite root
cd bin
cd ..
echo _
echo Updating node...
curl -lo bin\node.exe http://nodejs.org/dist/v%NODE_VERSION%/node.exe
echo _
echo Installing etherpad-lite and dependencies...
cmd /C npm install src/
echo _
echo Updating jquery...
curl -lo "node_modules\ep_etherpad-lite\static\js\jquery.min.js" "http://code.jquery.com/jquery-%JQUERY_VERSION%.min.js"
echo _
echo Copying custom templates...
set custom_dir=node_modules\ep_etherpad-lite\static\custom
FOR %%f IN (index pad timeslider) DO (
if NOT EXIST "%custom_dir%\%%f.js" copy "%custom_dir%\js.template" "%custom_dir%\%%f.js"
if NOT EXIST "%custom_dir%\%%f.css" copy "%custom_dir%\css.template" "%custom_dir%\%%f.css"
)
echo _
echo Clearing cache.
del /S var\minified*
echo _
echo Setting up settings.json...
IF NOT EXIST settings.json copy settings.json.template settings.json
echo _
echo Installed Etherpad-lite!
\ No newline at end of file
......@@ -8,8 +8,8 @@
"ip": "0.0.0.0",
"port" : 9001,
//The Type of the database. You can choose between dirty, sqlite and mysql
//You should use mysql or sqlite for anything else than testing or development
//The Type of the database. You can choose between dirty, postgres, sqlite and mysql
//You shouldn't use "dirty" for for anything else than testing or development
"dbType" : "dirty",
//the database specific settings
"dbSettings" : {
......
/*
This file must be valid JSON. But comments are allowed
Please edit settings.json, not settings.json.template
*/
{
//Ip and port which etherpad should bind at
"ip": "0.0.0.0",
"port" : 9001,
//The Type of the database. You can choose between sqlite and mysql
"dbType" : "dirty",
//the database specific settings
"dbSettings" : {
"filename" : "var/dirty.db"
},
/* An Example of MySQL Configuration
"dbType" : "mysql",
"dbSettings" : {
"user" : "root",
"host" : "localhost",
"password": "",
"database": "store"
},
*/
//the default text of a pad
"defaultPadText" : "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n",
/* Users must have a session to access pads. This effectively allows only group pads to be accessed. */
"requireSession" : false,
/* Users may edit pads but not create new ones. Pad creation is only via the API. This applies both to group pads and regular pads. */
"editOnly" : false,
/* if true, all css & js will be minified before sending to the client. This will improve the loading performance massivly,
but makes it impossible to debug the javascript/css */
"minify" : false,
/* This is the path to the Abiword executable. Setting it to null, disables abiword.
Abiword is needed to enable the import/export of pads*/
"abiword" : null,
/* cache 6 hours = 1000*60*60*6 */
"maxAge": 21600000
}
{
"parts": [
{ "name": "express", "hooks": {
"createServer": "ep_etherpad-lite/node/hooks/express:createServer",
"restartServer": "ep_etherpad-lite/node/hooks/express:restartServer"
} },
{ "name": "static", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/static:expressCreateServer" } },
{ "name": "specialpages", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/specialpages:expressCreateServer" } },
{ "name": "padurlsanitize", "hooks": { "expressCreateServer": "ep_etherpad-lite/node/hooks/express/padurlsanitize:expressCreateServer" } },
......
......@@ -47,6 +47,8 @@ exports.createGroupPad = groupManager.createGroupPad;
exports.createAuthor = authorManager.createAuthor;
exports.createAuthorIfNotExistsFor = authorManager.createAuthorIfNotExistsFor;
exports.listPadsOfAuthor = authorManager.listPadsOfAuthor;
exports.padUsersCount = padMessageHandler.padUsersCount;
/**********************/
/**SESSION FUNCTIONS***/
......@@ -282,6 +284,24 @@ exports.getRevisionsCount = function(padID, callback)
});
}
/**
getLastEdited(padID) returns the timestamp of the last revision of the pad
Example returns:
{code: 0, message:"ok", data: {lastEdited: 1340815946602}}
{code: 1, message:"padID does not exist", data: null}
*/
exports.getLastEdited = function(padID, callback)
{
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(ERR(err, callback)) return;
callback(null, {lastEdited: pad.getLastEdited()});
});
}
/**
createPad(padName [, text]) creates a new pad in this group
......@@ -463,6 +483,26 @@ exports.isPasswordProtected = function(padID, callback)
});
}
/**
listAuthorsOfPad(padID) returns an array of authors who contributed to this pad
Example returns:
{code: 0, message:"ok", data: {authorIDs : ["a.s8oes9dhwrvt0zif", "a.akf8finncvomlqva"]}
{code: 1, message:"padID does not exist", data: null}
*/
exports.listAuthorsOfPad = function(padID, callback)
{
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(ERR(err, callback)) return;
callback(null, {authorIDs: pad.getAllAuthors()});
});
}
/******************************/
/** INTERNAL HELPER FUNCTIONS */
/******************************/
......
......@@ -55,6 +55,7 @@ exports.getAuthor4Token = function (token, callback)
/**
* Returns the AuthorID for a mapper.
* @param {String} token The mapper
* @param {String} name The name of the author (optional)
* @param {Function} callback callback (err, author)
*/
exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
......@@ -153,6 +154,7 @@ exports.getAuthorColorId = function (author, callback)
/**
* Sets the color Id of the author
* @param {String} author The id of the author
* @param {String} colorId The color id of the author
* @param {Function} callback (optional)
*/
exports.setAuthorColorId = function (author, colorId, callback)
......@@ -173,9 +175,95 @@ exports.getAuthorName = function (author, callback)
/**
* Sets the name of the author
* @param {String} author The id of the author
* @param {String} name The name of the author
* @param {Function} callback (optional)
*/
exports.setAuthorName = function (author, name, callback)
{
db.setSub("globalAuthor:" + author, ["name"], name, callback);
}
/**
* Returns an array of all pads this author contributed to
* @param {String} author The id of the author
* @param {Function} callback (optional)
*/
exports.listPadsOfAuthor = function (authorID, callback)
{
/* There are two other places where this array is manipulated:
* (1) When the author is added to a pad, the author object is also updated
* (2) When a pad is deleted, each author of that pad is also updated
*/
//get the globalAuthor
db.get("globalAuthor:" + authorID, function(err, author)
{
if(ERR(err, callback)) return;
//author does not exists
if(author == null)
{
callback(new customError("authorID does not exist","apierror"))
}
//everything is fine, return the pad IDs
else
{
var pads = [];
if(author.padIDs != null)
{
for (var padId in author.padIDs)
{
pads.push(padId);
}
}
callback(null, {padIDs: pads});
}
});
}
/**
* Adds a new pad to the list of contributions
* @param {String} author The id of the author
* @param {String} padID The id of the pad the author contributes to
*/
exports.addPad = function (authorID, padID)
{
//get the entry
db.get("globalAuthor:" + authorID, function(err, author)
{
if(ERR(err)) return;
if(author == null) return;
//the entry doesn't exist so far, let's create it
if(author.padIDs == null)
{
author.padIDs = {};
}
//add the entry for this pad
author.padIDs[padID] = 1;// anything, because value is not used
//save the new element back
db.set("globalAuthor:" + authorID, author);
});
}
/**
* Removes a pad from the list of contributions
* @param {String} author The id of the author
* @param {String} padID The id of the pad the author contributes to
*/
exports.removePad = function (authorID, padID)
{
db.get("globalAuthor:" + authorID, function (err, author)
{
if(ERR(err)) return;
if(author == null) return;
if(author.padIDs != null)
{
//remove pad from author
delete author.padIDs[padID];
db.set("globalAuthor:" + authorID, author);
}
});
}
\ No newline at end of file
......@@ -80,8 +80,12 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
newRevData.meta.atext = this.atext;
}
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
this.saveToDatabase();
// set the author to pad
if(author)
authorManager.addPad(author, this.id);
};
//save all attributes to the database
......@@ -102,6 +106,12 @@ Pad.prototype.saveToDatabase = function saveToDatabase(){
db.set("pad:"+this.id, dbObject);
}
// get time of last edit (changeset application)
Pad.prototype.getLastEdit = function getLastEdit(callback){
var revNum = this.getHeadRevisionNumber();
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
}
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
};
......@@ -436,6 +446,18 @@ Pad.prototype.remove = function remove(callback) {
db.remove("pad:"+padID+":revs:"+i);
}
callback();
},
//remove pad from all authors who contributed
function(callback)
{
var authorIDs = _this.getAllAuthors();
authorIDs.forEach(function (authorID)
{
authorManager.removePad(authorID, padID);
});
callback();
}
], callback);
......
......@@ -72,3 +72,33 @@ exports.getPadId = function(readOnlyId, callback)
{
db.get("readonly2pad:" + readOnlyId, callback);
}
/**
* returns a the padId and readonlyPadId in an object for any id
* @param {String} padIdOrReadonlyPadId read only id or real pad id
*/
exports.getIds = function(padIdOrReadonlyPadId, callback) {
var handleRealPadId = function () {
exports.getReadOnlyId(padIdOrReadonlyPadId, function (err, value) {
callback(null, {
readOnlyPadId: value,
padId: padIdOrReadonlyPadId,
readonly: false
});
});
}
if (padIdOrReadonlyPadId.indexOf("r.") != 0)
return handleRealPadId();
exports.getPadId(padIdOrReadonlyPadId, function (err, value) {
if(ERR(err, callback)) return;
if (value == null)
return handleRealPadId();
callback(null, {
readOnlyPadId: padIdOrReadonlyPadId,
padId: value,
readonly: true
});
});
}
......@@ -40,13 +40,14 @@ catch(e)
//a list of all functions
var functions = {
"createGroup" : [],
"createGroupIfNotExistsFor" : ["groupMapper"],
"createGroupIfNotExistsFor" : ["groupMapper"],
"deleteGroup" : ["groupID"],
"listPads" : ["groupID"],
"createPad" : ["padID", "text"],
"createGroupPad" : ["groupID", "padName", "text"],
"createAuthor" : ["name"],
"createAuthorIfNotExistsFor": ["authorMapper" , "name"],
"listPadsOfAuthor" : ["authorID"],
"createSession" : ["groupID", "authorID", "validUntil"],
"deleteSession" : ["sessionID"],
"getSessionInfo" : ["sessionID"],
......@@ -57,12 +58,15 @@ var functions = {
"getHTML" : ["padID", "rev"],
"setHTML" : ["padID", "html"],
"getRevisionsCount" : ["padID"],
"getLastEdited" : ["padID"],
"deletePad" : ["padID"],
"getReadOnlyID" : ["padID"],
"setPublicStatus" : ["padID", "publicStatus"],
"getPublicStatus" : ["padID"],
"setPassword" : ["padID", "password"],
"isPasswordProtected" : ["padID"]
"isPasswordProtected" : ["padID"],
"listAuthorsOfPad" : ["padID"],
"padUsersCount" : ["padID"]
};
/**
......
This diff is collapsed.
/**
* 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)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var async = require("async");
var padManager = require("../db/PadManager");
var Changeset = require("ep_etherpad-lite/static/js/Changeset");
var AttributePool = require("ep_etherpad-lite/static/js/AttributePool");
var settings = require('../utils/Settings');
var authorManager = require("../db/AuthorManager");
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
/**
* Saves the Socket class we need to send and recieve data from the client
*/
var socketio;
/**
* This Method is called by server.js to tell the message handler on which socket it should send
* @param socket_io The Socket
*/
exports.setSocketIO = function(socket_io)
{
socketio=socket_io;
}
/**
* Handles the connection of a new user
* @param client the new client
*/
exports.handleConnect = function(client)
{
}
/**
* Handles the disconnection of a user
* @param client the client that leaves
*/
exports.handleDisconnect = function(client)
{
}
/**
* Handles a message from a user
* @param client the client that send this message
* @param message the message from the client
*/
exports.handleMessage = function(client, message)
{
//Check what type of message we get and delegate to the other methodes
if(message.type == "CLIENT_READY")
{
handleClientReady(client, message);
}
else if(message.type == "CHANGESET_REQ")
{
handleChangesetRequest(client, message);
}
//if the message type is unkown, throw an exception
else
{
messageLogger.warn("Dropped message, unknown Message Type: '" + message.type + "'");
}
}
function handleClientReady(client, message)
{
if(message.padId == null)
{
messageLogger.warn("Dropped message, changeset request has no padId!");
return;
}
//send the timeslider client the clientVars, with this values its able to start
createTimesliderClientVars (message.padId, function(err, clientVars)
{
ERR(err);
client.json.send({type: "CLIENT_VARS", data: clientVars});
})
}
/**
* Handles a request for a rough changeset, the timeslider client needs it
*/
function handleChangesetRequest(client, message)
{
//check if all ok
if(message.data == null)
{
messageLogger.warn("Dropped message, changeset request has no data!");
return;
}
if(message.padId == null)
{
messageLogger.warn("Dropped message, changeset request has no padId!");
return;
}
if(message.data.granularity == null)
{
messageLogger.warn("Dropped message, changeset request has no granularity!");
return;
}
if(message.data.start == null)
{
messageLogger.warn("Dropped message, changeset request has no start!");
return;
}
if(message.data.requestID == null)
{
messageLogger.warn("Dropped message, changeset request has no requestID!");
return;
}
var granularity = message.data.granularity;
var start = message.data.start;
var end = start + (100 * granularity);
var padId = message.padId;
//build the requested rough changesets and send them back
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
{
ERR(err);
var data = changesetInfo;
data.requestID = message.data.requestID;
client.json.send({type: "CHANGESET_REQ", data: data});
});
}
function createTimesliderClientVars (padId, callback)
{
var clientVars = {
viewId: padId,
colorPalette: ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ff8f8f", "#ffe38f", "#c7ff8f", "#8fffab", "#8fffff", "#8fabff", "#c78fff", "#ff8fe3", "#d97979", "#d9c179", "#a9d979", "#79d991", "#79d9d9", "#7991d9", "#a979d9", "#d979c1", "#d9a9a9", "#d9cda9", "#c1d9a9", "#a9d9b5", "#a9d9d9", "#a9b5d9", "#c1a9d9", "#d9a9cd"],
savedRevisions: [],
padIdForUrl: padId,
fullWidth: false,
disableRightBar: false,
initialChangesets: [],
abiwordAvailable: settings.abiwordAvailable(),
hooks: [],
initialStyledContents: {}
};
var pad;
var initialChangesets = [];
async.series([
//get the pad from the database
function(callback)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback();
});
},
//get all saved revisions and add them
function(callback)
{