fixed merge confilcts in pad.html

This commit is contained in:
John McLear 2011-08-01 13:20:05 +01:00
commit 9911fb95b9
54 changed files with 612 additions and 263 deletions

View file

@ -34,9 +34,8 @@ The Node.js version of your Linux repository might be too old/new. Please compil
2. Install npm `curl http://npmjs.org/install.sh | sh`
3. Ensure you have installed the sqlite develob libraries, gzip and git `apt-get install libsqlite3-dev gzip git-core`
4. Clone the git repository `git clone 'git://github.com/Pita/etherpad-lite.git'`
5. Install the dependencies `cd etherpad-lite && npm install`
6. Start it with `bin/run.sh`
7. Open your web browser and visit <http://localhost:9001>
5. Start it with `bin/run.sh` (the first run will install all dependencies)
6. Open your web browser and visit <http://localhost:9001>
# Next Steps
You can modify the settings in the file settings.json
@ -60,4 +59,4 @@ You can join the [mailinglist](http://groups.google.com/group/etherpad-lite-dev)
You also help the project, if you only host a ep-lite instance and share your experience with us.
# License
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)

30
bin/debugRun.sh Executable file
View file

@ -0,0 +1,30 @@
#!/bin/sh
#Move to the folder where ep-lite is installed
FOLDER=$(dirname $(readlink -f $0))
cd $FOLDER
#Was this script started in the bin folder? if yes move out
if [ -d "../bin" ]; then
cd "../"
fi
#prepare the enviroment
bin/installDeps.sh || exit 1
hash node-inspector > /dev/null 2>&1 || {
echo "You need to install node-inspector to run the tests!" >&2
echo "You can install it with npm" >&2
echo "Run: npm install -g node-inspector" >&2
exit 1
}
node-inspector &
echo "If you new to node-inspector, take a look at this video: http://youtu.be/AOnK3NVnxL8"
cd "node"
node --debug server.js
#kill node-inspector before ending
kill $!

View file

