diff --git a/README.md b/README.md
index b21cd8e82..ab241333f 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,7 @@ Here is the **[FAQ](https://github.com/Pita/etherpad-lite/wiki/FAQ)**
**As any user (we recommend creating a separate user called etherpad-lite):**
-
+
Move to a folder where you want to install Etherpad Lite. Clone the git repository git clone 'git://github.com/Pita/etherpad-lite.git'
Install the dependencies with bin/installDeps.sh
Start it with bin/run.sh
@@ -114,8 +114,8 @@ You also help the project, if you only host a Etherpad Lite instance and share y
# Modules created for this project
* [ueberDB](https://github.com/Pita/ueberDB) "transforms every database into a object key value store" - manages all database access
-* [doc.md](https://github.com/Pita/doc.md) "A simple JSDoc documentation tool that creates markdown for node.js modules exports" - is used to generate the docs
* [channels](https://github.com/Pita/channels) "Event channels in node.js" - ensures that ueberDB operations are atomic and in series for each key
+* [async-stacktrace](https://github.com/Pita/async-stacktrace) "Improves node.js stacktraces and makes it easier to handle errors"
# License
[Apache License v2](http://www.apache.org/licenses/LICENSE-2.0.html)
diff --git a/bin/buildForWindows.sh b/bin/buildForWindows.sh
index 8e6ce4f6e..6e46e29a1 100755
--- a/bin/buildForWindows.sh
+++ b/bin/buildForWindows.sh
@@ -1,6 +1,6 @@
#!/bin/sh
-NODE_VERSION="0.6.1"
+NODE_VERSION="0.6.5"
#Move to the folder where ep-lite is installed
cd `dirname $0`
diff --git a/bin/checkPad.js b/bin/checkPad.js
new file mode 100644
index 000000000..9e0544415
--- /dev/null
+++ b/bin/checkPad.js
@@ -0,0 +1,133 @@
+/*
+ This is a debug tool. It checks all revisions for data corruption
+*/
+
+if(process.argv.length != 3)
+{
+ console.error("Use: node checkPad.js $PADID");
+ process.exit(1);
+}
+//get the padID
+var padId = process.argv[2];
+
+//initalize the database
+var log4js = require("log4js");
+log4js.setGlobalLogLevel("INFO");
+var async = require("async");
+var db = require('../node/db/DB');
+var Changeset = require('../node/utils/Changeset');
+var padManager;
+
+async.series([
+ //intallize the database
+ function (callback)
+ {
+ db.init(callback);
+ },
+ //get the pad
+ function (callback)
+ {
+ padManager = require('../node/db/PadManager');
+
+ padManager.doesPadExists(padId, function(err, exists)
+ {
+ if(!exists)
+ {
+ console.error("Pad does not exist");
+ process.exit(1);
+ }
+
+ padManager.getPad(padId, function(err, _pad)
+ {
+ pad = _pad;
+ callback(err);
+ });
+ });
+ },
+ function (callback)
+ {
+ //create an array with key kevisions
+ //key revisions always save the full pad atext
+ var head = pad.getHeadRevisionNumber();
+ var keyRevisions = [];
+ for(var i=0;i /dev/null 2>&1 || {
- echo "You need to install node!" >&2
- exit 1
-}
-
-hash doc.md > /dev/null 2>&1 || {
- echo "You need to install doc.md! npm install -g doc.md" >&2
- exit 1
-}
-
-echo "empty doc folder..."
-rm -rf doc/jsdoc/*
-
-doc.md node doc/jsdoc
diff --git a/bin/installDeps.sh b/bin/installDeps.sh
index 088260853..0533caf2d 100755
--- a/bin/installDeps.sh
+++ b/bin/installDeps.sh
@@ -16,7 +16,7 @@ hash curl > /dev/null 2>&1 || {
#Is node installed?
hash node > /dev/null 2>&1 || {
- echo "Please install node.js ( http://nodesjs.org )" >&2
+ echo "Please install node.js ( http://nodejs.org )" >&2
exit 1
}
@@ -28,8 +28,8 @@ hash npm > /dev/null 2>&1 || {
#check npm version
NPM_VERSION=$(npm --version)
-if [ ! $(echo $NPM_VERSION | cut -d "." -f 1-2) = "1.0" ]; then
- echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.0.x" >&2
+if [ ! $(echo $NPM_VERSION | cut -d "." -f 1) = "1" ]; then
+ echo "You're running a wrong version of npm, you're using $NPM_VERSION, we need 1.x" >&2
exit 1
fi
diff --git a/doc/jsdoc/README.md b/doc/jsdoc/README.md
deleted file mode 100644
index 066f3731d..000000000
--- a/doc/jsdoc/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# About this folder
-This is the documentation generated with doc.md. This documentation might be not up to date, you can generate the latest jsdocs with bin/generateJsDoc.sh
\ No newline at end of file
diff --git a/doc/jsdoc/db/AuthorManager.md b/doc/jsdoc/db/AuthorManager.md
deleted file mode 100644
index 257d112c9..000000000
--- a/doc/jsdoc/db/AuthorManager.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# 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,
-it creates a author for the token
-
-* **token** *(String)* The token
-* **callback** *(Function)* callback (err, author)
-The callback function that is called when the result is here
-
-- - -
-### getAuthorColorId (author, callback)
-Returns the color Id of the author
-
-* **author** *(String)* The id of the author
-* **callback** *(Function)* callback(err, colorId)
-
-- - -
-### getAuthorName (author, callback)
-Returns the name of the author
-
-* **author** *(String)* The id of the author
-* **callback** *(Function)* callback(err, name)
-
-- - -
-### setAuthorColorId (author, colorId, callback)
-Sets the color Id of the author
-
-* **author** *(String)* The id of the author
-* **colorId** *No description*
-* **callback** *(Function)* (optional)
-
-- - -
-### setAuthorName (author, name, callback)
-Sets the name of the author
-
-* **author** *(String)* The id of the author
-* **name** *No description*
-* **callback** *(Function)* (optional)
-
diff --git a/doc/jsdoc/db/DB.md b/doc/jsdoc/db/DB.md
deleted file mode 100644
index 6acfd826c..000000000
--- a/doc/jsdoc/db/DB.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# db
-`require("./db/DB");`
-
-The DB Module provides a database initalized with the settings
-provided by the settings module
-
-## Functions
-
-- - -
-### init (callback)
-Initalizes the database with the settings provided by the settings module
-
-* **callback** *(Function)* *No description*
-
-##Variables
-
-- - -
-### db
-The UeberDB Object that provides the database functions
-
diff --git a/doc/jsdoc/db/Pad.md b/doc/jsdoc/db/Pad.md
deleted file mode 100644
index 54b122b7d..000000000
--- a/doc/jsdoc/db/Pad.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# db/
-`require("./db/Pad");`
-
-The pad object, defined with joose
-
-## Functions
-
-- - -
-### cleanText (txt)
-Copied from the Etherpad source code. It converts Windows line breaks to Unix line breaks and convert Tabs to spaces
-
-* **txt** *No description*
-
diff --git a/doc/jsdoc/db/PadManager.md b/doc/jsdoc/db/PadManager.md
deleted file mode 100644
index 7721b3e86..000000000
--- a/doc/jsdoc/db/PadManager.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# db/PadMana
-`require("./db/PadManager");`
-
-The Pad Manager is a Factory for pad Objects
-
-## Functions
-
-- - -
-### getPad (id, callback)
-A Array with all known Pads
-
-* **id** A String with the id of the pad
-* **callback** *(Function)* *No description*
-
diff --git a/doc/jsdoc/db/ReadOnlyManager.md b/doc/jsdoc/db/ReadOnlyManager.md
deleted file mode 100644
index 4b6fc8afd..000000000
--- a/doc/jsdoc/db/ReadOnlyManager.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/easysync_tests.md b/doc/jsdoc/easysync_tests.md
deleted file mode 100644
index 0a9a7b448..000000000
--- a/doc/jsdoc/easysync_tests.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# easysync_tests
-`require("./easysync_tests");`
-
-I found this tests in the old Etherpad and used it to test if the Changeset library can be run on node.js.
-It has no use for ep-lite, but I thought I keep it cause it may help someone to understand the Changeset library
-https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2_tests.js
-
diff --git a/doc/jsdoc/handler/ExportHandler.md b/doc/jsdoc/handler/ExportHandler.md
deleted file mode 100644
index 7117715f7..000000000
--- a/doc/jsdoc/handler/ExportHandler.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/handler/ImportHandler.md b/doc/jsdoc/handler/ImportHandler.md
deleted file mode 100644
index e255586f1..000000000
--- a/doc/jsdoc/handler/ImportHandler.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/handler/PadMessageHandler.md b/doc/jsdoc/handler/PadMessageHandler.md
deleted file mode 100644
index a17df95fd..000000000
--- a/doc/jsdoc/handler/PadMessageHandler.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/handler/SocketIORouter.md b/doc/jsdoc/handler/SocketIORouter.md
deleted file mode 100644
index 8effc96d6..000000000
--- a/doc/jsdoc/handler/SocketIORouter.md
+++ /dev/null
@@ -1,23 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/handler/TimesliderMessageHandler.md b/doc/jsdoc/handler/TimesliderMessageHandler.md
deleted file mode 100644
index ac1edac62..000000000
--- a/doc/jsdoc/handler/TimesliderMessageHandler.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# handler/TimesliderMessag
-`require("./handler/TimesliderMessageHandler");`
-
-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)
-Saves the Socket class we need to send and recieve data from the client
-
-* **socket_io** The Socket
-
diff --git a/doc/jsdoc/server.md b/doc/jsdoc/server.md
deleted file mode 100644
index 90b37486c..000000000
--- a/doc/jsdoc/server.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# server
-`require("./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
-to MessageHandler and minfied requests are passed to minified.
-
-##Variables
-
-- - -
-### maxAge
-
-
diff --git a/doc/jsdoc/utils/Abiword.md b/doc/jsdoc/utils/Abiword.md
deleted file mode 100644
index 680e24992..000000000
--- a/doc/jsdoc/utils/Abiword.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/utils/AttributePoolFactory.md b/doc/jsdoc/utils/AttributePoolFactory.md
deleted file mode 100644
index 4f52fcc3f..000000000
--- a/doc/jsdoc/utils/AttributePoolFactory.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# 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
-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:
-https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
-
-## Functions
-
-- - -
-### createAttributePool ()
-
-
diff --git a/doc/jsdoc/utils/Changeset.md b/doc/jsdoc/utils/Changeset.md
deleted file mode 100644
index 6f08514a2..000000000
--- a/doc/jsdoc/utils/Changeset.md
+++ /dev/null
@@ -1,363 +0,0 @@
-# utils/Cha
-`require("./utils/Changeset");`
-
-## Functions
-
-- - -
-### _slicerZipperFunc (attOp, csOp, opOut, pool)
-
-* **attOp** *No description*
-* **csOp** *No description*
-* **opOut** *No description*
-* **pool** *No description*
-
-- - -
-### appendATextToAssembler (atext, assem)
-
-* **atext** *No description*
-* **assem** *No description*
-
-- - -
-### applyToAText (cs, atext, pool)
-
-* **cs** *No description*
-* **atext** *No description*
-* **pool** *No description*
-
-- - -
-### applyToAttribution (cs, astr, pool)
-
-* **cs** *No description*
-* **astr** *No description*
-* **pool** *No description*
-
-- - -
-### applyToText (cs, str)
-
-* **cs** *No description*
-* **str** *No description*
-
-- - -
-### applyZip (in1, idx1, in2, idx2, func)
-
-* **in1** *No description*
-* **idx1** *No description*
-* **in2** *No description*
-* **idx2** *No description*
-* **func** *No description*
-
-- - -
-### attribsAttributeValue (attribs, key, pool)
-
-* **attribs** *No description*
-* **key** *No description*
-* **pool** *No description*
-
-- - -
-### attributeTester (attribPair, pool)
-
-* **attribPair** *No description*
-* **pool** *No description*
-
-- - -
-### builder (oldLen)
-
-* **oldLen** *No description*
-
-- - -
-### characterRangeFollow (cs, startChar, endChar, insertionsAfter)
-
-* **cs** *No description*
-* **startChar** *No description*
-* **endChar** *No description*
-* **insertionsAfter** *No description*
-
-- - -
-### checkRep (cs)
-
-* **cs** *No description*
-
-- - -
-### clearOp (op)
-
-* **op** *No description*
-
-- - -
-### cloneAText (atext)
-
-* **atext** *No description*
-
-- - -
-### cloneOp (op)
-
-* **op** *No description*
-
-- - -
-### compose (cs1, cs2, pool)
-
-* **cs1** *No description*
-* **cs2** *No description*
-* **pool** *No description*
-
-- - -
-### composeAttributes (att1, att2, resultIsMutation, pool)
-
-* **att1** *No description*
-* **att2** *No description*
-* **resultIsMutation** *No description*
-* **pool** *No description*
-
-- - -
-### copyAText (atext1, atext2)
-
-* **atext1** *No description*
-* **atext2** *No description*
-
-- - -
-### copyOp (op1, op2)
-
-* **op1** *No description*
-* **op2** *No description*
-
-- - -
-### eachAttribNumber (cs, func)
-
-* **cs** *No description*
-* **func** *No description*
-
-- - -
-### filterAttribNumbers (cs, filter)
-
-* **cs** *No description*
-* **filter** *No description*
-
-- - -
-### follow (cs1, cs2, reverseInsertOrder, pool)
-
-* **cs1** *No description*
-* **cs2** *No description*
-* **reverseInsertOrder** *No description*
-* **pool** *No description*
-
-- - -
-### followAttributes (att1, att2, pool)
-
-* **att1** *No description*
-* **att2** *No description*
-* **pool** *No description*
-
-- - -
-### identity (N)
-
-* **N** *No description*
-
-- - -
-### inverse (cs, lines, alines, pool)
-
-* **cs** *No description*
-* **lines** *No description*
-* **alines** *No description*
-* **pool** *No description*
-
-- - -
-### isIdentity (cs)
-
-* **cs** *No description*
-
-- - -
-### joinAttributionLines (theAlines)
-
-* **theAlines** *No description*
-
-- - -
-### makeAText (text, attribs)
-
-* **text** *No description*
-* **attribs** *No description*
-
-- - -
-### makeAttribsString (opcode, attribs, pool)
-
-* **opcode** *No description*
-* **attribs** *No description*
-* **pool** *No description*
-
-- - -
-### makeAttribution (text)
-
-* **text** *No description*
-
-- - -
-### makeSplice (oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool)
-
-* **oldFullText** *No description*
-* **spliceStart** *No description*
-* **numRemoved** *No description*
-* **newText** *No description*
-* **optNewTextAPairs** *No description*
-* **pool** *No description*
-
-- - -
-### mapAttribNumbers (cs, func)
-
-* **cs** *No description*
-* **func** *No description*
-
-- - -
-### mergingOpAssembler ()
-
-
-- - -
-### moveOpsToNewPool (cs, oldPool, newPool)
-
-* **cs** *No description*
-* **oldPool** *No description*
-* **newPool** *No description*
-
-- - -
-### mutateAttributionLines (cs, lines, pool)
-
-* **cs** *No description*
-* **lines** *No description*
-* **pool** *No description*
-
-- - -
-### mutateTextLines (cs, lines)
-
-* **cs** *No description*
-* **lines** *No description*
-
-- - -
-### newLen (cs)
-
-* **cs** *No description*
-
-- - -
-### newOp (optOpcode)
-
-* **optOpcode** *No description*
-
-- - -
-### numToString (num)
-
-* **num** *No description*
-
-- - -
-### oldLen (cs)
-
-* **cs** *No description*
-
-- - -
-### oneInsertedLineAtATimeOpIterator (opsStr, optStartIndex, charBank)
-
-* **opsStr** *No description*
-* **optStartIndex** *No description*
-* **charBank** *No description*
-
-- - -
-### opAssembler ()
-
-
-- - -
-### opAttributeValue (op, key, pool)
-
-* **op** *No description*
-* **key** *No description*
-* **pool** *No description*
-
-- - -
-### opIterator (opsStr, optStartIndex)
-
-* **opsStr** *No description*
-* **optStartIndex** *No description*
-
-- - -
-### opString (op)
-
-* **op** *No description*
-
-- - -
-### pack (oldLen, newLen, opsStr, bank)
-
-* **oldLen** *No description*
-* **newLen** *No description*
-* **opsStr** *No description*
-* **bank** *No description*
-
-- - -
-### parseNum (str)
-
-* **str** *No description*
-
-- - -
-### prepareForWire (cs, pool)
-
-* **cs** *No description*
-* **pool** *No description*
-
-- - -
-### smartOpAssembler ()
-
-
-- - -
-### splitAttributionLines (attrOps, text)
-
-* **attrOps** *No description*
-* **text** *No description*
-
-- - -
-### splitTextLines (text)
-
-* **text** *No description*
-
-- - -
-### stringAssembler ()
-
-
-- - -
-### stringIterator (str)
-
-* **str** *No description*
-
-- - -
-### stringOp (str)
-
-* **str** *No description*
-
-- - -
-### subattribution (astr, start, optEnd)
-
-* **astr** *No description*
-* **start** *No description*
-* **optEnd** *No description*
-
-- - -
-### textLinesMutator (lines)
-
-* **lines** *No description*
-
-- - -
-### toBaseTen (cs)
-
-* **cs** *No description*
-
-- - -
-### toSplices (cs)
-
-* **cs** *No description*
-
-- - -
-### unpack (cs)
-
-* **cs** *No description*
-
-##Variables
-
-- - -
-### assert
-
-
-- - -
-### error
-
-
diff --git a/doc/jsdoc/utils/ExportHtml.md b/doc/jsdoc/utils/ExportHtml.md
deleted file mode 100644
index 8c56abbc4..000000000
--- a/doc/jsdoc/utils/ExportHtml.md
+++ /dev/null
@@ -1,24 +0,0 @@
-# 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*
-
diff --git a/doc/jsdoc/utils/Minify.md b/doc/jsdoc/utils/Minify.md
deleted file mode 100644
index b2d1cba31..000000000
--- a/doc/jsdoc/utils/Minify.md
+++ /dev/null
@@ -1,16 +0,0 @@
-# utils/
-`require("./utils/Minify");`
-
-This Module manages all /minified/* requests. It controls the
-minification && compression of Javascript and CSS.
-
-## Functions
-
-- - -
-### minifyJS (req, res, jsFilename)
-creates the minifed javascript for the given minified name
-
-* **req** the Express request
-* **res** the Express response
-* **jsFilename** *No description*
-
diff --git a/doc/jsdoc/utils/Settings.md b/doc/jsdoc/utils/Settings.md
deleted file mode 100644
index 1750c4382..000000000
--- a/doc/jsdoc/utils/Settings.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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
-
-- - -
-### dbType
-
-
-- - -
-### 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
-
-- - -
-### minify
-A flag that shows if minification is enabled or not
-
-- - -
-### port
-The Port ep-lite should listen to
-
diff --git a/node/db/API.js b/node/db/API.js
index c40c49cac..09cc95afc 100644
--- a/node/db/API.js
+++ b/node/db/API.js
@@ -18,6 +18,8 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
+var customError = require("../utils/customError");
var padManager = require("./PadManager");
var padMessageHandler = require("../handler/PadMessageHandler");
var readOnlyManager = require("./ReadOnlyManager");
@@ -87,7 +89,7 @@ exports.getText = function(padID, rev, callback)
}
else
{
- callback({stop: "rev is not a number"});
+ callback(new customError("rev is not a number", "apierror"));
return;
}
}
@@ -95,25 +97,21 @@ exports.getText = function(padID, rev, callback)
//ensure this is not a negativ number
if(rev !== undefined && rev < 0)
{
- callback({stop: "rev is a negativ number"});
+ callback(new customError("rev is a negativ number","apierror"));
return;
}
//ensure this is not a float value
if(rev !== undefined && !is_int(rev))
{
- callback({stop: "rev is a float value"});
+ callback(new customError("rev is a float value","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//the client asked for a special revision
if(rev !== undefined)
@@ -121,19 +119,18 @@ exports.getText = function(padID, rev, callback)
//check if this is a valid revision
if(rev > pad.getHeadRevisionNumber())
{
- callback({stop: "rev is higher than the head revision of the pad"});
+ callback(new customError("rev is higher than the head revision of the pad","apierror"));
return;
}
//get the text of this revision
pad.getInternalRevisionAText(rev, function(err, atext)
{
- if(!err)
- {
- data = {text: atext.text};
- }
+ if(ERR(err, callback)) return;
- callback(err, data);
+ data = {text: atext.text};
+
+ callback(null, data);
})
}
//the client wants the latest text, lets return it to him
@@ -158,11 +155,7 @@ exports.setText = function(padID, text, callback)
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//set the text
pad.setText(text);
@@ -196,30 +189,26 @@ exports.getHTML = function(padID, rev, callback)
}
else
{
- callback({stop: "rev is not a number"});
+ callback(new customError("rev is not a number","apierror"));
return;
}
}
if(rev !== undefined && rev < 0)
{
- callback({stop: "rev is a negative number"});
+ callback(new customError("rev is a negative number","apierror"));
return;
}
if(rev !== undefined && !is_int(rev))
{
- callback({stop: "rev is a float value"});
+ callback(new customError("rev is a float value","apierror"));
return;
}
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//the client asked for a special revision
if(rev !== undefined)
@@ -227,18 +216,16 @@ exports.getHTML = function(padID, rev, callback)
//check if this is a valid revision
if(rev > pad.getHeadRevisionNumber())
{
- callback({stop: "rev is higher than the head revision of the pad"});
+ callback(new customError("rev is higher than the head revision of the pad","apierror"));
return;
}
//get the html of this revision
exportHtml.getPadHTML(pad, rev, function(err, html)
{
- if(!err)
- {
- data = {html: html};
- }
- callback(err, data);
+ if(ERR(err, callback)) return;
+ data = {html: html};
+ callback(null, data);
});
}
//the client wants the latest text, lets return it to him
@@ -246,11 +233,11 @@ exports.getHTML = function(padID, rev, callback)
{
exportHtml.getPadHTML(pad, undefined, function (err, html)
{
- if(!err)
- {
- data = {html: html};
- }
- callback(err, data);
+ if(ERR(err, callback)) return;
+
+ data = {html: html};
+
+ callback(null, data);
});
}
});
@@ -261,11 +248,7 @@ exports.setHTML = function(padID, html, callback)
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
// add a new changeset with the new html to the pad
importHtml.setPadHTML(pad, cleanText(html));
@@ -293,11 +276,7 @@ exports.getRevisionsCount = function(padID, callback)
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
callback(null, {revisions: pad.getHeadRevisionNumber()});
});
@@ -314,16 +293,17 @@ Example returns:
exports.createPad = function(padID, text, callback)
{
//ensure there is no $ in the padID
- if(padID.indexOf("$") != -1)
+ if(padID && padID.indexOf("$") != -1)
{
- callback({stop: "createPad can't create group pads"});
+ callback(new customError("createPad can't create group pads","apierror"));
return;
}
//create pad
getPadSafe(padID, false, text, function(err)
{
- callback(err);
+ if(ERR(err, callback)) return;
+ callback();
});
}
@@ -339,11 +319,7 @@ exports.deletePad = function(padID, callback)
{
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
pad.remove(callback);
});
@@ -362,16 +338,13 @@ exports.getReadOnlyID = function(padID, callback)
//we don't need the pad object, but this function does all the security stuff for us
getPadSafe(padID, true, function(err)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//get the readonlyId
readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId)
{
- callback(err, {readOnlyID: readOnlyId});
+ if(ERR(err, callback)) return;
+ callback(null, {readOnlyID: readOnlyId});
});
});
}
@@ -387,20 +360,16 @@ Example returns:
exports.setPublicStatus = function(padID, publicStatus, callback)
{
//ensure this is a group pad
- if(padID.indexOf("$") == -1)
+ if(padID && padID.indexOf("$") == -1)
{
- callback({stop: "You can only get/set the publicStatus of pads that belong to a group"});
+ callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//convert string to boolean
if(typeof publicStatus == "string")
@@ -424,20 +393,16 @@ Example returns:
exports.getPublicStatus = function(padID, callback)
{
//ensure this is a group pad
- if(padID.indexOf("$") == -1)
+ if(padID && padID.indexOf("$") == -1)
{
- callback({stop: "You can only get/set the publicStatus of pads that belong to a group"});
+ callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
callback(null, {publicStatus: pad.getPublicStatus()});
});
@@ -454,20 +419,16 @@ Example returns:
exports.setPassword = function(padID, password, callback)
{
//ensure this is a group pad
- if(padID.indexOf("$") == -1)
+ if(padID && padID.indexOf("$") == -1)
{
- callback({stop: "You can only get/set the password of pads that belong to a group"});
+ callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//set the password
pad.setPassword(password);
@@ -487,20 +448,16 @@ Example returns:
exports.isPasswordProtected = function(padID, callback)
{
//ensure this is a group pad
- if(padID.indexOf("$") == -1)
+ if(padID && padID.indexOf("$") == -1)
{
- callback({stop: "You can only get/set the password of pads that belong to a group"});
+ callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
callback(null, {isPasswordProtected: pad.isPasswordProtected()});
});
@@ -528,34 +485,31 @@ function getPadSafe(padID, shouldExist, text, callback)
//check if padID is a string
if(typeof padID != "string")
{
- callback({stop: "padID is not a string"});
+ callback(new customError("padID is not a string","apierror"));
return;
}
//check if the padID maches the requirements
if(!padManager.isValidPadId(padID))
{
- callback({stop: "padID did not match requirements"});
+ callback(new customError("padID did not match requirements","apierror"));
return;
}
//check if the pad exists
padManager.doesPadExists(padID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//does not exist, but should
- else if(exists == false && shouldExist == true)
+ if(exists == false && shouldExist == true)
{
- callback({stop: "padID does not exist"});
+ callback(new customError("padID does not exist","apierror"));
}
//does exists, but shouldn't
else if(exists == true && shouldExist == false)
{
- callback({stop: "padID does already exist"});
+ callback(new customError("padID does already exist","apierror"));
}
//pad exists, let's get it
else
diff --git a/node/db/AuthorManager.js b/node/db/AuthorManager.js
index 958e2c16c..f4f42d112 100644
--- a/node/db/AuthorManager.js
+++ b/node/db/AuthorManager.js
@@ -18,6 +18,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var db = require("./DB").db;
var async = require("async");
@@ -29,7 +30,8 @@ exports.doesAuthorExists = function (authorID, callback)
//check if the database entry of this author exists
db.get("globalAuthor:" + authorID, function (err, author)
{
- callback(err, author != null);
+ if(ERR(err, callback)) return;
+ callback(null, author != null);
});
}
@@ -42,8 +44,9 @@ exports.getAuthor4Token = function (token, callback)
{
mapAuthorWithDBKey("token2author", token, function(err, author)
{
+ if(ERR(err, callback)) return;
//return only the sub value authorID
- callback(err, author ? author.authorID : author);
+ callback(null, author ? author.authorID : author);
});
}
@@ -56,12 +59,7 @@ exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
{
mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author)
{
- //error?
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//set the name of this author
if(name)
@@ -84,24 +82,14 @@ function mapAuthorWithDBKey (mapperkey, mapper, callback)
//try to map to an author
db.get(mapperkey + ":" + mapper, function (err, author)
{
- //error?
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//there is no author with this mapper, so create one
if(author == null)
{
exports.createAuthor(null, function(err, author)
{
- //error?
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//create the token2author relation
db.set(mapperkey + ":" + mapper, author.authorID);
diff --git a/node/db/GroupManager.js b/node/db/GroupManager.js
index 1598e72e1..473ea9b77 100644
--- a/node/db/GroupManager.js
+++ b/node/db/GroupManager.js
@@ -18,6 +18,8 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
+var customError = require("../utils/customError");
var db = require("./DB").db;
var async = require("async");
var padManager = require("./PadManager");
@@ -34,15 +36,12 @@ exports.deleteGroup = function(groupID, callback)
//try to get the group entry
db.get("group:" + groupID, function (err, _group)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//group does not exist
- else if(_group == null)
+ if(_group == null)
{
- callback({stop: "groupID does not exist"});
+ callback(new customError("groupID does not exist","apierror"));
}
//group exists, everything is fine
else
@@ -67,7 +66,7 @@ exports.deleteGroup = function(groupID, callback)
{
padManager.getPad(padID, function(err, pad)
{
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
pad.remove(callback);
});
@@ -79,7 +78,7 @@ exports.deleteGroup = function(groupID, callback)
//try to get the group entry
db.get("group2sessions:" + groupID, function (err, group2sessions)
{
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
//skip if there is no group2sessions entry
if(group2sessions == null) {callback(); return}
@@ -107,7 +106,8 @@ exports.deleteGroup = function(groupID, callback)
}
], function(err)
{
- callback(err);
+ if(ERR(err, callback)) return;
+ callback();
});
}
@@ -116,7 +116,8 @@ exports.doesGroupExist = function(groupID, callback)
//try to get the group entry
db.get("group:" + groupID, function (err, group)
{
- callback(err, group != null);
+ if(ERR(err, callback)) return;
+ callback(null, group != null);
});
}
@@ -135,30 +136,21 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
//ensure mapper is optional
if(typeof groupMapper != "string")
{
- callback({stop: "groupMapper is no string"});
+ callback(new customError("groupMapper is no string","apierror"));
return;
}
//try to get a group for this mapper
db.get("mapper2group:"+groupMapper, function(err, groupID)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//there is no group for this mapper, let's create a group
if(groupID == null)
{
exports.createGroup(function(err, responseObj)
{
- //check for errors
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//create the mapper entry for this group
db.set("mapper2group:"+groupMapper, responseObj.groupID);
@@ -169,7 +161,8 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
//there is a group for this mapper, let's return it
else
{
- callback(err, {groupID: groupID});
+ if(ERR(err, callback)) return;
+ callback(null, {groupID: groupID});
}
});
}
@@ -185,15 +178,12 @@ exports.createGroupPad = function(groupID, padName, text, callback)
{
exports.doesGroupExist(groupID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//group does not exist
- else if(exists == false)
+ if(exists == false)
{
- callback({stop: "groupID does not exist"});
+ callback(new customError("groupID does not exist","apierror"));
}
//group exists, everything is fine
else
@@ -207,15 +197,12 @@ exports.createGroupPad = function(groupID, padName, text, callback)
{
padManager.doesPadExists(padID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//pad exists already
- else if(exists == true)
+ if(exists == true)
{
- callback({stop: "padName does already exist"});
+ callback(new customError("padName does already exist","apierror"));
}
//pad does not exist, everything is fine
else
@@ -229,7 +216,8 @@ exports.createGroupPad = function(groupID, padName, text, callback)
{
padManager.getPad(padID, text, function(err)
{
- callback(err);
+ if(ERR(err, callback)) return;
+ callback();
});
},
//create an entry in the group for this pad
@@ -240,7 +228,8 @@ exports.createGroupPad = function(groupID, padName, text, callback)
}
], function(err)
{
- callback(err, {padID: padID});
+ if(ERR(err, callback)) return;
+ callback(null, {padID: padID});
});
}
@@ -248,22 +237,20 @@ exports.listPads = function(groupID, callback)
{
exports.doesGroupExist(groupID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//group does not exist
- else if(exists == false)
+ if(exists == false)
{
- callback({stop: "groupID does not exist"});
+ callback(new customError("groupID does not exist","apierror"));
}
//group exists, let's get the pads
else
{
db.getSub("group:" + groupID, ["pads"], function(err, pads)
{
- callback(err, {padIDs: pads});
+ if(ERR(err, callback)) return;
+ callback(null, {padIDs: pads});
});
}
});
diff --git a/node/db/Pad.js b/node/db/Pad.js
index bba7562c8..7807d4643 100644
--- a/node/db/Pad.js
+++ b/node/db/Pad.js
@@ -4,6 +4,7 @@
require('joose');
+var ERR = require("async-stacktrace");
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
var db = require("./DB").db;
@@ -164,8 +165,9 @@ Class('Pad', {
{
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
{
+ if(ERR(err, callback)) return;
atext = Changeset.cloneAText(_atext);
- callback(err);
+ callback();
});
},
//get all needed changesets
@@ -175,8 +177,9 @@ Class('Pad', {
{
_this.getRevisionChangeset(item, function(err, changeset)
{
+ if(ERR(err, callback)) return;
changesets[item] = changeset;
- callback(err);
+ callback();
});
}, callback);
}
@@ -199,7 +202,8 @@ Class('Pad', {
}
], function(err)
{
- callback(err, atext);
+ if(ERR(err, callback)) return;
+ callback(null, atext);
});
},
@@ -247,8 +251,9 @@ Class('Pad', {
{
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
{
+ if(ERR(err, callback)) return;
entry = _entry;
- callback(err);
+ callback();
});
},
//add the authorName
@@ -264,13 +269,15 @@ Class('Pad', {
//get the authorName
authorManager.getAuthorName(entry.userId, function(err, authorName)
{
+ if(ERR(err, callback)) return;
entry.userName = authorName;
- callback(err);
+ callback();
});
}
], function(err)
{
- callback(err, entry);
+ if(ERR(err, callback)) return;
+ callback(null, entry);
});
},
@@ -311,11 +318,14 @@ Class('Pad', {
{
_this.getChatMessage(entryObject.entryNum, function(err, entry)
{
+ if(ERR(err, callback)) return;
entries[entryObject.order] = entry;
- callback(err);
+ 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
@@ -328,7 +338,7 @@ Class('Pad', {
console.warn("WARNING: Found broken chat entry in pad " + _this.id);
}
- callback(err, cleanedEntries);
+ callback(null, cleanedEntries);
});
},
@@ -345,11 +355,7 @@ Class('Pad', {
//try to load the pad
db.get("pad:"+this.id, function(err, value)
{
- if(err)
- {
- callback(err, null);
- return;
- }
+ if(ERR(err, callback)) return;
//if this pad exists, load it
if(value != null)
@@ -410,7 +416,7 @@ Class('Pad', {
db.get("group:" + groupID, function (err, group)
{
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
//remove the pad entry
delete group.pads[padID];
@@ -432,7 +438,7 @@ Class('Pad', {
{
readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
{
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
db.remove("pad2readonly:" + padID);
db.remove("readonly2pad:" + readonlyID);
@@ -475,7 +481,8 @@ Class('Pad', {
}
], function(err)
{
- callback(err);
+ if(ERR(err, callback)) return;
+ callback();
})
},
//set in db
diff --git a/node/db/PadManager.js b/node/db/PadManager.js
index 4e16c7c45..1aadcd1ff 100644
--- a/node/db/PadManager.js
+++ b/node/db/PadManager.js
@@ -18,6 +18,8 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
+var customError = require("../utils/customError");
require("../db/Pad");
var db = require("./DB").db;
@@ -30,12 +32,20 @@ var db = require("./DB").db;
* If this is needed in other places, it would be wise to make this a prototype
* that's defined somewhere more sensible.
*/
-globalPads = {
+var globalPads = {
get: function (name) { return this[':'+name]; },
set: function (name, value) { this[':'+name] = value; },
remove: function (name) { delete this[':'+name]; }
};
+/**
+ * An array of padId transformations. These represent changes in pad name policy over
+ * time, and allow us to "play back" these changes so legacy padIds can be found.
+ */
+var padIdTransforms = [
+ [/\s+/g, '_']
+];
+
/**
* Returns a Pad Object with the callback
* @param id A String with the id of the pad
@@ -46,7 +56,7 @@ exports.getPad = function(id, text, callback)
//check if this is a valid padId
if(!exports.isValidPadId(id))
{
- callback({stop: id + " is not a valid padId"});
+ callback(new customError(id + " is not a valid padId","apierror"));
return;
}
@@ -63,14 +73,14 @@ exports.getPad = function(id, text, callback)
//check if text is a string
if(typeof text != "string")
{
- callback({stop: "text is not a string"});
+ callback(new customError("text is not a string","apierror"));
return;
}
//check if text is less than 100k chars
if(text.length > 100000)
{
- callback({stop: "text must be less than 100k chars"});
+ callback(new customError("text must be less than 100k chars","apierror"));
return;
}
}
@@ -90,15 +100,10 @@ exports.getPad = function(id, text, callback)
//initalize the pad
pad.init(text, function(err)
{
- if(err)
- {
- callback(err, null);
- }
- else
- {
- globalPads.set(id, pad);
- callback(null, pad);
- }
+ if(ERR(err, callback)) return;
+
+ globalPads.set(id, pad);
+ callback(null, pad);
});
}
}
@@ -108,10 +113,44 @@ exports.doesPadExists = function(padId, callback)
{
db.get("pad:"+padId, function(err, value)
{
- callback(err, value != null);
+ if(ERR(err, callback)) return;
+ callback(null, value != null);
});
}
+//returns a sanitized padId, respecting legacy pad id formats
+exports.sanitizePadId = function(padId, callback) {
+ var transform_index = arguments[2] || 0;
+ //we're out of possible transformations, so just return it
+ if(transform_index >= padIdTransforms.length)
+ {
+ callback(padId);
+ }
+ //check if padId exists
+ else
+ {
+ exports.doesPadExists(padId, function(junk, exists)
+ {
+ if(exists)
+ {
+ callback(padId);
+ }
+ else
+ {
+ //get the next transformation *that's different*
+ var transformedPadId = padId;
+ while(transformedPadId == padId && transform_index < padIdTransforms.length)
+ {
+ transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]);
+ transform_index += 1;
+ }
+ //check the next transform
+ exports.sanitizePadId(transformedPadId, callback, transform_index);
+ }
+ });
+ }
+}
+
exports.isValidPadId = function(padId)
{
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
diff --git a/node/db/ReadOnlyManager.js b/node/db/ReadOnlyManager.js
index cd18a1888..73b3be9ec 100644
--- a/node/db/ReadOnlyManager.js
+++ b/node/db/ReadOnlyManager.js
@@ -18,6 +18,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var db = require("./DB").db;
var async = require("async");
@@ -55,8 +56,9 @@ exports.getReadOnlyId = function (padId, callback)
}
], function(err)
{
+ if(ERR(err, callback)) return;
//return the results
- callback(err, readOnlyId);
+ callback(null, readOnlyId);
})
}
diff --git a/node/db/SecurityManager.js b/node/db/SecurityManager.js
index 0f35d8735..52d5afcbe 100644
--- a/node/db/SecurityManager.js
+++ b/node/db/SecurityManager.js
@@ -17,7 +17,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
+
+var ERR = require("async-stacktrace");
var db = require("./DB").db;
var async = require("async");
var authorManager = require("./AuthorManager");
@@ -56,6 +57,8 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
//get author for this token
authorManager.getAuthor4Token(token, function(err, author)
{
+ if(ERR(err, callback)) return;
+
// assume user has access
statusObject = {accessStatus: "grant", authorID: author};
// user can't create pads
@@ -64,17 +67,19 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
// check if pad exists
padManager.doesPadExists(padID, function(err, exists)
{
+ if(ERR(err, callback)) return;
+
// pad doesn't exist - user can't have access
if(!exists) statusObject.accessStatus = "deny";
// grant or deny access, with author of token
- callback(err, statusObject);
+ callback(null, statusObject);
});
}
// user may create new pads - no need to check anything
else
{
// grant access, with author of token
- callback(err, statusObject);
+ callback(null, statusObject);
}
})
@@ -102,8 +107,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
{
padManager.doesPadExists(padID, function(err, exists)
{
+ if(ERR(err, callback)) return;
padExists = exists;
- callback(err);
+ callback();
});
},
//get informations about this session
@@ -112,13 +118,13 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
sessionManager.getSessionInfo(sessionID, function(err, sessionInfo)
{
//skip session validation if the session doesn't exists
- if(err && err.stop == "sessionID does not exist")
+ if(err && err.message == "sessionID does not exist")
{
callback();
return;
}
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
var now = Math.floor(new Date().getTime()/1000);
@@ -139,8 +145,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
//get author for this token
authorManager.getAuthor4Token(token, function(err, author)
{
+ if(ERR(err, callback)) return;
tokenAuthor = author;
- callback(err);
+ callback();
});
}
], callback);
@@ -157,7 +164,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
padManager.getPad(padID, function(err, pad)
{
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
//is it a public pad?
isPublic = pad.getPublicStatus();
@@ -265,6 +272,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
}
], function(err)
{
- callback(err, statusObject);
+ if(ERR(err, callback)) return;
+ callback(null, statusObject);
});
}
diff --git a/node/db/SessionManager.js b/node/db/SessionManager.js
index fc6fe306d..a394f5442 100644
--- a/node/db/SessionManager.js
+++ b/node/db/SessionManager.js
@@ -18,6 +18,8 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
+var customError = require("../utils/customError");
var db = require("./DB").db;
var async = require("async");
var groupMangager = require("./GroupManager");
@@ -28,7 +30,8 @@ exports.doesSessionExist = function(sessionID, callback)
//check if the database entry of this session exists
db.get("session:" + sessionID, function (err, session)
{
- callback(err, session != null);
+ if(ERR(err, callback)) return;
+ callback(null, session != null);
});
}
@@ -45,15 +48,12 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
{
groupMangager.doesGroupExist(groupID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//group does not exist
- else if(exists == false)
+ if(exists == false)
{
- callback({stop: "groupID does not exist"});
+ callback(new customError("groupID does not exist","apierror"));
}
//everything is fine, continue
else
@@ -67,15 +67,12 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
{
authorMangager.doesAuthorExists(authorID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//author does not exist
- else if(exists == false)
+ if(exists == false)
{
- callback({stop: "authorID does not exist"});
+ callback(new customError("authorID does not exist","apierror"));
}
//everything is fine, continue
else
@@ -97,7 +94,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
}
else
{
- callback({stop: "validUntil is not a number"});
+ callback(new customError("validUntil is not a number","apierror"));
return;
}
}
@@ -105,21 +102,21 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
//ensure this is not a negativ number
if(validUntil < 0)
{
- callback({stop: "validUntil is a negativ number"});
+ callback(new customError("validUntil is a negativ number","apierror"));
return;
}
//ensure this is not a float value
if(!is_int(validUntil))
{
- callback({stop: "validUntil is a float value"});
+ callback(new customError("validUntil is a float value","apierror"));
return;
}
//check if validUntil is in the future
if(Math.floor(new Date().getTime()/1000) > validUntil)
{
- callback({stop: "validUntil is in the past"});
+ callback(new customError("validUntil is in the past","apierror"));
return;
}
@@ -137,12 +134,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
//get the entry
db.get("group2sessions:" + groupID, function(err, group2sessions)
{
- //did a error happen?
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//the entry doesn't exist so far, let's create it
if(group2sessions == null)
@@ -165,12 +157,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
//get the entry
db.get("author2sessions:" + authorID, function(err, author2sessions)
{
- //did a error happen?
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
//the entry doesn't exist so far, let's create it
if(author2sessions == null)
@@ -189,8 +176,10 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
}
], function(err)
{
+ if(ERR(err, callback)) return;
+
//return error and sessionID
- callback(err, {sessionID: sessionID});
+ callback(null, {sessionID: sessionID});
})
}
@@ -199,15 +188,12 @@ exports.getSessionInfo = function(sessionID, callback)
//check if the database entry of this session exists
db.get("session:" + sessionID, function (err, session)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//session does not exists
- else if(session == null)
+ if(session == null)
{
- callback({stop: "sessionID does not exist"})
+ callback(new customError("sessionID does not exist","apierror"))
}
//everything is fine, return the sessioninfos
else
@@ -231,15 +217,12 @@ exports.deleteSession = function(sessionID, callback)
//get the session entry
db.get("session:" + sessionID, function (err, session)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//session does not exists
- else if(session == null)
+ if(session == null)
{
- callback({stop: "sessionID does not exist"})
+ callback(new customError("sessionID does not exist","apierror"))
}
//everything is fine, return the sessioninfos
else
@@ -256,8 +239,9 @@ exports.deleteSession = function(sessionID, callback)
{
db.get("group2sessions:" + groupID, function (err, _group2sessions)
{
+ if(ERR(err, callback)) return;
group2sessions = _group2sessions;
- callback(err);
+ callback();
});
},
//get the author2sessions entry
@@ -265,8 +249,9 @@ exports.deleteSession = function(sessionID, callback)
{
db.get("author2sessions:" + authorID, function (err, _author2sessions)
{
+ if(ERR(err, callback)) return;
author2sessions = _author2sessions;
- callback(err);
+ callback();
});
},
//remove the values from the database
@@ -287,7 +272,8 @@ exports.deleteSession = function(sessionID, callback)
}
], function(err)
{
- callback(err);
+ if(ERR(err, callback)) return;
+ callback();
})
}
@@ -295,15 +281,12 @@ exports.listSessionsOfGroup = function(groupID, callback)
{
groupMangager.doesGroupExist(groupID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//group does not exist
- else if(exists == false)
+ if(exists == false)
{
- callback({stop: "groupID does not exist"});
+ callback(new customError("groupID does not exist","apierror"));
}
//everything is fine, continue
else
@@ -317,15 +300,12 @@ exports.listSessionsOfAuthor = function(authorID, callback)
{
authorMangager.doesAuthorExists(authorID, function(err, exists)
{
- //error
- if(err)
- {
- callback(err);
- }
+ if(ERR(err, callback)) return;
+
//group does not exist
- else if(exists == false)
+ if(exists == false)
{
- callback({stop: "authorID does not exist"});
+ callback(new customError("authorID does not exist","apierror"));
}
//everything is fine, continue
else
@@ -346,8 +326,9 @@ function listSessionsWithDBKey (dbkey, callback)
//get the group2sessions entry
db.get(dbkey, function(err, sessionObject)
{
+ if(ERR(err, callback)) return;
sessions = sessionObject ? sessionObject.sessionIDs : null;
- callback(err);
+ callback();
});
},
function(callback)
@@ -364,14 +345,16 @@ function listSessionsWithDBKey (dbkey, callback)
{
exports.getSessionInfo(sessionID, function(err, sessionInfo)
{
+ if(ERR(err, callback)) return;
sessions[sessionID] = sessionInfo;
- callback(err);
+ callback();
});
}, callback);
}
], function(err)
{
- callback(err, sessions);
+ if(ERR(err, callback)) return;
+ callback(null, sessions);
});
}
diff --git a/node/easysync_tests.js b/node/easysync_tests.js
index e0d82c33f..5b73b7170 100644
--- a/node/easysync_tests.js
+++ b/node/easysync_tests.js
@@ -20,8 +20,8 @@
* limitations under the License.
*/
-var Changeset = require('./Changeset');
-var AttributePoolFactory = require("./AttributePoolFactory");
+var Changeset = require('./utils/Changeset');
+var AttributePoolFactory = require("./utils/AttributePoolFactory");
function random() {
this.nextInt = function (maxValue) {
diff --git a/node/handler/APIHandler.js b/node/handler/APIHandler.js
index 2159cc406..0cd9cb58e 100644
--- a/node/handler/APIHandler.js
+++ b/node/handler/APIHandler.js
@@ -18,8 +18,10 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var fs = require("fs");
var api = require("../db/API");
+var padManager = require("../db/PadManager");
//ensure we have an apikey
var apikey = null;
@@ -94,7 +96,33 @@ exports.handle = function(functionName, fields, req, res)
res.send({code: 3, message: "no such function", data: null});
return;
}
-
+
+ //sanitize any pad id's before continuing
+ if(fields["padID"])
+ {
+ padManager.sanitizePadId(fields["padID"], function(padId)
+ {
+ fields["padID"] = padId;
+ callAPI(functionName, fields, req, res);
+ });
+ }
+ else if(fields["padName"])
+ {
+ padManager.sanitizePadId(fields["padName"], function(padId)
+ {
+ fields["padName"] = padId;
+ callAPI(functionName, fields, req, res);
+ });
+ }
+ else
+ {
+ callAPI(functionName, fields, req, res);
+ }
+}
+
+//calls the api function
+function callAPI(functionName, fields, req, res)
+{
//put the function parameters in an array
var functionParams = [];
for(var i=0;i -1)
- {
- setTimeout(function()
- {
- fs.unlink(destFile, callback);
- }, 100);
- }
+ if(os.type().indexOf("Windows") > -1)
+ {
+ setTimeout(function()
+ {
+ fs.unlink(destFile, callback);
+ }, 100);
+ }
+ else
+ {
+ fs.unlink(destFile, callback);
+ }
}
], callback);
}
], function(err)
{
- if(err && err != "stop") throw err;
+ if(err && err != "stop") ERR(err);
})
}
};
diff --git a/node/handler/ImportHandler.js b/node/handler/ImportHandler.js
index 283d105dd..06a2ce5ca 100644
--- a/node/handler/ImportHandler.js
+++ b/node/handler/ImportHandler.js
@@ -18,6 +18,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var padManager = require("../db/PadManager");
var padMessageHandler = require("./PadMessageHandler");
var async = require("async");
@@ -122,8 +123,9 @@ exports.doImport = function(req, res, padId)
{
padManager.getPad(padId, function(err, _pad)
{
+ if(ERR(err, callback)) return;
pad = _pad;
- callback(err);
+ callback();
});
},
@@ -132,17 +134,22 @@ exports.doImport = function(req, res, padId)
{
fs.readFile(destFile, "utf8", function(err, _text)
{
+ if(ERR(err, callback)) return;
text = _text;
//node on windows has a delay on releasing of the file lock.
//We add a 100ms delay to work around this
- if(os.type().indexOf("Windows") > -1)
- {
+ if(os.type().indexOf("Windows") > -1)
+ {
setTimeout(function()
{
- callback(err);
+ callback();
}, 100);
- }
+ }
+ else
+ {
+ callback();
+ }
});
},
@@ -176,7 +183,7 @@ exports.doImport = function(req, res, padId)
return;
}
- if(err) throw err;
+ ERR(err);
//close the connection
res.send("ok");
diff --git a/node/handler/PadMessageHandler.js b/node/handler/PadMessageHandler.js
index 188da5aea..0b48320b3 100644
--- a/node/handler/PadMessageHandler.js
+++ b/node/handler/PadMessageHandler.js
@@ -18,6 +18,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var async = require("async");
var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset");
@@ -27,7 +28,6 @@ var readOnlyManager = require("../db/ReadOnlyManager");
var settings = require('../utils/Settings');
var securityManager = require("../db/SecurityManager");
var log4js = require('log4js');
-var os = require("os");
var messageLogger = log4js.getLogger("message");
/**
@@ -107,7 +107,7 @@ exports.handleDisconnect = function(client)
//get the author color out of the db
authorManager.getAuthorColorId(author, function(err, color)
{
- if(err) throw err;
+ ERR(err);
//prepare the notification for the other users on the pad, that this user left
var messageToTheOtherUsers = {
@@ -218,16 +218,18 @@ function handleChatMessage(client, message)
{
padManager.getPad(padId, function(err, _pad)
{
+ if(ERR(err, callback)) return;
pad = _pad;
- callback(err);
+ callback();
});
},
function(callback)
{
authorManager.getAuthorName(userId, function(err, _userName)
{
+ if(ERR(err, callback)) return;
userName = _userName;
- callback(err);
+ callback();
});
},
//save the chat message and broadcast it
@@ -257,7 +259,7 @@ function handleChatMessage(client, message)
}
], function(err)
{
- if(err) throw err;
+ ERR(err);
});
}
@@ -375,8 +377,9 @@ function handleUserChanges(client, message)
{
padManager.getPad(session2pad[client.id], function(err, value)
{
+ if(ERR(err, callback)) return;
pad = value;
- callback(err);
+ callback();
});
},
//create the changeset
@@ -422,16 +425,10 @@ function handleUserChanges(client, message)
pad.getRevisionChangeset(r, function(err, c)
{
- if(err)
- {
- callback(err);
- return;
- }
- else
- {
- changeset = Changeset.follow(c, changeset, false, apool);
- callback(null);
- }
+ if(ERR(err, callback)) return;
+
+ changeset = Changeset.follow(c, changeset, false, apool);
+ callback(null);
});
},
//use the callback of the series function
@@ -469,7 +466,7 @@ function handleUserChanges(client, message)
}
], function(err)
{
- if(err) throw err;
+ ERR(err);
});
}
@@ -502,25 +499,23 @@ exports.updatePadClients = function(pad, callback)
{
pad.getRevisionAuthor(r, function(err, value)
{
+ if(ERR(err, callback)) return;
author = value;
- callback(err);
+ callback();
});
},
function (callback)
{
pad.getRevisionChangeset(r, function(err, value)
{
+ if(ERR(err, callback)) return;
revChangeset = value;
- callback(err);
+ callback();
});
}
], function(err)
{
- if(err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
if(author == sessioninfos[session].author)
{
@@ -633,7 +628,7 @@ function handleClientReady(client, message)
{
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
{
- if(err) {callback(err); return}
+ if(ERR(err, callback)) return;
//access was granted
if(statusObject.accessStatus == "grant")
@@ -657,8 +652,9 @@ function handleClientReady(client, message)
{
authorManager.getAuthorColorId(author, function(err, value)
{
+ if(ERR(err, callback)) return;
authorColorId = value;
- callback(err);
+ callback();
});
},
//get author name
@@ -666,24 +662,27 @@ function handleClientReady(client, message)
{
authorManager.getAuthorName(author, function(err, value)
{
+ if(ERR(err, callback)) return;
authorName = value;
- callback(err);
+ callback();
});
},
function(callback)
{
padManager.getPad(message.padId, function(err, value)
{
+ if(ERR(err, callback)) return;
pad = value;
- callback(err);
+ callback();
});
},
function(callback)
{
readOnlyManager.getReadOnlyId(message.padId, function(err, value)
{
+ if(ERR(err, callback)) return;
readOnlyId = value;
- callback(err);
+ callback();
});
}
], callback);
@@ -701,9 +700,10 @@ function handleClientReady(client, message)
{
authorManager.getAuthor(authorId, function(err, author)
{
+ if(ERR(err, callback)) return;
delete author.timestamp;
historicalAuthorData[authorId] = author;
- callback(err);
+ callback();
});
}, callback);
},
@@ -712,8 +712,9 @@ function handleClientReady(client, message)
{
pad.getLastChatMessages(100, function(err, _chatMessages)
{
+ if(ERR(err, callback)) return;
chatMessages = _chatMessages;
- callback(err);
+ callback();
});
}
], callback);
@@ -753,13 +754,6 @@ function handleClientReady(client, message)
var apool = attribsForWire.pool.toJsonable();
atext.attribs = attribsForWire.translated;
- //check if abiword is avaiable
- var abiwordAvailable = settings.abiword != null ? "yes" : "no";
- if(settings.abiword != null && os.type().indexOf("Windows") != -1)
- {
- abiwordAvailable = "withoutPDF";
- }
-
var clientVars = {
"accountPrivs": {
"maxRevisions": 100
@@ -796,7 +790,7 @@ function handleClientReady(client, message)
"fullWidth": false,
"hideSidebar": false
},
- "abiwordAvailable": abiwordAvailable,
+ "abiwordAvailable": settings.abiwordAvailable(),
"hooks": {}
}
@@ -806,24 +800,26 @@ function handleClientReady(client, message)
clientVars.userName = authorName;
}
- //This is a reconnect, so we don't have to send the client the ClientVars again
- if(message.reconnect == true)
+ if(sessioninfos[client.id] !== undefined)
{
- //Save the revision in sessioninfos, we take the revision from the info the client send to us
- sessioninfos[client.id].rev = message.client_rev;
+ //This is a reconnect, so we don't have to send the client the ClientVars again
+ if(message.reconnect == true)
+ {
+ //Save the revision in sessioninfos, we take the revision from the info the client send to us
+ sessioninfos[client.id].rev = message.client_rev;
+ }
+ //This is a normal first connect
+ else
+ {
+ //Send the clientVars to the Client
+ client.json.send(clientVars);
+ //Save the revision in sessioninfos
+ sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
+ }
+
+ //Save the revision and the author id in sessioninfos
+ sessioninfos[client.id].author = author;
}
- //This is a normal first connect
- else
- {
- //Send the clientVars to the Client
- client.json.send(clientVars);
- //Save the revision in sessioninfos
- sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
- }
-
- //Save the revision and the author id in sessioninfos
- sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
- sessioninfos[client.id].author = author;
//prepare the notification for the other users on the pad, that this user joined
var messageToTheOtherUsers = {
@@ -859,16 +855,18 @@ function handleClientReady(client, message)
{
authorManager.getAuthorColorId(sessioninfos[sessionID].author, function(err, value)
{
+ if(ERR(err, callback)) return;
sessionAuthorColorId = value;
- callback(err);
+ callback();
})
},
function(callback)
{
authorManager.getAuthorName(sessioninfos[sessionID].author, function(err, value)
{
+ if(ERR(err, callback)) return;
sessionAuthorName = value;
- callback(err);
+ callback();
})
}
],callback);
@@ -903,6 +901,6 @@ function handleClientReady(client, message)
}
],function(err)
{
- if(err) throw err;
+ ERR(err);
});
}
diff --git a/node/handler/SocketIORouter.js b/node/handler/SocketIORouter.js
index 00e3e4f3f..f3b82b8c7 100644
--- a/node/handler/SocketIORouter.js
+++ b/node/handler/SocketIORouter.js
@@ -19,6 +19,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
var securityManager = require("../db/SecurityManager");
@@ -109,7 +110,7 @@ exports.setSocketIO = function(_socket)
{
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
{
- if(err) throw err;
+ ERR(err);
//access was granted, mark the client as authorized and handle the message
if(statusObject.accessStatus == "grant")
diff --git a/node/handler/TimesliderMessageHandler.js b/node/handler/TimesliderMessageHandler.js
index 86864977f..b30a9fc9d 100644
--- a/node/handler/TimesliderMessageHandler.js
+++ b/node/handler/TimesliderMessageHandler.js
@@ -18,10 +18,12 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var async = require("async");
var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
+var settings = require('../utils/Settings');
var authorManager = require("../db/AuthorManager");
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
@@ -92,7 +94,7 @@ function handleClientReady(client, message)
//send the timeslider client the clientVars, with this values its able to start
createTimesliderClientVars (message.padId, function(err, clientVars)
{
- if(err) throw err;
+ ERR(err);
client.json.send({type: "CLIENT_VARS", data: clientVars});
})
@@ -138,7 +140,7 @@ function handleChangesetRequest(client, message)
//build the requested rough changesets and send them back
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
{
- if(err) throw err;
+ ERR(err);
var data = changesetInfo;
data.requestID = message.data.requestID;
@@ -159,6 +161,7 @@ function createTimesliderClientVars (padId, callback)
fullWidth: false,
disableRightBar: false,
initialChangesets: [],
+ abiwordAvailable: settings.abiwordAvailable(),
hooks: [],
initialStyledContents: {}
};
@@ -171,8 +174,9 @@ function createTimesliderClientVars (padId, callback)
{
padManager.getPad(padId, function(err, _pad)
{
+ if(ERR(err, callback)) return;
pad = _pad;
- callback(err);
+ callback();
});
},
//get all authors and add them to
@@ -187,15 +191,17 @@ function createTimesliderClientVars (padId, callback)
{
authorManager.getAuthor(authorId, function(err, author)
{
+ if(ERR(err, callback)) return;
historicalAuthorData[authorId] = author;
- callback(err);
+ callback();
});
}, function(err)
{
+ if(ERR(err, callback)) return;
//add historicalAuthorData to the clientVars and continue
clientVars.historicalAuthorData = historicalAuthorData;
clientVars.initialStyledContents.historicalAuthorData = historicalAuthorData;
- callback(err);
+ callback();
});
},
//get the timestamp of the last revision
@@ -203,8 +209,9 @@ function createTimesliderClientVars (padId, callback)
{
pad.getRevisionDate(pad.getHeadRevisionNumber(), function(err, date)
{
+ if(ERR(err, callback)) return;
clientVars.currentTime = date;
- callback(err);
+ callback();
});
},
function(callback)
@@ -235,14 +242,16 @@ function createTimesliderClientVars (padId, callback)
Math.floor(lastRev / topGranularity)*topGranularity+topGranularity, granularity,
function(err, changeset)
{
+ if(ERR(err, callback)) return;
clientVars.initialChangesets.push(changeset);
- callback(err);
+ callback();
});
}, callback);
}
], function(err)
{
- callback(err, clientVars);
+ if(ERR(err, callback)) return;
+ callback(null, clientVars);
});
}
@@ -267,8 +276,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
padManager.getPad(padId, function(err, _pad)
{
+ if(ERR(err, callback)) return;
pad = _pad;
- callback(err);
+ callback();
});
},
function(callback)
@@ -309,8 +319,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
composePadChangesets(padId, item.start, item.end, function(err, changeset)
{
+ if(ERR(err, callback)) return;
composedChangesets[item.start + "/" + item.end] = changeset;
- callback(err);
+ callback();
});
}, callback);
},
@@ -321,8 +332,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
pad.getRevisionDate(revNum, function(err, revDate)
{
+ if(ERR(err, callback)) return;
revisionDate[revNum] = Math.floor(revDate/1000);
- callback(err);
+ callback();
});
}, callback);
},
@@ -331,8 +343,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
getPadLines(padId, startNum-1, function(err, _lines)
{
+ if(ERR(err, callback)) return;
lines = _lines;
- callback(err);
+ callback();
});
}
], callback);
@@ -383,20 +396,15 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
}
], function(err)
{
- if(err)
- {
- callback(err);
- }
- else
- {
- callback(null, {forwardsChangesets: forwardsChangesets,
- backwardsChangesets: backwardsChangesets,
- apool: apool.toJsonable(),
- actualEndNum: endNum,
- timeDeltas: timeDeltas,
- start: startNum,
- granularity: granularity });
- }
+ if(ERR(err, callback)) return;
+
+ callback(null, {forwardsChangesets: forwardsChangesets,
+ backwardsChangesets: backwardsChangesets,
+ apool: apool.toJsonable(),
+ actualEndNum: endNum,
+ timeDeltas: timeDeltas,
+ start: startNum,
+ granularity: granularity });
});
}
@@ -416,8 +424,9 @@ function getPadLines(padId, revNum, callback)
{
padManager.getPad(padId, function(err, _pad)
{
+ if(ERR(err, callback)) return;
pad = _pad;
- callback(err);
+ callback();
});
},
//get the atext
@@ -427,8 +436,9 @@ function getPadLines(padId, revNum, callback)
{
pad.getInternalRevisionAText(revNum, function(err, _atext)
{
+ if(ERR(err, callback)) return;
atext = _atext;
- callback(err);
+ callback();
});
}
else
@@ -445,7 +455,8 @@ function getPadLines(padId, revNum, callback)
}
], function(err)
{
- callback(err, result);
+ if(ERR(err, callback)) return;
+ callback(null, result);
});
}
@@ -465,8 +476,9 @@ function composePadChangesets(padId, startNum, endNum, callback)
{
padManager.getPad(padId, function(err, _pad)
{
+ if(ERR(err, callback)) return;
pad = _pad;
- callback(err);
+ callback();
});
},
//fetch all changesets we need
@@ -486,8 +498,9 @@ function composePadChangesets(padId, startNum, endNum, callback)
{
pad.getRevisionChangeset(revNum, function(err, value)
{
+ if(ERR(err, callback)) return;
changesets[revNum] = value;
- callback(err);
+ callback();
});
},callback);
},
@@ -509,7 +522,7 @@ function composePadChangesets(padId, startNum, endNum, callback)
//return err and changeset
function(err)
{
- if(err) throw err;
- callback(err, changeset);
+ if(ERR(err, callback)) return;
+ callback(null, changeset);
});
}
diff --git a/node/server.js b/node/server.js
index 08c09ab0e..6a4f0cfe2 100644
--- a/node/server.js
+++ b/node/server.js
@@ -20,6 +20,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var log4js = require('log4js');
var os = require("os");
var socketio = require('socket.io');
@@ -91,6 +92,9 @@ async.waterfall([
var httpLogger = log4js.getLogger("http");
app.configure(function()
{
+ // Activate http basic auth if it has been defined in settings.json
+ if(settings.httpAuth != null) 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.
// 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"))
@@ -98,6 +102,12 @@ async.waterfall([
app.use(express.cookieParser());
});
+ app.error(function(err, req, res, next){
+ res.send(500);
+ console.error(err.stack ? err.stack : err.toString());
+ gracefulShutdown();
+ });
+
//serve static files
app.get('/static/*', function(req, res)
{
@@ -129,7 +139,7 @@ async.waterfall([
{
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
{
- if(err) throw err;
+ if(ERR(err, callback)) return;
//there is access, continue
if(accessObj.accessStatus == "grant")
@@ -143,6 +153,26 @@ async.waterfall([
}
});
}
+
+ //checks for basic http auth
+ function basic_auth (req, res, next) {
+ if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
+ // fetch login and password
+ if (new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString() == settings.httpAuth) {
+ next();
+ return;
+ }
+ }
+
+ res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
+ if (req.headers.authorization) {
+ setTimeout(function () {
+ res.send('Authentication required', 401);
+ }, 1000);
+ } else {
+ res.send('Authentication required', 401);
+ }
+ }
//serve read only pad
app.get('/ro/:id', function(req, res)
@@ -159,12 +189,14 @@ async.waterfall([
{
readOnlyManager.getPadId(req.params.id, function(err, _padId)
{
+ if(ERR(err, callback)) return;
+
padId = _padId;
//we need that to tell hasPadAcess about the pad
req.params.pad = padId;
- callback(err);
+ callback();
});
},
//render the html document
@@ -182,8 +214,9 @@ async.waterfall([
//render the html document
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
{
+ if(ERR(err, callback)) return;
html = _html;
- callback(err);
+ callback();
});
});
}
@@ -191,7 +224,7 @@ async.waterfall([
{
//throw any unexpected error
if(err && err != "notfound")
- throw err;
+ ERR(err);
if(err == "notfound")
res.send('404 - Not Found', 404);
@@ -199,100 +232,108 @@ async.waterfall([
res.send(html);
});
});
-
- //serve pad.html under /p
- app.get('/p/:pad', function(req, res, next)
- {
+
+ //redirects browser to the pad's sanitized url if needed. otherwise, renders the html
+ function goToPad(req, res, render) {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
- return;
}
-
- res.header("Server", serverName);
- var filePath = path.normalize(__dirname + "/../static/pad.html");
- res.sendfile(filePath, { maxAge: exports.maxAge });
+ else
+ {
+ padManager.sanitizePadId(req.params.pad, function(padId) {
+ //the pad id was sanitized, so we redirect to the sanitized version
+ if(padId != req.params.pad)
+ {
+ var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + padId);
+ res.header('Location', real_path);
+ res.send('You should be redirected to ' + real_path + '', 302);
+ }
+ //the pad id was fine, so just render it
+ else
+ {
+ render();
+ }
+ });
+ }
+ }
+
+ //serve pad.html under /p
+ app.get('/p/:pad', function(req, res, next)
+ {
+ goToPad(req, res, function() {
+ res.header("Server", serverName);
+ var filePath = path.normalize(__dirname + "/../static/pad.html");
+ res.sendfile(filePath, { maxAge: exports.maxAge });
+ });
});
//serve timeslider.html under /p/$padname/timeslider
app.get('/p/:pad/timeslider', function(req, res, next)
{
- //ensure the padname is valid and the url doesn't end with a /
- if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
- {
- res.send('Such a padname is forbidden', 404);
- return;
- }
-
- res.header("Server", serverName);
- var filePath = path.normalize(__dirname + "/../static/timeslider.html");
- res.sendfile(filePath, { maxAge: exports.maxAge });
+ goToPad(req, res, function() {
+ res.header("Server", serverName);
+ var filePath = path.normalize(__dirname + "/../static/timeslider.html");
+ res.sendfile(filePath, { maxAge: exports.maxAge });
+ });
});
//serve timeslider.html under /p/$padname/timeslider
- app.get('/p/:pad/export/:type', function(req, res, next)
+ app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
{
- //ensure the padname is valid and the url doesn't end with a /
- if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
- {
- res.send('Such a padname is forbidden', 404);
- return;
- }
-
- var types = ["pdf", "doc", "txt", "html", "odt"];
- //send a 404 if we don't support this filetype
- if(types.indexOf(req.params.type) == -1)
- {
- next();
- return;
- }
-
- //if abiword is disabled, and this is a format we only support with abiword, output a message
- if(settings.abiword == null && req.params.type != "html" && req.params.type != "txt" )
- {
- res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
- return;
- }
-
- res.header("Access-Control-Allow-Origin", "*");
- res.header("Server", serverName);
-
- hasPadAccess(req, res, function()
- {
- exportHandler.doExport(req, res, req.params.pad, req.params.type);
+ goToPad(req, res, function() {
+ var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
+ //send a 404 if we don't support this filetype
+ if(types.indexOf(req.params.type) == -1)
+ {
+ next();
+ return;
+ }
+
+ //if abiword is disabled, and this is a format we only support with abiword, output a message
+ if(settings.abiword == null &&
+ ["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");
+ return;
+ }
+
+ res.header("Access-Control-Allow-Origin", "*");
+ res.header("Server", serverName);
+
+ hasPadAccess(req, res, function()
+ {
+ exportHandler.doExport(req, res, req.params.pad, req.params.type);
+ });
});
});
//handle import requests
app.post('/p/:pad/import', function(req, res, next)
{
- //ensure the padname is valid and the url doesn't end with a /
- if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
- {
- res.send('Such a padname is forbidden', 404);
- return;
- }
-
- //if abiword is disabled, skip handling this request
- if(settings.abiword == null)
- {
- next();
- return;
- }
+ goToPad(req, res, function() {
+ //if abiword is disabled, skip handling this request
+ if(settings.abiword == null)
+ {
+ next();
+ return;
+ }
- res.header("Server", serverName);
-
- hasPadAccess(req, res, function()
- {
- importHandler.doImport(req, res, req.params.pad);
+ res.header("Server", serverName);
+
+ hasPadAccess(req, res, function()
+ {
+ importHandler.doImport(req, res, req.params.pad);
+ });
});
});
var apiLogger = log4js.getLogger("API");
//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("Content-Type", "application/json; charset=utf-8");
diff --git a/node/utils/Changeset.js b/node/utils/Changeset.js
index 2c9e06838..9e1b60ebe 100644
--- a/node/utils/Changeset.js
+++ b/node/utils/Changeset.js
@@ -3,6 +3,12 @@
* 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 helps other people to understand this code better and helps them to improve it.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/*
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
diff --git a/node/utils/ExportDokuWiki.js b/node/utils/ExportDokuWiki.js
new file mode 100644
index 000000000..48e4b2915
--- /dev/null
+++ b/node/utils/ExportDokuWiki.js
@@ -0,0 +1,344 @@
+/**
+ * Copyright 2011 Adrian Lang
+ *
+ * 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 async = require("async");
+var Changeset = require("./Changeset");
+var padManager = require("../db/PadManager");
+
+function getPadDokuWiki(pad, revNum, callback)
+{
+ var atext = pad.atext;
+ var dokuwiki;
+ async.waterfall([
+ // fetch revision atext
+
+
+ function (callback)
+ {
+ if (revNum != undefined)
+ {
+ pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
+ {
+ atext = revisionAtext;
+ callback(err);
+ });
+ }
+ else
+ {
+ callback(null);
+ }
+ },
+
+ // convert atext to dokuwiki text
+
+ function (callback)
+ {
+ dokuwiki = getDokuWikiFromAtext(pad, atext);
+ callback(null);
+ }],
+ // run final callback
+
+
+ function (err)
+ {
+ callback(err, dokuwiki);
+ });
+}
+
+function getDokuWikiFromAtext(pad, atext)
+{
+ var apool = pad.apool();
+ var textLines = atext.text.slice(0, -1).split('\n');
+ var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
+
+ var tags = ['======', '=====', '**', '//', '__', 'del>'];
+ var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough'];
+ var anumMap = {};
+
+ props.forEach(function (propName, i)
+ {
+ var propTrueNum = apool.putAttrib([propName, true], true);
+ if (propTrueNum >= 0)
+ {
+ anumMap[propTrueNum] = i;
+ }
+ });
+
+ function getLineDokuWiki(text, attribs)
+ {
+ var propVals = [false, false, false];
+ var ENTER = 1;
+ var STAY = 2;
+ var LEAVE = 0;
+
+ // Use order of tags (b/i/u) as order of nesting, for simplicity
+ // and decent nesting. For example,
+ // Just boldBold and italicsJust italics
+ // becomes
+ // Just bold Bold and italicsJust italics
+ var taker = Changeset.stringIterator(text);
+ var assem = Changeset.stringAssembler();
+
+ function emitOpenTag(i)
+ {
+ if (tags[i].indexOf('>') !== -1) {
+ assem.append('<');
+ }
+ assem.append(tags[i]);
+ }
+
+ function emitCloseTag(i)
+ {
+ if (tags[i].indexOf('>') !== -1) {
+ assem.append('');
+ }
+ assem.append(tags[i]);
+ }
+
+ var urls = _findURLs(text);
+
+ var idx = 0;
+
+ function processNextChars(numChars)
+ {
+ if (numChars <= 0)
+ {
+ return;
+ }
+
+ var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
+ idx += numChars;
+
+ while (iter.hasNext())
+ {
+ var o = iter.next();
+ var propChanged = false;
+ Changeset.eachAttribNumber(o.attribs, function (a)
+ {
+ if (a in anumMap)
+ {
+ var i = anumMap[a]; // i = 0 => bold, etc.
+ if (!propVals[i])
+ {
+ propVals[i] = ENTER;
+ propChanged = true;
+ }
+ else
+ {
+ propVals[i] = STAY;
+ }
+ }
+ });
+ for (var i = 0; i < propVals.length; i++)
+ {
+ if (propVals[i] === true)
+ {
+ propVals[i] = LEAVE;
+ propChanged = true;
+ }
+ else if (propVals[i] === STAY)
+ {
+ propVals[i] = true; // set it back
+ }
+ }
+ // now each member of propVal is in {false,LEAVE,ENTER,true}
+ // according to what happens at start of span
+ if (propChanged)
+ {
+ // leaving bold (e.g.) also leaves italics, etc.
+ var left = false;
+ for (var i = 0; i < propVals.length; i++)
+ {
+ var v = propVals[i];
+ if (!left)
+ {
+ if (v === LEAVE)
+ {
+ left = true;
+ }
+ }
+ else
+ {
+ if (v === true)
+ {
+ propVals[i] = STAY; // tag will be closed and re-opened
+ }
+ }
+ }
+
+ for (var i = propVals.length - 1; i >= 0; i--)
+ {
+ if (propVals[i] === LEAVE)
+ {
+ emitCloseTag(i);
+ propVals[i] = false;
+ }
+ else if (propVals[i] === STAY)
+ {
+ emitCloseTag(i);
+ }
+ }
+ for (var i = 0; i < propVals.length; i++)
+ {
+ if (propVals[i] === ENTER || propVals[i] === STAY)
+ {
+ emitOpenTag(i);
+ propVals[i] = true;
+ }
+ }
+ // propVals is now all {true,false} again
+ } // end if (propChanged)
+ var chars = o.chars;
+ if (o.lines)
+ {
+ chars--; // exclude newline at end of line, if present
+ }
+ var s = taker.take(chars);
+
+ assem.append(_escapeDokuWiki(s));
+ } // end iteration over spans in line
+ for (var i = propVals.length - 1; i >= 0; i--)
+ {
+ if (propVals[i])
+ {
+ emitCloseTag(i);
+ propVals[i] = false;
+ }
+ }
+ } // end processNextChars
+ if (urls)
+ {
+ urls.forEach(function (urlData)
+ {
+ var startIndex = urlData[0];
+ var url = urlData[1];
+ var urlLength = url.length;
+ processNextChars(startIndex - idx);
+ assem.append('[[');
+
+ // Do not use processNextChars since a link does not contain syntax and
+ // needs no escaping
+ var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + urlLength));
+ idx += urlLength;
+ assem.append(taker.take(iter.next().chars));
+
+ assem.append(']]');
+ });
+ }
+ processNextChars(text.length - idx);
+
+ return assem.toString() + "\n";
+ } // end getLineDokuWiki
+ var pieces = [];
+
+ for (var i = 0; i < textLines.length; i++)
+ {
+ var line = _analyzeLine(textLines[i], attribLines[i], apool);
+ var lineContent = getLineDokuWiki(line.text, line.aline);
+
+ if (line.listLevel && lineContent)
+ {
+ pieces.push(new Array(line.listLevel + 1).join(' ') + '* ');
+ }
+ pieces.push(lineContent);
+ }
+
+ return pieces.join('');
+}
+
+function _analyzeLine(text, aline, apool)
+{
+ var line = {};
+
+ // identify list
+ var lineMarker = 0;
+ line.listLevel = 0;
+ if (aline)
+ {
+ var opIter = Changeset.opIterator(aline);
+ if (opIter.hasNext())
+ {
+ var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool);
+ if (listType)
+ {
+ lineMarker = 1;
+ listType = /([a-z]+)([12345678])/.exec(listType);
+ if (listType)
+ {
+ line.listTypeName = listType[1];
+ line.listLevel = Number(listType[2]);
+ }
+ }
+ }
+ }
+ if (lineMarker)
+ {
+ line.text = text.substring(1);
+ line.aline = Changeset.subattribution(aline, 1);
+ }
+ else
+ {
+ line.text = text;
+ line.aline = aline;
+ }
+
+ return line;
+}
+
+exports.getPadDokuWikiDocument = function (padId, revNum, callback)
+{
+ padManager.getPad(padId, function (err, pad)
+ {
+ if (err)
+ {
+ callback(err);
+ return;
+ }
+
+ getPadDokuWiki(pad, revNum, callback);
+ });
+}
+
+function _escapeDokuWiki(s)
+{
+ s = s.replace(/(\/\/|\*\*|__)/g, '%%$1%%');
+ return s;
+}
+
+// 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_SPACE = /\s/;
+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');
+
+// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
+
+
+function _findURLs(text)
+{
+ _REGEX_URL.lastIndex = 0;
+ var urls = null;
+ var execResult;
+ while ((execResult = _REGEX_URL.exec(text)))
+ {
+ urls = (urls || []);
+ var startIndex = execResult.index;
+ var url = execResult[0];
+ urls.push([startIndex, url]);
+ }
+
+ return urls;
+}
diff --git a/node/utils/ExportHtml.js b/node/utils/ExportHtml.js
index fe5ee9e46..8d33d32ef 100644
--- a/node/utils/ExportHtml.js
+++ b/node/utils/ExportHtml.js
@@ -17,6 +17,7 @@
var async = require("async");
var Changeset = require("./Changeset");
var padManager = require("../db/PadManager");
+var ERR = require("async-stacktrace");
function getPadPlainText(pad, revNum)
{
@@ -58,8 +59,9 @@ function getPadHTML(pad, revNum, callback)
{
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
{
+ if(ERR(err, callback)) return;
atext = revisionAtext;
- callback(err);
+ callback();
});
}
else
@@ -81,7 +83,8 @@ function getPadHTML(pad, revNum, callback)
function (err)
{
- callback(err, html);
+ if(ERR(err, callback)) return;
+ callback(null, html);
});
}
@@ -459,11 +462,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
{
padManager.getPad(padId, function (err, pad)
{
- if (err)
- {
- callback(err);
- return;
- }
+ if(ERR(err, callback)) return;
var head =
(noDocType ? '' : '\n') +
@@ -488,7 +487,8 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
getPadHTML(pad, revNum, function (err, html)
{
- callback(err, head + html + foot);
+ if(ERR(err, callback)) return;
+ callback(null, head + html + foot);
});
});
}
diff --git a/node/utils/ImportHtml.js b/node/utils/ImportHtml.js
index 6441708e1..1b0bcaea7 100644
--- a/node/utils/ImportHtml.js
+++ b/node/utils/ImportHtml.js
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-var jsdom = require('jsdom').jsdom;
+var jsdom = require('jsdom-nocontextifiy').jsdom;
var log4js = require('log4js');
var Changeset = require("./Changeset");
diff --git a/node/utils/Minify.js b/node/utils/Minify.js
index dbb409666..588456fd9 100644
--- a/node/utils/Minify.js
+++ b/node/utils/Minify.js
@@ -19,6 +19,7 @@
* limitations under the License.
*/
+var ERR = require("async-stacktrace");
var settings = require('./Settings');
var async = require('async');
var fs = require('fs');
@@ -33,7 +34,7 @@ var os = require('os');
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", "excanvas.js", "farbtastic.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"];
+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", "pad_impexp.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"];
/**
* creates the minifed javascript for the given minified name
@@ -77,7 +78,7 @@ exports.minifyJS = function(req, res, jsFilename)
//read the files in the folder
fs.readdir(path, function(err, files)
{
- if(err) { callback(err); return; }
+ if(ERR(err, callback)) return;
//we wanna check the directory itself for changes too
files.push(".");
@@ -88,7 +89,7 @@ exports.minifyJS = function(req, res, jsFilename)
//get the stat data of this file
fs.stat(path + "/" + filename, function(err, stats)
{
- if(err) { callback(err); return; }
+ if(ERR(err, callback)) return;
//get the modification time
var modificationTime = stats.mtime.getTime();
@@ -110,7 +111,11 @@ exports.minifyJS = function(req, res, jsFilename)
//check the modification time of the minified js
fs.stat("../var/minified_" + jsFilename, function(err, stats)
{
- if(err && err.code != "ENOENT") callback(err);
+ if(err && err.code != "ENOENT")
+ {
+ ERR(err, callback);
+ return;
+ }
//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)
@@ -131,8 +136,9 @@ exports.minifyJS = function(req, res, jsFilename)
{
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
{
+ if(ERR(err, callback)) return;
fileValues[item] = data;
- callback(err);
+ callback();
});
}, callback);
},
@@ -161,6 +167,8 @@ exports.minifyJS = function(req, res, jsFilename)
//read the included file
fs.readFile(filename, "utf-8", function(err, data)
{
+ if(ERR(err, callback)) return;
+
//compress the file
if(type == "JS")
{
@@ -188,17 +196,19 @@ exports.minifyJS = function(req, res, jsFilename)
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
embeds[item] = "'" + embeds[item] + "'";
- callback(err);
+ callback();
});
}, function(err)
{
+ if(ERR(err, callback)) return;
+
//replace the include command with the include
for(var i in embeds)
{
fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]);
}
- callback(err);
+ callback();
});
},
//put all together and write it into a file
@@ -227,7 +237,10 @@ exports.minifyJS = function(req, res, jsFilename)
if(os.type().indexOf("Windows") == -1)
{
gzip(result, 9, function(err, compressedResult){
- if(err) {callback(err); return}
+ //weird gzip bug that returns 0 instead of null if everything is ok
+ err = err === 0 ? null : err;
+
+ if(ERR(err, callback)) return;
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
});
@@ -242,7 +255,10 @@ exports.minifyJS = function(req, res, jsFilename)
}
], function(err)
{
- if(err && err != "stop") throw err;
+ if(err && err != "stop")
+ {
+ if(ERR(err)) return;
+ }
//check if gzip is supported by this browser
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1;
@@ -270,15 +286,16 @@ exports.minifyJS = function(req, res, jsFilename)
async.forEach(jsFiles, function (item, callback)
{
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
- {
+ {
+ if(ERR(err, callback)) return;
fileValues[item] = data;
- callback(err);
+ callback();
});
},
//send all files together
function(err)
{
- if(err) throw err;
+ if(ERR(err)) return;
for(var i=0;i li {
+ padding: 4px 8px;
+ margin-top: 2px;
+ }
+ #chaticon {
+ opacity: .8;
+ }
+ #users {
+ right: 4px;
+ }
+ #mycolorpicker {
+ left: -72px; /* #mycolorpicker:width - #users:width */
+ }
+ #editorcontainer {
+ margin-bottom: 33px;
+ }
+ #editbar ul#menu_right {
+ background: #f7f7f7;
+ background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ width: 100%;
+ overflow: hidden;
+ height: 32px;
+ position: fixed;
+ bottom: 0;
+ border-top: 1px solid #ccc;
+ }
+ #editbar ul#menu_right li:not(:last-child) {
+ display: none;
+ }
+ #editbar ul#menu_right li:last-child {
+ height: 24px;
+ border-radius: 0;
+ margin-top: 0;
+ border: 0;
+ float: right;
+ }
+ #chaticon {
+ bottom: 0;
+ right: 55px;
+ border-right: none;
+ border-radius: 0;
+ background: #f7f7f7;
+ background: -moz-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ background: -ms-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ background: -o-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ background: -webkit-linear-gradient(#f7f7f7, #f1f1f1 80%);
+ border: 0;
+ }
+ #chatbox {
+ bottom: 32px;
+ right: 0;
+ border-top-right-radius: 0;
+ }
+ #editbar ul li a span {
+ top: -3px;
+ }
+ #usericonback {
+ margin-top: 4px;
+ }
+}
+.rtl{
+ direction:RTL;
+}
diff --git a/static/css/timeslider.css b/static/css/timeslider.css
index c808a65c3..9df408683 100644
--- a/static/css/timeslider.css
+++ b/static/css/timeslider.css
@@ -195,6 +195,11 @@ float:right;
color: #222;
}
+#importexport{
+ top:103px;
+ width:185px;
+}
+
ul { margin-left: 1.5em; }
ul ul { margin-left: 0 !important; }
ul.list-bullet1 { margin-left: 1.5em; }
diff --git a/static/img/fileicons.gif b/static/img/fileicons.gif
index 26f63882b..c03b6031a 100644
Binary files a/static/img/fileicons.gif and b/static/img/fileicons.gif differ
diff --git a/static/index.html b/static/index.html
index 2bed58f98..eb41e8afd 100644
--- a/static/index.html
+++ b/static/index.html
@@ -1,6 +1,8 @@
+
Etherpad Lite
+
-
-
New Pad
or create/open a Pad with the name
-
@@ -124,5 +145,4 @@
//start the costum js
if(typeof costumStart == "function") costumStart();
-
\ No newline at end of file
diff --git a/static/js/ace.js b/static/js/ace.js
index 27899aaab..69d3cf09b 100644
--- a/static/js/ace.js
+++ b/static/js/ace.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js
index de2fede1c..1246a16ec 100644
--- a/static/js/ace2_common.js
+++ b/static/js/ace2_common.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
@@ -142,4 +148,4 @@ function htmlPrettyEscape(str)
if (typeof exports !== "undefined")
{
exports.map = map;
-}
\ No newline at end of file
+}
diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js
index 7b0aa89f9..15c6debae 100644
--- a/static/js/ace2_inner.js
+++ b/static/js/ace2_inner.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
@@ -79,6 +85,11 @@ function OUTER(gscope)
var doesWrap = true;
var hasLineNumbers = true;
var isStyled = true;
+
+ // check for mobile os presence
+ var ua = navigator.userAgent.toLowerCase();
+ var isAndroid = ua.indexOf("android") > -1;
+ var isMobileSafari = ua.indexOf("mobile") > -1;
// space around the innermost iframe element
var iframePadLeft = MIN_LINEDIV_WIDTH + LINE_NUMBER_PADDING_RIGHT + EDIT_BODY_PADDING_LEFT;
@@ -1099,6 +1110,8 @@ function OUTER(gscope)
else if (k == "showslinenumbers")
{
hasLineNumbers = !! value;
+ // disable line numbers on mobile devices
+ if(isAndroid || isMobileSafari) hasLineNumbers = false;
setClassPresence(sideDiv, "sidedivhidden", !hasLineNumbers);
fixView();
}
@@ -1127,6 +1140,10 @@ function OUTER(gscope)
{
setTextSize(value);
}
+ else if (k == 'rtlistrue')
+ {
+ setClassPresence(root, "rtl", !! value);
+ }
}
editorInfo.ace_setBaseText = function(txt)
@@ -4069,8 +4086,6 @@ function OUTER(gscope)
catch (e)
{}
if (!origSelectionRange) return false;
- var selectionParent = origSelectionRange.parentElement();
- if (selectionParent.ownerDocument != doc) return false;
return true;
}
@@ -5779,31 +5794,17 @@ function OUTER(gscope)
{
var newNumLines = rep.lines.length();
if (newNumLines < 1) newNumLines = 1;
- if (newNumLines != lineNumbersShown)
- {
- var container = sideDivInner;
- var odoc = outerWin.document;
- while (lineNumbersShown < newNumLines)
- {
- lineNumbersShown++;
- var n = lineNumbersShown;
- var div = odoc.createElement("DIV");
- div.appendChild(odoc.createTextNode(String(n)));
- container.appendChild(div);
- }
- while (lineNumbersShown > newNumLines)
- {
- container.removeChild(container.lastChild);
- lineNumbersShown--;
- }
- }
-
+ //update height of all current line numbers
if (currentCallStack && currentCallStack.domClean)
{
var a = sideDivInner.firstChild;
var b = doc.body.firstChild;
+ var n = 0;
while (a && b)
{
+ if(n > lineNumbersShown) //all updated, break
+ break;
+
var h = (b.clientHeight || b.offsetHeight);
if (b.nextSibling)
{
@@ -5817,10 +5818,42 @@ function OUTER(gscope)
if (h)
{
var hpx = h + "px";
- if (a.style.height != hpx) a.style.height = hpx;
+ if (a.style.height != hpx) {
+ a.style.height = hpx;
+ }
}
a = a.nextSibling;
b = b.nextSibling;
+ n++;
+ }
+ }
+
+ if (newNumLines != lineNumbersShown)
+ {
+ var container = sideDivInner;
+ var odoc = outerWin.document;
+ var fragment = odoc.createDocumentFragment();
+ while (lineNumbersShown < newNumLines)
+ {
+ lineNumbersShown++;
+ var n = lineNumbersShown;
+ var div = odoc.createElement("DIV");
+ //calculate height for new line number
+ var h = (b.clientHeight || b.offsetHeight);
+ if (b.nextSibling)
+ h = b.nextSibling.offsetTop - b.offsetTop;
+ if(h) // apply style to div
+ div.style.height = h +"px";
+
+ div.appendChild(odoc.createTextNode(String(n)));
+ fragment.appendChild(div);
+ b = b.nextSibling;
+ }
+ container.appendChild(fragment);
+ while (lineNumbersShown > newNumLines)
+ {
+ container.removeChild(container.lastChild);
+ lineNumbersShown--;
}
}
}
diff --git a/static/js/broadcast.js b/static/js/broadcast.js
index 32424731b..865574abd 100644
--- a/static/js/broadcast.js
+++ b/static/js/broadcast.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/broadcast_revisions.js b/static/js/broadcast_revisions.js
index 828677733..33a2f9dfa 100644
--- a/static/js/broadcast_revisions.js
+++ b/static/js/broadcast_revisions.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/broadcast_slider.js b/static/js/broadcast_slider.js
index 68f424495..af5a50411 100644
--- a/static/js/broadcast_slider.js
+++ b/static/js/broadcast_slider.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/changesettracker.js b/static/js/changesettracker.js
index 09e413f39..cc7f64623 100644
--- a/static/js/changesettracker.js
+++ b/static/js/changesettracker.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/chat.js b/static/js/chat.js
index 1bc44d201..4dade69f3 100644
--- a/static/js/chat.js
+++ b/static/js/chat.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
@@ -16,21 +22,33 @@
var chat = (function()
{
+ var ua = navigator.userAgent.toLowerCase();
+ var isAndroid = ua.indexOf("android") > -1;
+ var isMobileSafari = ua.indexOf("mobile") > -1;
+ var bottomMargin = "0px";
+ var sDuration = 500;
+ var hDuration = 750;
var chatMentions = 0;
var title = document.title;
+ if (isAndroid || isMobileSafari){
+ sDuration = 0;
+ hDuration = 0;
+ }
var self = {
show: function ()
{
$("#chaticon").hide("slide", {
direction: "down"
- }, 500, function ()
+ }, hDuration, function ()
{
$("#chatbox").show("slide", {
direction: "down"
- }, 750, self.scrollDown);
+ }, sDuration, self.scrollDown);
$("#chatbox").resizable(
{
handles: 'nw',
+ minHeight: 40,
+ minWidth: 80,
start: function (event, ui)
{
$("#focusprotector").show();
@@ -39,7 +57,10 @@ var chat = (function()
{
$("#focusprotector").hide();
- $("#chatbox").css({right: "20px", bottom: "0px", left: "", top: ""});
+ if(isAndroid || isMobileSafari)
+ bottommargin = "32px";
+
+ $("#chatbox").css({right: "20px", bottom: bottomMargin, left: "", top: ""});
self.scrollDown();
}
@@ -51,9 +72,9 @@ var chat = (function()
hide: function ()
{
$("#chatcounter").text("0");
- $("#chatbox").hide("slide", { direction: "down" }, 750, function()
+ $("#chatbox").hide("slide", { direction: "down" }, sDuration, function()
{
- $("#chaticon").show("slide", { direction: "down" }, 500);
+ $("#chaticon").show("slide", { direction: "down" }, hDuration);
});
},
scrollDown: function()
diff --git a/static/js/collab_client.js b/static/js/collab_client.js
index c64b99949..fafe0fd2f 100644
--- a/static/js/collab_client.js
+++ b/static/js/collab_client.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/colorutils.js b/static/js/colorutils.js
index 03141271b..92d8da719 100644
--- a/static/js/colorutils.js
+++ b/static/js/colorutils.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/colorutils.js
// THIS FILE IS ALSO SERVED AS CLIENT-SIDE JS
/**
diff --git a/static/js/contentcollector.js b/static/js/contentcollector.js
index ac7e34028..883ca09f0 100644
--- a/static/js/contentcollector.js
+++ b/static/js/contentcollector.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.contentcollector
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
// %APPJET%: import("etherpad.admin.plugins");
diff --git a/static/js/cssmanager.js b/static/js/cssmanager.js
index 5601ea25b..b8208b83f 100644
--- a/static/js/cssmanager.js
+++ b/static/js/cssmanager.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/cssmanager_client.js b/static/js/cssmanager_client.js
index 2fa61027e..60bf9f8c5 100644
--- a/static/js/cssmanager_client.js
+++ b/static/js/cssmanager_client.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/cssmanager.js
/**
* Copyright 2009 Google Inc.
diff --git a/static/js/domline.js b/static/js/domline.js
index f081f7df3..0b9abd3f1 100644
--- a/static/js/domline.js
+++ b/static/js/domline.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
// %APPJET%: import("etherpad.admin.plugins");
/**
diff --git a/static/js/domline_client.js b/static/js/domline_client.js
index b37996d92..9f47dae54 100644
--- a/static/js/domline_client.js
+++ b/static/js/domline_client.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/domline.js
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline
/**
diff --git a/static/js/draggable.js b/static/js/draggable.js
index 2f40c1c7e..7bf523eb5 100644
--- a/static/js/draggable.js
+++ b/static/js/draggable.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/easysync2.js b/static/js/easysync2.js
index 7f5d0cf98..17ab585d9 100644
--- a/static/js/easysync2.js
+++ b/static/js/easysync2.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2
// %APPJET%: jimport("com.etherpad.Easysync2Support");
/**
diff --git a/static/js/easysync2_client.js b/static/js/easysync2_client.js
index 5d0e582d8..3611da233 100644
--- a/static/js/easysync2_client.js
+++ b/static/js/easysync2_client.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/easysync2.js
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2
/**
diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js
index ace55f38d..13a746186 100644
--- a/static/js/linestylefilter.js
+++ b/static/js/linestylefilter.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
// %APPJET%: import("etherpad.collab.ace.easysync2.Changeset");
// %APPJET%: import("etherpad.admin.plugins");
diff --git a/static/js/linestylefilter_client.js b/static/js/linestylefilter_client.js
index 9c3ae01c9..9fd2a3f8c 100644
--- a/static/js/linestylefilter_client.js
+++ b/static/js/linestylefilter_client.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/linestylefilter.js
// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter
/**
diff --git a/static/js/pad2.js b/static/js/pad2.js
index 6f398bade..bbb385b3b 100644
--- a/static/js/pad2.js
+++ b/static/js/pad2.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
@@ -21,6 +27,8 @@ var LineNumbersDisabled = false;
var noColors = false;
var useMonospaceFontGlobal = false;
var globalUserName = false;
+var hideQRCode = false;
+var rtlIsTrue = false;
$(document).ready(function()
{
@@ -86,12 +94,15 @@ function getParams()
var showLineNumbers = params["showLineNumbers"];
var useMonospaceFont = params["useMonospaceFont"];
var IsnoColors = params["noColors"];
+ var hideQRCode = params["hideQRCode"];
+ var rtl = params["rtl"];
if(IsnoColors)
{
if(IsnoColors == "true")
{
noColors = true;
+ $('#clearAuthorship').hide();
}
}
if(showControls)
@@ -102,7 +113,6 @@ function getParams()
$('#editorcontainer').css({"top":"0px"});
}
}
-
if(showChat)
{
if(showChat == "false")
@@ -110,7 +120,6 @@ function getParams()
$('#chaticon').hide();
}
}
-
if(showLineNumbers)
{
if(showLineNumbers == "false")
@@ -118,7 +127,6 @@ function getParams()
LineNumbersDisabled = true;
}
}
-
if(useMonospaceFont)
{
if(useMonospaceFont == "true")
@@ -126,13 +134,22 @@ function getParams()
useMonospaceFontGlobal = true;
}
}
-
-
if(userName)
{
// If the username is set as a parameter we should set a global value that we can call once we have initiated the pad.
globalUserName = unescape(userName);
}
+ if(hideQRCode)
+ {
+ $('#qrcode').hide();
+ }
+ if(rtl)
+ {
+ if(rtl == "true")
+ {
+ rtlIsTrue = true
+ }
+ }
}
function getUrlVars()
@@ -177,7 +194,7 @@ function handshake()
padId = decodeURIComponent(padId); // unescape neccesary due to Safari and Opera interpretation of spaces
if(!isReconnect)
- document.title = document.title + " | " + padId;
+ document.title = document.title + " | " + padId.replace(/_+/g, ' ');
var token = readCookie("token");
if (token == null)
@@ -291,6 +308,11 @@ function handshake()
{
pad.changeViewOption('noColors', true);
}
+
+ if (rtlIsTrue == true)
+ {
+ pad.changeViewOption('rtl', true);
+ }
// If the Monospacefont value is set to true then change it to monospace.
if (useMonospaceFontGlobal == true)
diff --git a/static/js/pad_connectionstatus.js b/static/js/pad_connectionstatus.js
index 3d244303c..d35eb02d6 100644
--- a/static/js/pad_connectionstatus.js
+++ b/static/js/pad_connectionstatus.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/pad_cookie.js b/static/js/pad_cookie.js
index 5db6a2ca0..64211750d 100644
--- a/static/js/pad_cookie.js
+++ b/static/js/pad_cookie.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/pad_docbar.js b/static/js/pad_docbar.js
index 6ee38f686..c67ca55c4 100644
--- a/static/js/pad_docbar.js
+++ b/static/js/pad_docbar.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/pad_editbar.js b/static/js/pad_editbar.js
index 3818db871..6cd5163de 100644
--- a/static/js/pad_editbar.js
+++ b/static/js/pad_editbar.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
@@ -151,7 +157,7 @@ var padeditbar = (function()
}, cmd, true);
}
}
- padeditor.ace.focus();
+ if(padeditor.ace) padeditor.ace.focus();
},
toogleDropDown: function(moduleName)
{
diff --git a/static/js/pad_editor.js b/static/js/pad_editor.js
index e98252cff..183de948d 100644
--- a/static/js/pad_editor.js
+++ b/static/js/pad_editor.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
@@ -89,6 +95,7 @@ var padeditor = (function()
self.ace.setProperty("showsauthorcolors", noColors);
+ self.ace.setProperty("rtlIsTrue", rtlIsTrue);
},
initViewZoom: function()
{
diff --git a/static/js/pad_impexp.js b/static/js/pad_impexp.js
index 8224fce56..2f519a1ec 100644
--- a/static/js/pad_impexp.js
+++ b/static/js/pad_impexp.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
@@ -230,10 +236,16 @@ var padimpexp = (function()
var self = {
init: function()
{
+ //get /p/padname
+ var pad_root_path = new RegExp(/.*\/p\/[^\/]+/).exec(document.location.pathname)
+ //get http://example.com/p/padname
+ var pad_root_url = document.location.href.replace(document.location.pathname, pad_root_path)
+
// build the export links
- $("#exporthtmla").attr("href", document.location.pathname + "/export/html");
- $("#exportplaina").attr("href", document.location.pathname + "/export/txt");
- $("#exportwordlea").attr("href", document.location.pathname + "/export/wordle");
+ $("#exporthtmla").attr("href", pad_root_path + "/export/html");
+ $("#exportplaina").attr("href", pad_root_path + "/export/txt");
+ $("#exportwordlea").attr("href", pad_root_path + "/export/wordle");
+ $("#exportdokuwikia").attr("href", pad_root_path + "/export/dokuwiki");
//hide stuff thats not avaible if abiword is disabled
if(clientVars.abiwordAvailable == "no")
@@ -241,29 +253,29 @@ var padimpexp = (function()
$("#exportworda").remove();
$("#exportpdfa").remove();
$("#exportopena").remove();
- $("#importexport").css({"height":"95px"});
- $("#importexportline").css({"height":"95px"});
+ $("#importexport").css({"height":"115px"});
+ $("#importexportline").css({"height":"115px"});
$("#import").html("Import is not available");
}
else if(clientVars.abiwordAvailable == "withoutPDF")
{
$("#exportpdfa").remove();
- $("#exportworda").attr("href", document.location.pathname + "/export/doc");
- $("#exportopena").attr("href", document.location.pathname + "/export/odt");
+ $("#exportworda").attr("href", pad_root_path + "/export/doc");
+ $("#exportopena").attr("href", pad_root_path + "/export/odt");
$("#importexport").css({"height":"142px"});
$("#importexportline").css({"height":"142px"});
- $("#importform").get(0).setAttribute('action', document.location.href + "/import");
+ $("#importform").attr('action', pad_root_url + "/import");
}
else
{
- $("#exportworda").attr("href", document.location.pathname + "/export/doc");
- $("#exportpdfa").attr("href", document.location.pathname + "/export/pdf");
- $("#exportopena").attr("href", document.location.pathname + "/export/odt");
+ $("#exportworda").attr("href", pad_root_path + "/export/doc");
+ $("#exportpdfa").attr("href", pad_root_path + "/export/pdf");
+ $("#exportopena").attr("href", pad_root_path + "/export/odt");
- $("#importform").get(0).setAttribute('action', document.location.pathname + "/import");
+ $("#importform").attr('action', pad_root_path + "/import");
}
$("#impexp-close").click(function()
@@ -301,7 +313,7 @@ var padimpexp = (function()
},
export2Wordle: function()
{
- var padUrl = document.location.href + "/export/txt";
+ var padUrl = $('#exportwordlea').attr('href').replace(/\/wordle$/, '/txt')
$.get(padUrl, function(data)
{
diff --git a/static/js/pad_modals.js b/static/js/pad_modals.js
index ccd4607ec..cf9d04355 100644
--- a/static/js/pad_modals.js
+++ b/static/js/pad_modals.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/pad_savedrevs.js b/static/js/pad_savedrevs.js
index ae4220cd9..487e85f1a 100644
--- a/static/js/pad_savedrevs.js
+++ b/static/js/pad_savedrevs.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/pad_userlist.js b/static/js/pad_userlist.js
index 8680fc2e6..748674916 100644
--- a/static/js/pad_userlist.js
+++ b/static/js/pad_userlist.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
@@ -711,7 +717,14 @@ var paduserlist = (function()
}
$("#myswatch").css({'background-color': myUserInfo.colorId});
- $("#usericon").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId});
+
+ if ($.browser.msie && parseInt($.browser.version) <= 8) {
+ $("#usericon").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId,'background-color': myUserInfo.colorId});
+ }
+ else
+ {
+ $("#usericon").css({'box-shadow': 'inset 0 0 30px ' + myUserInfo.colorId});
+ }
}
};
return self;
diff --git a/static/js/pad_utils.js b/static/js/pad_utils.js
index 331590fed..76a167057 100644
--- a/static/js/pad_utils.js
+++ b/static/js/pad_utils.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/plugins.js b/static/js/plugins.js
index 2ebf98f3c..b29d01d4d 100644
--- a/static/js/plugins.js
+++ b/static/js/plugins.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
plugins = {
callHook: function(hookName, args)
{
diff --git a/static/js/skiplist.js b/static/js/skiplist.js
index c21873887..c9654be46 100644
--- a/static/js/skiplist.js
+++ b/static/js/skiplist.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/undo-xpopup.js b/static/js/undo-xpopup.js
index 2fc0ee1a7..e733f3ea3 100644
--- a/static/js/undo-xpopup.js
+++ b/static/js/undo-xpopup.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/undomodule.js b/static/js/undomodule.js
index b070ce253..3891629aa 100644
--- a/static/js/undomodule.js
+++ b/static/js/undomodule.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/js/virtual_lines.js b/static/js/virtual_lines.js
index e27e4af40..ece96b149 100644
--- a/static/js/virtual_lines.js
+++ b/static/js/virtual_lines.js
@@ -1,3 +1,9 @@
+/**
+ * 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.
+ * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
+ */
+
/**
* Copyright 2009 Google Inc.
*
diff --git a/static/pad.html b/static/pad.html
index 7565993a4..8903847fd 100644
--- a/static/pad.html
+++ b/static/pad.html
@@ -4,12 +4,14 @@
Etherpad Lite
+
@@ -23,60 +25,60 @@