@ -1,15 +1,20 @@
#!/bin/bash
#!/bin/sh
#Move to the folder where ep-lite is installed
FOLDER=$(dirname $(readlink -f $0))
cd $FOLDER
#Was this script started in the bin folder? if yes move out
if [ -d "../bin" ]; then
cd "../"
fi
type -P node &>/dev/null || {
hash node > /dev/null 2>&1 || {
echo "You need to install node!" >&2
exit 1
}
type -P doc.md &>/dev/null || {
hash doc.md > /dev/null 2>&1 || {
echo "You need to install doc.md! npm install -g doc.md" >&2
exit 1
}

71
bin/installDeps.sh Executable file
View file

@ -0,0 +1,71 @@
#!/bin/sh
#Move to the folder where ep-lite is installed
FOLDER=$(dirname $(readlink -f $0))
cd $FOLDER
#Was this script started in the bin folder? if yes move out
if [ -d "../bin" ]; then
cd "../"
fi
#Is wget installed?
hash wget > /dev/null 2>&1 || {
echo "Please install wget" >&2
exit 1
}
#Is node installed?
hash node > /dev/null 2>&1 || {
echo "Please install node.js ( http://nodesjs.org )" >&2
exit 1
}
#Is npm installed?
hash npm > /dev/null 2>&1 || {
echo "Please install npm ( http://npmjs.org )" >&2
exit 1
}
#Does a settings.json exist? if no copy the template
if [ ! -f "settings.json" ]; then
echo "Copy the settings template to settings.json..."
cp -v settings.json.template settings.json || exit 1
fi
echo "Ensure that all dependencies are up to date..."
npm install || exit 1
echo "Ensure jQuery is downloaded and up to date..."
DOWNLOAD_JQUERY="true"
NEEDED_VERSION="1.6.2"
if [ -f "static/js/jquery.min.js" ]; then
VERSION=$(cat static/js/jquery.min.js | head -n 2 | tail -n 1 | grep -o "v[0-9]*\.[0-9]*\.[0-9]*");
if [ ${VERSION#v} = $NEEDED_VERSION ]; then
DOWNLOAD_JQUERY="false"
fi
fi
if [ $DOWNLOAD_JQUERY = "true" ]; then
wget -O static/js/jquery.min.js http://code.jquery.com/jquery-$NEEDED_VERSION.min.js || exit 1
fi
#Remove all minified data to force node creating it new
echo "Clear minfified cache..."
rm -f var/minified*
echo "ensure custom css/js files are created..."
for f in "index" "pad" "timeslider"
do
if [ ! -f "static/custom/$f.js" ]; then
cp -v "static/custom/js.template" "static/custom/$f.js" || exit 1
fi
if [ ! -f "static/custom/$f.css" ]; then
cp -v "static/custom/css.template" "static/custom/$f.css" || exit 1
fi
done
exit 0

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
#Move to the folder where ep-lite is installed
FOLDER=$(dirname $(readlink -f $0))
@ -10,51 +10,14 @@ if [ -d "../bin" ]; then
fi
#Stop the script if its started as root
if [[ $EUID -eq 0 ]]; then
if [ "$(id -u)" -eq 0 ]; then
echo "You shouldn't start Etherpad-Lite as root!" 1>&2
echo "Use authbind if you want to use a port lower than 1024 -> http://en.wikipedia.org/wiki/Authbind" 1>&2
exit 1
fi
#Is node installed?
type -P node &>/dev/null || {
echo "You need to install node to run Etherpad-Lite!" >&2
exit 1
}
#Is npm installed?
type -P npm &>/dev/null || {
echo "You need to install npm to run Etherpad-Lite!" >&2
exit 1
}
#Does a settings.json exist? if no copy the template
if [ ! -f "settings.json" ]; then
echo "Copy the settings template to settings.json..."
cp -v settings.json.template settings.json
fi
echo "Ensure that all dependencies are up to date..."
npm install
echo "Ensure jQuery is downloaded and up to date..."
DOWNLOAD_JQUERY="true"
NEEDED_VERSION="1.6.2"
if [ -f "static/js/jquery.min.js" ]; then
VERSION=$(cat static/js/jquery.min.js | head -n 2 | tail -n 1 | grep -o "v[0-9]*\.[0-9]*\.[0-9]*");
if [[ ${VERSION:1} = $NEEDED_VERSION ]]; then
DOWNLOAD_JQUERY="false"
fi
fi
if [[ $DOWNLOAD_JQUERY = "true" ]]; then
wget -O static/js/jquery.min.js http://code.jquery.com/jquery-$NEEDED_VERSION.min.js
fi
#Remove all minified data to force node creating it new
echo "Clear minfified cache..."
rm var/minified* 2> /dev/null
#prepare the enviroment
bin/installDeps.sh || exit 1
#Move to the node folder and start
echo "start..."

View file

@ -1,19 +0,0 @@
#!/bin/bash
type -P node-inspector &>/dev/null || {
echo "You need to install node-inspector to run the tests!" >&2
echo "You can install it with npm" >&2
echo "Run: npm install node-inspector" >&2
exit 1
}
node-inspector &
echo "If you new to node-inspector, take a look at this video: http://youtu.be/AOnK3NVnxL8"
if [ -d "../bin" ]; then
cd "../"
fi
cd "node"
node --debug server.js

View file

@ -1,14 +0,0 @@
#!/bin/bash
type -P nodeunit &>/dev/null || {
echo "You need to install Nodeunit to run the tests!" >&2
echo "You can install it with npm" >&2
echo "Run: npm install nodeunit" >&2
exit 1
}
if [ -d "../bin" ]; then
cd "../"
fi
nodeunit tests

View file

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
#This script ensures that ep-lite is automatically restarting after an error happens
@ -49,12 +49,12 @@ do
bin/run.sh >>$1 2>>$1
#Send email
if [ $ERROR_HANDLING == 1 ]; then
if [ $ERROR_HANDLING = 1 ]; then
TIME_NOW=$(date +%s)
TIME_SINCE_LAST_SEND=$(($TIME_NOW - $LAST_EMAIL_SEND))
if [ $TIME_SINCE_LAST_SEND -gt $TIME_BETWEEN_EMAILS ]; then
echo -e "Server was restared at: $(date)\nThe last 50 lines of the log before the error happens:\n $(tail -n 50 $1)" | mail -s "Pad Server was restarted" $EMAIL_ADDRESS
printf "Server was restared at: $(date)\nThe last 50 lines of the log before the error happens:\n $(tail -n 50 $1)" | mail -s "Pad Server was restarted" $EMAIL_ADDRESS
LAST_EMAIL_SEND=$TIME_NOW
fi

View file

@ -1,10 +1,17 @@
# AuthorManager
`require("./AuthorManager");`
# db/AuthorMana
`require("./db/AuthorManager");`
The AuthorManager controlls all information about the Pad authors
## Functions
- - -
### getAuthor (author, callback)
Internal function that creates the database entry for an author
* **author** *(String)* The id of the author
* **callback** *(Function)* callback(err, authorObj)
- - -
### getAuthor4Token (token, callback)
Returns the Author Id for a token. If the token is unkown,

View file

@ -1,5 +1,5 @@
# db
`require("./db");`
`require("./db/DB");`
The DB Module provides a database initalized with the settings
provided by the settings module

View file

@ -1,5 +1,5 @@
# Mod
`require("./Models/Pad");`
# db/
`require("./db/Pad");`
The pad object, defined with joose

View file

@ -1,5 +1,5 @@
# PadManager
`require("./PadManager");`
# db/PadMana
`require("./db/PadManager");`
The Pad Manager is a Factory for pad Objects

View file

@ -0,0 +1,21 @@
# db/ReadOnlyMana
`require("./db/ReadOnlyManager");`
The ReadOnlyManager manages the database and rendering releated to read only pads
## Functions
- - -
### getPadId (readOnlyId, callback)
returns a the padId for a read only id
* **readOnlyId** *(String)* read only id
* **callback** *No description*
- - -
### getReadOnlyId (padId, callback)
returns a read only id for a pad
* **padId** *(String)* the id of the pad
* **callback** *No description*

View file

@ -0,0 +1,16 @@
# handler/Expor
`require("./handler/ExportHandler");`
Handles the export requests
## Functions
- - -
### doExport (req, res, padId, type)
do a requested export
* **req** *No description*
* **res** *No description*
* **padId** *No description*
* **type** *No description*

View file

@ -0,0 +1,15 @@
# handler/Impor
`require("./handler/ImportHandler");`
Handles the import requests
## Functions
- - -
### doImport (req, res, padId)
do a requested import
* **req** *No description*
* **res** *No description*
* **padId** *No description*

View file

@ -0,0 +1,38 @@
# handler/PadMessag
`require("./handler/PadMessageHandler");`
The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
## Functions
- - -
### handleConnect (client)
Handles the connection of a new user
* **client** the new client
- - -
### handleDisconnect (client)
Handles the disconnection of a user
* **client** the client that leaves
- - -
### handleMessage (client, message)
Handles a message from a user
* **client** the client that send this message
* **message** the message from the client
- - -
### setSocketIO (socket_io)
A associative array that translates a session to a pad
* **socket_io** The Socket
- - -
### updatePadClients (pad, callback)
* **pad** *No description*
* **callback** *No description*

View file

@ -0,0 +1,23 @@
# handler/Socket
`require("./handler/SocketIORouter");`
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
## Functions
- - -
### addComponent (moduleName, module)
Saves all components
key is the component name
value is the component module
* **moduleName** *No description*
* **module** *No description*
- - -
### setSocketIO (_socket)
sets the socket.io and adds event functions for routing
* **_socket** *No description*

View file

@ -1,5 +1,5 @@
# MessageHandler
`require("./MessageHandler");`
# handler/TimesliderMessag
`require("./handler/TimesliderMessageHandler");`
The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
@ -26,7 +26,7 @@ Handles a message from a user
- - -
### setSocketIO (socket_io)
A associative array that translates a session to a pad
Saves the Socket class we need to send and recieve data from the client
* **socket_io** The Socket

View file

@ -0,0 +1,15 @@
# utils/A
`require("./utils/Abiword");`
Controls the communication with the Abiword application
## Functions
- - -
### convertFile (srcFile, destFile, type, callback)
* **srcFile** *No description*
* **destFile** *No description*
* **type** *No description*
* **callback** *No description*

View file

@ -1,5 +1,5 @@
# AttributePoolFactory
`require("./AttributePoolFactory");`
# utils/AttributePoolF
`require("./utils/AttributePoolFactory");`
This code represents the Attribute Pool Object of the original Etherpad.
90% of the code is still like in the original Etherpad

View file

@ -1,5 +1,5 @@
# Changeset
`require("./Changeset");`
# utils/Cha
`require("./utils/Changeset");`
## Functions

View file

@ -0,0 +1,24 @@
# utils/Expo
`require("./utils/ExportHtml");`
Copyright 2009 Google Inc.
* 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.
## Functions
- - -
### getPadHTMLDocument (padId, revNum, noDocType, callback)
* **padId** *No description*
* **revNum** *No description*
* **noDocType** *No description*
* **callback** *No description*

View file

@ -1,5 +1,5 @@
# minify
`require("./minify");`
# utils/
`require("./utils/Minify");`
This Module manages all /minified/* requests. It controls the
minification && compression of Javascript and CSS.
@ -7,9 +7,10 @@ minification && compression of Javascript and CSS.
## Functions
- - -
### padJS (req, res)
Answers a http request for the pad javascript
### minifyJS (req, res, jsFilename)
creates the minifed javascript for the given minified name
* **req** the Express request
* **res** the Express response
* **jsFilename** *No description*

View file

@ -1,11 +1,15 @@
# settings
`require("./settings");`
# utils/Se
`require("./utils/Settings");`
The Settings Modul reads the settings out of settings.json and provides
this information to the other modules
##Variables
- - -
### abiword
The path of the abiword executable
- - -
### dbSettings
This setting is passed with dbType to ueberDB to set up the database
@ -18,6 +22,10 @@ This setting is passed with dbType to ueberDB to set up the database
### defaultPadText
The default Text of a new pad
- - -
### ip
The IP ep-lite should listen to
- - -
### logHTTP
A flag that shows if http requests should be loged to stdout

13
node/README.md Normal file
View file

@ -0,0 +1,13 @@
# About the folder structure
* **db** - all modules that are accesing the data structure and are communicating directly to the database
* **handler** - all modules that responds directly to requests/messages of the browser
* **utils** - helper modules
# Module name conventions
Module file names starts with a capital letter and uses camelCase
# Where does it start?
server.js is started directly

View file

@ -18,7 +18,7 @@
* limitations under the License.
*/
var db = require("./db").db;
var db = require("./DB").db;
var async = require("async");
/**
@ -32,38 +32,48 @@ exports.getAuthor4Token = function (token, callback)
{
var author;
async.waterfall([
async.series([
//try to get the author for this token
function(callback)
{
db.get("token2author:" + token, callback);
db.get("token2author:" + token, function (err, _author)
{
author = _author;
callback(err);
});
},
function(value, callback)
function(callback)
{
//there is no author with this token, so create one
if(value == null)
if(author == null)
{
//create the new author name
author = "g." + _randomString(16);
//set the token2author db entry
db.set("token2author:" + token, author);
//set the globalAuthors db entry
var authorObj = {colorId : Math.floor(Math.random()*32), name: null, timestamp: new Date().getTime()};
db.set("globalAuthor:" + author, authorObj);
callback(null);
createAuthor(token, function(err, _author)
{
author = _author;
callback(err);
});
}
//there is a author with this token
else
{
author = value;
//update the author time
db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime());
callback(null);
//check if there is also an author object for this token, if not, create one
db.get("globalAuthor:" + author, function(err, authorObject)
{
if(authorObject == null)
{
createAuthor(token, function(err, _author)
{
author = _author;
callback(err);
});
}
//the author exists, update the timestamp of this author
else
{
db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime());
callback();
}
});
}
}
], function(err)
@ -72,6 +82,36 @@ exports.getAuthor4Token = function (token, callback)
});
}
/**
* Internal function that creates the database entry for an author
* @param {String} token The token
*/
function createAuthor (token, callback)
{
//create the new author name
var author = "g." + _randomString(16);
//create the globalAuthors db entry
var authorObj = {colorId : Math.floor(Math.random()*32), name: null, timestamp: new Date().getTime()};
//we do this in series to ensure this db entries are written in the correct order
async.series([
//set the global author db entry
function(callback)
{
db.set("globalAuthor:" + author, authorObj, callback);
},
//set the token2author db entry
function(callback)
{
db.set("token2author:" + token, author, callback);
}
], function(err)
{
callback(err, author);
});
}
/**
* Returns the Author Obj of the author
* @param {String} author The id of the author

View file

@ -20,7 +20,7 @@
*/
var ueberDB = require("ueberDB");
var settings = require("./settings");
var settings = require("../utils/Settings");
//set database settings
var db = new ueberDB.database(settings.dbType, settings.dbSettings);

View file

@ -2,12 +2,12 @@
* The pad object, defined with joose
*/
var Changeset = require("../Changeset");
var AttributePoolFactory = require("../AttributePoolFactory");
var db = require("../db").db;
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
var db = require("./DB").db;
var async = require("async");
var settings = require('../settings');
var authorManager = require("../AuthorManager");
var settings = require('../utils/Settings');
var authorManager = require("./AuthorManager");
/**
* Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
@ -214,7 +214,7 @@ Class('Pad', {
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
},
getChatMessage: function(entryNum, withName, callback)
getChatMessage: function(entryNum, callback)
{
var _this = this;
var entry;
@ -231,9 +231,9 @@ Class('Pad', {
},
//add the authorName
function(callback)
{
//skip if we don't need the authorName
if(!withName)
{
//this chat message doesn't exist, return null
if(entry == null)
{
callback();
return;
@ -287,14 +287,26 @@ Class('Pad', {
var entries = [];
async.forEach(neededEntries, function(entryObject, callback)
{
_this.getChatMessage(entryObject.entryNum, true, function(err, entry)
_this.getChatMessage(entryObject.entryNum, function(err, entry)
{
entries[entryObject.order] = entry;
callback(err);
});
}, function(err)
{
callback(err, entries);
//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(err, cleanedEntries);
});
},

View file

@ -18,7 +18,7 @@
* limitations under the License.
*/
var Changeset = require("./Models/Pad");
require("../db/Pad");
/**
* A Array with all known Pads

View file

@ -18,7 +18,7 @@
* limitations under the License.
*/
var db = require("./db").db;
var db = require("./DB").db;
var async = require("async");
/**

View file

@ -18,15 +18,15 @@
* limitations under the License.
*/
var exporthtml = require("./exporters/exporthtml");
var padManager = require("./PadManager");
var exporthtml = require("../utils/ExportHtml");
var padManager = require("../db/PadManager");
var async = require("async");
var fs = require("fs");
var settings = require('./settings');
var settings = require('../utils/Settings');
//load abiword only if its enabled
if(settings.abiword != null)
var abiword = require("./Abiword");
var abiword = require("../utils/Abiword");
/**
* do a requested export

View file

@ -18,16 +18,16 @@
* limitations under the License.
*/
var padManager = require("./PadManager");
var padManager = require("../db/PadManager");
var padMessageHandler = require("./PadMessageHandler");
var async = require("async");
var fs = require("fs");
var settings = require('./settings');
var settings = require('../utils/Settings');
var formidable = require('formidable');
//load abiword only if its enabled
if(settings.abiword != null)
var abiword = require("./Abiword");
var abiword = require("../utils/Abiword");
/**
* do a requested import

View file

@ -19,11 +19,12 @@
*/
var async = require("async");
var padManager = require("./PadManager");
var Changeset = require("./Changeset");
var AttributePoolFactory = require("./AttributePoolFactory");
var authorManager = require("./AuthorManager");
var readOnlyManager = require("./ReadOnlyManager");
var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
var authorManager = require("../db/AuthorManager");
var readOnlyManager = require("../db/ReadOnlyManager");
var settings = require('../utils/Settings');
/**
* A associative array that translates a session to a pad
@ -84,7 +85,7 @@ exports.handleDisconnect = function(client)
var sessionPad=session2pad[client.id];
//if this connection was already etablished with a handshake, send a disconnect message to the others
if(sessioninfos[client.id].author)
if(sessioninfos[client.id] && sessioninfos[client.id].author)
{
var author = sessioninfos[client.id].author;
@ -318,11 +319,6 @@ function handleUserInfoUpdate(client, message)
}
}
function errlog(name, value)
{
console.error(name+"=" + JSON.stringify(value));
}
/**
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations
* This Method is nearly 90% copied out of the Etherpad Source Code. So I can't tell you what happens here exactly
@ -414,9 +410,13 @@ function handleUserChanges(client, message)
function (callback)
{
var prevText = pad.text();
if (Changeset.oldLen(changeset) != prevText.length) {
throw "Can't apply USER_CHANGES "+changeset+" with oldLen "
+ Changeset.oldLen(changeset) + " to document of length " + 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);
client.json.send({disconnect:"badChangeset"});
callback();
return;
}
var thisAuthor = sessioninfos[client.id].author;
@ -644,6 +644,7 @@ function handleClientReady(client, message)
{
authorManager.getAuthor(authorId, function(err, author)
{
delete author.timestamp;
historicalAuthorData[authorId] = author;
callback(err);
});
@ -705,7 +706,7 @@ function handleClientReady(client, message)
},
"collab_client_vars": {
"initialAttributedText": atext,
"clientIp": (client.request && client.request.connection) ? client.request.connection.remoteAddress : "127.0.0.1",
"clientIp": "127.0.0.1",
//"clientAgent": "Anonymous Agent",
"padId": message.padId,
"historicalAuthorData": historicalAuthorData,
@ -714,7 +715,7 @@ function handleClientReady(client, message)
"globalPadId": message.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"],
"clientIp": (client.request && client.request.connection) ? client.request.connection.remoteAddress : "127.0.0.1",
"clientIp": "127.0.0.1",
"userIsGuest": true,
"userColor": authorColorId,
"padId": message.padId,
@ -731,6 +732,7 @@ function handleClientReady(client, message)
"fullWidth": false,
"hideSidebar": false
},
"abiwordAvailable": settings.abiword != null,
"hooks": {}
}

View file

@ -19,6 +19,9 @@
* limitations under the License.
*/
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
/**
* Saves all components
* key is the component name
@ -54,7 +57,7 @@ exports.setSocketIO = function(_socket)
client._send = client.send;
client.send = function(message)
{
console.log(new Date().toUTCString() + ": message to " + client.id + ": " + JSON.stringify(message));
messageLogger.info("to " + client.id + ": " + JSON.stringify(message));
client._send(message);
}
@ -68,14 +71,14 @@ exports.setSocketIO = function(_socket)
{
if(message.protocolVersion && message.protocolVersion != 2)
{
console.error("Protocolversion header is not correct:" + JSON.stringify(message));
messageLogger.warn("Protocolversion header is not correct:" + JSON.stringify(message));
return;
}
//route this message to the correct component, if possible
if(message.component && components[message.component])
{
console.log(new Date().toUTCString() + ": message from " + client.id + ": " + JSON.stringify(message));
messageLogger.info("from " + client.id + ": " + JSON.stringify(message));
//check if component is registered in the components array
if(components[message.component])
@ -85,7 +88,7 @@ exports.setSocketIO = function(_socket)
}
else
{
console.error("Can't route the message:" + JSON.stringify(message));
messageLogger.error("Can't route the message:" + JSON.stringify(message));
}
});

View file

@ -19,10 +19,10 @@
*/
var async = require("async");
var padManager = require("./PadManager");
var Changeset = require("./Changeset");
var AttributePoolFactory = require("./AttributePoolFactory");
var authorManager = require("./AuthorManager");
var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
var authorManager = require("../db/AuthorManager");
/**
* Saves the Socket class we need to send and recieve data from the client

View file

@ -24,14 +24,15 @@ require('joose');
var socketio = require('socket.io');
var fs = require('fs');
var settings = require('./settings');
var socketIORouter = require("./SocketIORouter");
var db = require('./db');
var settings = require('./utils/Settings');
var socketIORouter = require("./handler/SocketIORouter");
var db = require('./db/DB');
var async = require('async');
var express = require('express');
var path = require('path');
var minify = require('./minify');
var minify = require('./utils/Minify');
var formidable = require('formidable');
var log4js = require('log4js');
var exportHandler;
var importHandler;
var exporthtml;
@ -48,7 +49,7 @@ try
}
catch(e)
{
console.error("Can't get git version for server header\n" + e.message)
console.warn("Can't get git version for server header\n" + e.message)
}
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
@ -69,14 +70,17 @@ async.waterfall([
var app = express.createServer();
//load modules that needs a initalized db
readOnlyManager = require("./ReadOnlyManager");
exporthtml = require("./exporters/exporthtml");
exportHandler = require('./ExportHandler');
importHandler = require('./ImportHandler');
readOnlyManager = require("./db/ReadOnlyManager");
exporthtml = require("./utils/ExportHtml");
exportHandler = require('./handler/ExportHandler');
importHandler = require('./handler/ImportHandler');
//set logging
if(settings.logHTTP)
app.use(express.logger({ format: ':date: :status, :method :url' }));
//install logging
var httpLogger = log4js.getLogger("http");
app.configure(function()
{
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
});
//serve static files
app.get('/static/*', function(req, res)
@ -87,19 +91,19 @@ async.waterfall([
});
//serve minified files
app.get('/minified/:id', function(req, res)
app.get('/minified/:id', function(req, res, next)
{
res.header("Server", serverName);
var id = req.params.id;
if(id == "pad.js")
if(id == "pad.js" || id == "timeslider.js")
{
minify.padJS(req,res);
minify.minifyJS(req,res,id);
}
else
{
res.send('404 - Not Found', 404);
next();
}
});
@ -224,7 +228,7 @@ async.waterfall([
{
new formidable.IncomingForm().parse(req, function(err, fields, files)
{
console.log(new Date().toUTCString() + ": DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
res.end("OK");
});
});
@ -254,8 +258,8 @@ async.waterfall([
});
//let the server listen
app.listen(settings.port);
console.log("Server is listening at port " + settings.port);
app.listen(settings.port, settings.ip);
console.log("Server is listening at " + settings.ip + ":" + settings.port);
//init socket.io and redirect all requests to the MessageHandler
var io = socketio.listen(app);
@ -264,11 +268,33 @@ async.waterfall([
//we should remove this when the new socket.io version is more stable
io.set('transports', ['xhr-polling']);
//reduce the log level
io.set('log level', 2);
var socketIOLogger = log4js.getLogger("socket.io");
io.set('logger', {
debug: function (str)
{
//supress debug messages
//socketIOLogger.debug(str);
},
info: function (str)
{
socketIOLogger.info(str);
},
warn: function (str)
{
socketIOLogger.warn(str);
},
error: function (str)
{
socketIOLogger.error(str);
},
});
var padMessageHandler = require("./PadMessageHandler");
var timesliderMessageHandler = require("./TimesliderMessageHandler");
//minify socket.io javascript
if(settings.minify)
io.enable('browser client minification');
var padMessageHandler = require("./handler/PadMessageHandler");
var timesliderMessageHandler = require("./handler/TimesliderMessageHandler");
//Initalize the Socket.IO Router
socketIORouter.setSocketIO(io);

View file

@ -21,7 +21,7 @@
var util = require('util');
var spawn = require('child_process').spawn;
var async = require("async");
var settings = require("./settings");
var settings = require("./Settings");
//Queue with the converts we have to do
var queue = async.queue(doConvertTask, 1);

View file

@ -14,8 +14,8 @@
* limitations under the License.
*/
var async = require("async");
var Changeset = require("../Changeset");
var padManager = require("../PadManager");
var Changeset = require("./Changeset");
var padManager = require("../db/PadManager");
function getPadPlainText(pad, revNum) {

View file

@ -19,7 +19,7 @@
* limitations under the License.
*/
var settings = require('./settings');
var settings = require('./Settings');
var async = require('async');
var fs = require('fs');
var cleanCSS = require('clean-css');
@ -28,18 +28,34 @@ var pro = require("uglify-js").uglify;
var path = require('path');
var Buffer = require('buffer').Buffer;
var gzip = require('gzip');
var server = require('./server');
var server = require('../server');
var padJS = ["jquery.min.js", "pad_utils.js", "plugins.js", "undo-xpopup.js", "json2.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "ace.js", "collab_client.js", "pad_userlist.js", "pad_impexp.js", "pad_savedrevs.js", "pad_connectionstatus.js", "pad2.js", "jquery-ui.js", "chat.js"];
var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"];
/**
* Answers a http request for the pad javascript
* creates the minifed javascript for the given minified name
* @param req the Express request
* @param res the Express response
*/
exports.padJS = function(req, res)
exports.minifyJS = function(req, res, jsFilename)
{
res.header("Content-Type","text/javascript");
var jsFiles = ["jquery.min.js", "pad_utils.js", "plugins.js", "undo-xpopup.js", "json2.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "ace.js", "collab_client.js", "pad_userlist.js", "pad_impexp.js", "pad_savedrevs.js", "pad_connectionstatus.js", "pad2.js", "jquery-ui.js", "chat.js"];
//choose the js files we need
if(jsFilename == "pad.js")
{
jsFiles = padJS;
}
else if(jsFilename == "timeslider.js")
{
jsFiles = timesliderJS;
}
else
{
throw new Error("there is no profile for creating " + name);
}
//minifying is enabled
if(settings.minify)
@ -91,7 +107,7 @@ exports.padJS = function(req, res)
function(callback)
{
//check the modification time of the minified js
fs.stat("../var/minified_pad.js", function(err, stats)
fs.stat("../var/minified_" + jsFilename, function(err, stats)
{
if(err && err.code != "ENOENT") callback(err);
@ -122,6 +138,13 @@ exports.padJS = function(req, res)
//find all includes in ace.js and embed them
function(callback)
{
//if this is not the creation of pad.js, skip this part
if(jsFilename != "pad.js")
{
callback();
return;
}
var founds = fileValues["ace.js"].match(/\$\$INCLUDE_[a-zA-Z_]+\([a-zA-Z0-9.\/_"]+\)/gi);
//go trough all includes
@ -194,7 +217,7 @@ exports.padJS = function(req, res)
//write the results plain in a file
function(callback)
{
fs.writeFile("../var/minified_pad.js", result, "utf8", callback);
fs.writeFile("../var/minified_" + jsFilename, result, "utf8", callback);
},
//write the results compressed in a file
function(callback)
@ -202,7 +225,7 @@ exports.padJS = function(req, res)
gzip(result, 9, function(err, compressedResult){
if(err) {callback(err); return}
fs.writeFile("../var/minified_pad.js.gz", compressedResult, callback);
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
});
}
],callback);
@ -217,12 +240,12 @@ exports.padJS = function(req, res)
var pathStr;
if(gzipSupport)
{
pathStr = path.normalize(__dirname + "/../var/minified_pad.js.gz");
pathStr = path.normalize(__dirname + "/../../var/minified_" + jsFilename + ".gz");
res.header('Content-Encoding', 'gzip');
}
else
{
pathStr = path.normalize(__dirname + "/../var/minified_pad.js");
pathStr = path.normalize(__dirname + "/../../var/minified_" + jsFilename );
}
res.sendfile(pathStr, { maxAge: server.maxAge });

View file

@ -21,6 +21,11 @@
var fs = require("fs");
/**
* The IP ep-lite should listen to
*/
exports.ip = "0.0.0.0";
/**
* The Port ep-lite should listen to
*/
@ -33,10 +38,6 @@ exports.dbType = "sqlite";
* This setting is passed with dbType to ueberDB to set up the database
*/
exports.dbSettings = { "filename" : "../var/sqlite.db" };
/**
* A flag that shows if http requests should be loged to stdout
*/
exports.logHTTP = true;
/**
* The default Text of a new pad
*/
@ -88,6 +89,6 @@ for(var i in settings)
else
{
console.error("WARNING: Unkown Setting: '" + i + "'");
console.error("If this isn't a mistake, add the default settings for this value to node/settings.js");
console.error("This setting doesn't exist or it was removed");
}
}

View file

@ -6,18 +6,20 @@
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com>",
"contributors": [
{ "name": "John McLear",
"name": "Hans Pinckaers"}
"name": "Hans Pinckaers",
"name": "Robin Buse"}
],
"dependencies" : {
"socket.io" : "0.7.7",
"ueberDB" : "0.0.10",
"ueberDB" : "0.0.12",
"async" : "0.1.9",
"joose" : "3.18.0",
"express" : "2.4.3",
"clean-css" : "0.2.4",
"uglify-js" : "1.0.6",
"gzip" : "0.1.0",
"formidable" : "1.0.2"
"formidable" : "1.0.2",
"log4js" : "0.3.7"
},
"version" : "0.0.4"
}

View file

@ -4,6 +4,8 @@
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
@ -23,9 +25,6 @@
},
*/
//if true, every http request will be loged to stdout
"logHTTP" : true,
//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",

3
static/custom/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*
!js.template
!css.template

View file

@ -0,0 +1,5 @@
/*
You may have to use !important to override css attributs, for example:
* {color: blue !important;}
*/

View file

@ -0,0 +1,6 @@
function costumStart()
{
//define your javascript here
//jquery is avaiable - except index.js
//you can load extra scripts with $.getScript http://api.jquery.com/jQuery.getScript/
}

View file

@ -81,11 +81,13 @@
width: 40px;
}
</style>
<link href="static/custom/index.css" rel="stylesheet">
<script src="static/custom/index.js"></script>
<div id="container">
<div id="button" onclick="go2Random()">New Pad</div>
<br>
<div class="label">or create/open a Pad with the name</div>
<form action="" onsubmit="go2Name();return false;">
<form action="#" onsubmit="go2Name();return false;">
<input type="text" id="padname" autofocus>
<input type="submit" value="OK">
</form>
@ -111,7 +113,7 @@
function randomPadName()
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var string_length = 10;
var randomstring = '';
for (var i = 0; i < string_length; i++)
@ -121,5 +123,8 @@
}
return randomstring;
}
//start the costum js
if(costumStart) costumStart();
</script>
</html>
</html>

View file

@ -20,7 +20,7 @@ function makeCSSManager(emptyStylesheetTitle, top)
function getSheetByTitle(title, top)
{
if(top)
var allSheets = window.top.document.styleSheets;
var allSheets = window.parent.parent.document.styleSheets;
else
var allSheets = document.styleSheets;

View file

@ -29,6 +29,9 @@ $(document).ready(function()
document.location = expectedURL;
}
//start the costum js
if(costumStart) costumStart();
handshake();
});
@ -64,7 +67,7 @@ function readCookie(name)
function randomString()
{
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var string_length = 20;
var randomstring = '';
for (var i = 0; i < string_length; i++)
@ -126,8 +129,8 @@ function handshake()
receivedClientVars = true;
clientVars = obj;
clientVars.userAgent = navigator.userAgent;
clientVars.collab_client_vars.clientAgent = navigator.userAgent;
clientVars.userAgent = "Anonymous";
clientVars.collab_client_vars.clientAgent = "Anonymous";
pad.init();

View file

@ -101,13 +101,13 @@ var padeditbar = (function()
else if (cmd == 'embed')
{
var padurl = document.location;
$('#embedinput').val("<iframe src='" + padurl + "' width=500 height=400>");
$('#embedinput').val("<iframe src='" + padurl + "' width=600 height=400>");
self.toogleDropDown("embed");
$('#embedinput').focus().select();
}
else if (cmd == 'import_export')
{
self.toogleDropDown("importexport");
self.toogleDropDown("importexport");
}
else if (cmd == 'readonly')

View file

@ -233,12 +233,26 @@ var padimpexp = (function()
// build the export links
$("#exporthtmla").attr("href", document.location.href + "/export/html");
$("#exportplaina").attr("href", document.location.href + "/export/txt");
$("#exportworda").attr("href", document.location.href + "/export/doc");
$("#exportpdfa").attr("href", document.location.href + "/export/pdf");
$("#exportopena").attr("href", document.location.href + "/export/odt");
$("#exportwordlea").attr("href", document.location.href + "/export/wordle");
$("#importform").get(0).setAttribute('action', document.location.href + "/import");
//hide stuff thats not avaible if abiword is disabled
if(!clientVars.abiwordAvailable)
{
$("#exportworda").remove();
$("#exportpdfa").remove();
$("#exportopena").remove();
$("#importexport").css({"height":"95px"});
$("#importexportline").css({"height":"95px"});
$("#import").html("Import is not available");
}
else
{
$("#exportworda").attr("href", document.location.href + "/export/doc");
$("#exportpdfa").attr("href", document.location.href + "/export/pdf");
$("#exportopena").attr("href", document.location.href + "/export/odt");
$("#importform").get(0).setAttribute('action', document.location.href + "/import");
}
$("#impexp-close").click(function()
{
@ -275,9 +289,7 @@ var padimpexp = (function()
},
export2Wordle: function()
{
padUrl = location.pathname;
padHost = location.host;
var padUrl = "http://" + padHost + padUrl + "/export/txt";
var padUrl = document.location.href + "/export/txt";
$.get(padUrl, function(data)
{

View file

@ -13,6 +13,8 @@
</script>
<script src="../socket.io/socket.io.js"></script>
<script src="../minified/pad.js"></script>
<link href="../static/custom/pad.css" rel="stylesheet">
<script src="../static/custom/pad.js"></script>
<style type="text/css" title="dynamicsyntax"></style>
</head>
@ -79,17 +81,17 @@
<ul id="menu_right">
<li>
<a onClick="window.pad&&pad.editbarClick('readonly');return false;" title="Create a readonly link for this pad">
<a id="readonlylink" onClick="window.pad&&pad.editbarClick('readonly');return false;" title="Create a readonly link for this pad">
<div class="buttonicon" style="background-position:0px -150px"></div>
</a>
</li>
<li>
<a onClick="window.pad&&pad.editbarClick('import_export');return false;" title="Import/Export from/to different document formats">
<a id="exportlink" onClick="window.pad&&pad.editbarClick('import_export');return false;" title="Import/Export from/to different document formats">
<div class="buttonicon" style="background-position:0px -68px"></div>
</a>
</li>
<li>
<a onClick="window.pad&&pad.editbarClick('embed');return false;" title="Embed this pad">
<a id="embedlink" onClick="window.pad&&pad.editbarClick('embed');return false;" title="Embed this pad">
<div class="buttonicon" style="background-position:0px -18px"></div>
</a>
</li>
@ -219,7 +221,7 @@ We removed this feature cause its not worth the space it needs in the editbar
<a id="exportworda" target="_blank" class="exportlink"><div class="exporttype" id="exportword">Microsoft Word</div></a>
<a id="exportpdfa" target="_blank" class="exportlink"><div class="exporttype" id="exportpdf">PDF</div></a>
<a id="exportopena" target="_blank" class="exportlink"><div class="exporttype" id="exportopen">OpenDocument</div></a>
<a id="exportwordlea" target="_blank" onClick="loadCont();return false;" class="exportlink"><div class="exporttype" id="exportwordle">Wordle</div></a>
<a id="exportwordlea" target="_blank" onClick="padimpexp.export2Wordle();return false;" class="exportlink"><div class="exporttype" id="exportwordle">Wordle</div></a>
<form id="wordlepost" name="wall" action="http://wordle.net/advanced" method="POST" style="margin-left:0px;">
<div id="hidetext" style=""><textarea id="text" name="text" id="text" style="display:none;">Coming soon!</textarea></div>
</form>

View file

@ -8,7 +8,13 @@
<link rel="stylesheet" href="../../static/css/pad.css">
<link rel="stylesheet" href="../../static/css/timeslider.css">
<style type="text/css" title="dynamicsyntax"></style>
<script src="../../static/js/jquery.min.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../minified/timeslider.js"></script>
<link href="../../static/custom/timeslider.css" rel="stylesheet">
<script src="../../static/custom/timeslider.js"></script>
<script>
// <![CDATA[
var clientVars = {};
@ -37,7 +43,7 @@
}
function randomString() {
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var string_length = 20;
var randomstring = '';
for (var i=0; i<string_length; i++) {
@ -49,8 +55,11 @@
var socket, token, padId;
$(window).load(function ()
$(document).ready(function ()
{
//start the costum js
if(costumStart) costumStart();
//get the padId out of the url
var urlParts= document.location.pathname.split("/");
padId = urlParts[urlParts.length-2];
@ -133,26 +142,6 @@
// ]]>
</script>
<script type="text/javascript" src="../../static/js/plugins.js"></script>
<script type="text/javascript" src="../../static/js/undo-xpopup.js"></script>
<script type="text/javascript" src="../../socket.io/socket.io.js"></script>
<script type="text/javascript" src="../../static/js/json2.js"></script>
<script type="text/javascript" src="../../static/js/colorutils.js"></script>
<script type="text/javascript" src="../../static/js/draggable.js"></script>
<script type="text/javascript" src="../../static/js/pad_utils.js"></script>
<script type="text/javascript" src="../../static/js/pad_cookie.js"></script>
<script type="text/javascript" src="../../static/js/pad_editor.js"></script>
<script type="text/javascript" src="../../static/js/pad_editbar.js"></script>
<script type="text/javascript" src="../../static/js/pad_docbar.js"></script>
<script type="text/javascript" src="../../static/js/pad_modals.js"></script>
<script type="text/javascript" src="../../static/js/easysync2_client.js"></script>
<script type="text/javascript" src="../../static/js/domline_client.js"></script>
<script type="text/javascript" src="../../static/js/linestylefilter_client.js"></script>
<script type="text/javascript" src="../../static/js/cssmanager_client.js"></script>
<script type="text/javascript" src="../../static/js/broadcast.js"></script>
<script type="text/javascript" src="../../static/js/broadcast_slider.js"></script>
<script type="text/javascript" src="../../static/js/broadcast_revisions.js">
</script>
</head>
<body id="padbody" class="timeslider limwidth nonpropad nonprouser">