mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-10 08:55:00 -04:00
Merge 0250f8d55c
into e02054d6fe
This commit is contained in:
commit
7578dad7bd
12 changed files with 101 additions and 115 deletions
|
@ -3,7 +3,7 @@ This load tester is extremely useful for testing how many dormant clients can co
|
||||||
TODO:
|
TODO:
|
||||||
Emulate characters being typed into a pad
|
Emulate characters being typed into a pad
|
||||||
|
|
||||||
HOW TO USE (from @mjd75) proper formatting at: https://github.com/Pita/etherpad-lite/issues/360
|
HOW TO USE (from @mjd75) proper formatting at: https://github.com/ether/etherpad-lite/issues/360
|
||||||
|
|
||||||
Server 1:
|
Server 1:
|
||||||
Installed Node.js (etc), EtherPad Lite and MySQL
|
Installed Node.js (etc), EtherPad Lite and MySQL
|
||||||
|
|
|
@ -9,7 +9,7 @@ The API gives another web application control of the pads. The basic functions a
|
||||||
|
|
||||||
The API is designed in a way, so you can reuse your existing user system with their permissions, and map it to etherpad lite. Means: Your web application still has to do authentication, but you can tell etherpad lite via the api, which visitors should get which permissions. This allows etherpad lite to fit into any web application and extend it with real-time functionality. You can embed the pads via an iframe into your website.
|
The API is designed in a way, so you can reuse your existing user system with their permissions, and map it to etherpad lite. Means: Your web application still has to do authentication, but you can tell etherpad lite via the api, which visitors should get which permissions. This allows etherpad lite to fit into any web application and extend it with real-time functionality. You can embed the pads via an iframe into your website.
|
||||||
|
|
||||||
Take a look at [HTTP API client libraries](https://github.com/Pita/etherpad-lite/wiki/HTTP-API-client-libraries) to see if a library in your favorite language.
|
Take a look at [HTTP API client libraries](https://github.com/ether/etherpad-lite/wiki/HTTP-API-client-libraries) to see if a library in your favorite language.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
|
|
@ -18,59 +18,48 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace")
|
||||||
var padManager = require("../db/PadManager");
|
, padManager = require("../db/PadManager")
|
||||||
var padMessageHandler = require("./PadMessageHandler");
|
, padMessageHandler = require("./PadMessageHandler")
|
||||||
var async = require("async");
|
, async = require("async")
|
||||||
var fs = require("fs");
|
, fs = require("fs")
|
||||||
var settings = require('../utils/Settings');
|
, path = require("path")
|
||||||
var formidable = require('formidable');
|
, settings = require('../utils/Settings')
|
||||||
var os = require("os");
|
, formidable = require('formidable')
|
||||||
|
, os = require("os")
|
||||||
|
, importHtml = require("../utils/ImportHtml");
|
||||||
|
|
||||||
//load abiword only if its enabled
|
//load abiword only if its enabled
|
||||||
if(settings.abiword != null)
|
if(settings.abiword != null)
|
||||||
var abiword = require("../utils/Abiword");
|
var abiword = require("../utils/Abiword");
|
||||||
|
|
||||||
var tempDirectory = "/tmp/";
|
|
||||||
|
|
||||||
//tempDirectory changes if the operating system is windows
|
|
||||||
if(os.type().indexOf("Windows") > -1)
|
|
||||||
{
|
|
||||||
tempDirectory = process.env.TEMP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* do a requested import
|
* do a requested import
|
||||||
*/
|
*/
|
||||||
exports.doImport = function(req, res, padId)
|
exports.doImport = function(req, res, padId)
|
||||||
{
|
{
|
||||||
//pipe to a file
|
//pipe to a file
|
||||||
//convert file to text via abiword
|
//convert file to html via abiword
|
||||||
//set text in the pad
|
//set html in the pad
|
||||||
|
|
||||||
var srcFile, destFile;
|
var srcFile, destFile
|
||||||
var pad;
|
, pad
|
||||||
var text;
|
, text;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//save the uploaded file to /tmp
|
//save the uploaded file to /tmp
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
|
||||||
var form = new formidable.IncomingForm();
|
var form = new formidable.IncomingForm();
|
||||||
form.keepExtensions = true;
|
form.keepExtensions = true;
|
||||||
form.uploadDir = tempDirectory;
|
|
||||||
|
|
||||||
form.parse(req, function(err, fields, files)
|
form.parse(req, function(err, fields, files) {
|
||||||
{
|
|
||||||
//the upload failed, stop at this point
|
//the upload failed, stop at this point
|
||||||
if(err || files.file === undefined)
|
if(err || files.file === undefined) {
|
||||||
{
|
|
||||||
console.warn("Uploading Error: " + err.stack);
|
console.warn("Uploading Error: " + err.stack);
|
||||||
callback("uploadFailed");
|
callback("uploadFailed");
|
||||||
}
|
}
|
||||||
//everything ok, continue
|
//everything ok, continue
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
//save the path of the uploaded file
|
//save the path of the uploaded file
|
||||||
srcFile = files.file.path;
|
srcFile = files.file.path;
|
||||||
callback();
|
callback();
|
||||||
|
@ -80,57 +69,48 @@ exports.doImport = function(req, res, padId)
|
||||||
|
|
||||||
//ensure this is a file ending we know, else we change the file ending to .txt
|
//ensure this is a file ending we know, else we change the file ending to .txt
|
||||||
//this allows us to accept source code files like .c or .java
|
//this allows us to accept source code files like .c or .java
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
var fileEnding = path.extname(srcFile).toLowerCase()
|
||||||
var fileEnding = (srcFile.split(".")[1] || "").toLowerCase();
|
, knownFileEndings = [".txt", ".doc", ".docx", ".pdf", ".odt", ".html", ".htm"]
|
||||||
var knownFileEndings = ["txt", "doc", "docx", "pdf", "odt", "html", "htm"];
|
, fileEndingKnown = (knownFileEndings.indexOf(fileEnding) > -1);
|
||||||
|
|
||||||
//find out if this is a known file ending
|
|
||||||
var fileEndingKnown = false;
|
|
||||||
for(var i in knownFileEndings)
|
|
||||||
{
|
|
||||||
if(fileEnding == knownFileEndings[i])
|
|
||||||
{
|
|
||||||
fileEndingKnown = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//if the file ending is known, continue as normal
|
//if the file ending is known, continue as normal
|
||||||
if(fileEndingKnown)
|
if(fileEndingKnown) {
|
||||||
{
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
//we need to rename this file with a .txt ending
|
//we need to rename this file with a .txt ending
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
var oldSrcFile = srcFile;
|
var oldSrcFile = srcFile;
|
||||||
srcFile = srcFile.split(".")[0] + ".txt";
|
srcFile = path.join(path.dirname(srcFile),path.basename(srcFile, fileEnding)+".txt");
|
||||||
|
|
||||||
fs.rename(oldSrcFile, srcFile, callback);
|
fs.rename(oldSrcFile, srcFile, callback);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
//convert file to text
|
//convert file to html
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
|
||||||
var randNum = Math.floor(Math.random()*0xFFFFFFFF);
|
var randNum = Math.floor(Math.random()*0xFFFFFFFF);
|
||||||
destFile = tempDirectory + "eplite_import_" + randNum + ".txt";
|
destFile = path.join(os.tmpDir(), "eplite_import_" + randNum + ".htm");
|
||||||
abiword.convertFile(srcFile, destFile, "txt", function(err){
|
|
||||||
//catch convert errors
|
if (abiword) {
|
||||||
if(err){
|
abiword.convertFile(srcFile, destFile, "htm", function(err) {
|
||||||
console.warn("Converting Error:", err);
|
//catch convert errors
|
||||||
return callback("convertFailed");
|
if(err) {
|
||||||
} else {
|
console.warn("Converting Error:", err);
|
||||||
callback();
|
return callback("convertFailed");
|
||||||
}
|
} else {
|
||||||
});
|
callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// if no abiword only rename
|
||||||
|
fs.rename(srcFile, destFile, callback);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
//get the pad object
|
//get the pad object
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
padManager.getPad(padId, function(err, _pad){
|
||||||
padManager.getPad(padId, function(err, _pad)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
callback();
|
callback();
|
||||||
|
@ -138,52 +118,47 @@ exports.doImport = function(req, res, padId)
|
||||||
},
|
},
|
||||||
|
|
||||||
//read the text
|
//read the text
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
fs.readFile(destFile, "utf8", function(err, _text){
|
||||||
fs.readFile(destFile, "utf8", function(err, _text)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
text = _text;
|
text = _text;
|
||||||
|
|
||||||
//node on windows has a delay on releasing of the file lock.
|
//node on windows has a delay on releasing of the file lock.
|
||||||
//We add a 100ms delay to work around this
|
//We add a 100ms delay to work around this
|
||||||
if(os.type().indexOf("Windows") > -1)
|
if(os.type().indexOf("Windows") > -1){
|
||||||
{
|
setTimeout(function() {callback();}, 100);
|
||||||
setTimeout(function()
|
} else {
|
||||||
{
|
callback();
|
||||||
callback();
|
}
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
//change text of the pad and broadcast the changeset
|
//change text of the pad and broadcast the changeset
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
var fileEnding = path.extname(srcFile).toLowerCase();
|
||||||
pad.setText(text);
|
if (abiword || fileEnding == ".htm" || fileEnding == ".html") {
|
||||||
|
importHtml.setPadHTML(pad, text);
|
||||||
|
} else {
|
||||||
|
pad.setText(text);
|
||||||
|
}
|
||||||
padMessageHandler.updatePadClients(pad, callback);
|
padMessageHandler.updatePadClients(pad, callback);
|
||||||
},
|
},
|
||||||
|
|
||||||
//clean up temporary files
|
//clean up temporary files
|
||||||
function(callback)
|
function(callback) {
|
||||||
{
|
//for node < 0.7 compatible
|
||||||
|
var fileExists = fs.exists || path.exists;
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(callback)
|
function(callback){
|
||||||
{
|
fileExists (srcFile, function(exist) { (exist)? fs.unlink(srcFile, callback): callback(); });
|
||||||
fs.unlink(srcFile, callback);
|
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback){
|
||||||
{
|
fileExists (destFile, function(exist) { (exist)? fs.unlink(destFile, callback): callback(); });
|
||||||
fs.unlink(destFile, callback);
|
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err) {
|
||||||
{
|
|
||||||
var status = "ok";
|
var status = "ok";
|
||||||
|
|
||||||
//check for known errors and replace the status
|
//check for known errors and replace the status
|
||||||
|
|
|
@ -26,7 +26,7 @@ exports.createServer = function () {
|
||||||
{
|
{
|
||||||
console.warn("Can't get git version for server header\n" + e.message)
|
console.warn("Can't get git version for server header\n" + e.message)
|
||||||
}
|
}
|
||||||
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues")
|
console.log("Report bugs at https://github.com/ether/etherpad-lite/issues")
|
||||||
|
|
||||||
serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
||||||
|
|
||||||
|
|
|
@ -28,12 +28,6 @@ exports.expressCreateServer = function (hook_name, args, cb) {
|
||||||
|
|
||||||
//handle import requests
|
//handle import requests
|
||||||
args.app.post('/p/:pad/import', function(req, res, next) {
|
args.app.post('/p/:pad/import', function(req, res, next) {
|
||||||
//if abiword is disabled, skip handling this request
|
|
||||||
if(settings.abiword == null) {
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function() {
|
hasPadAccess(req, res, function() {
|
||||||
importHandler.doImport(req, res, req.params.pad);
|
importHandler.doImport(req, res, req.params.pad);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name" : "ep_etherpad-lite",
|
"name" : "ep_etherpad-lite",
|
||||||
"description" : "A Etherpad based on node.js",
|
"description" : "A Etherpad based on node.js",
|
||||||
"homepage" : "https://github.com/Pita/etherpad-lite",
|
"homepage" : "https://github.com/ether/etherpad-lite",
|
||||||
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
||||||
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com> - Primary Technology Ltd",
|
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com> - Primary Technology Ltd",
|
||||||
"contributors" : [
|
"contributors" : [
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* 90% of the code is still like in the original Etherpad
|
* 90% of the code is still like in the original Etherpad
|
||||||
* Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
* Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
|
||||||
* You can find a explanation what a attribute pool is here:
|
* You can find a explanation what a attribute pool is here:
|
||||||
* https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
|
* https://github.com/ether/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -3266,7 +3266,7 @@ function Ace2Inner(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//hide the dropdownso
|
//hide the dropdownso
|
||||||
if(window.parent.parent.padeditbar){ // required in case its in an iframe should probably use parent.. See Issue 327 https://github.com/Pita/etherpad-lite/issues/327
|
if(window.parent.parent.padeditbar){ // required in case its in an iframe should probably use parent.. See Issue 327 https://github.com/ether/etherpad-lite/issues/327
|
||||||
window.parent.parent.padeditbar.toggleDropDown("none");
|
window.parent.parent.padeditbar.toggleDropDown("none");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -310,7 +310,7 @@ function handshake()
|
||||||
}
|
}
|
||||||
else if(obj.accessStatus == "wrongPassword")
|
else if(obj.accessStatus == "wrongPassword")
|
||||||
{
|
{
|
||||||
$("#editorloadingbox").html("<b>You're password was wrong</b><br>" +
|
$("#editorloadingbox").html("<b>Your password was wrong</b><br>" +
|
||||||
"<input id='passwordinput' type='password' name='password'>"+
|
"<input id='passwordinput' type='password' name='password'>"+
|
||||||
"<button type='button' onclick=\"" + padutils.escapeHtml('require('+JSON.stringify(module.id)+").savePassword()") + "\">ok</button>");
|
"<button type='button' onclick=\"" + padutils.escapeHtml('require('+JSON.stringify(module.id)+").savePassword()") + "\">ok</button>");
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,8 +225,8 @@ var padimpexp = (function()
|
||||||
$("#exportworda").remove();
|
$("#exportworda").remove();
|
||||||
$("#exportpdfa").remove();
|
$("#exportpdfa").remove();
|
||||||
$("#exportopena").remove();
|
$("#exportopena").remove();
|
||||||
$(".importformdiv").remove();
|
|
||||||
$("#import").html("Import is not available. To enable import please install abiword");
|
$("#importform").attr('action', pad_root_url + "/import");
|
||||||
}
|
}
|
||||||
else if(clientVars.abiwordAvailable == "withoutPDF")
|
else if(clientVars.abiwordAvailable == "withoutPDF")
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,12 +15,15 @@ body {
|
||||||
#iframe-container {
|
#iframe-container {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
float:right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#iframe-container iframe {
|
#iframe-container iframe {
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
position:absolute;
|
||||||
|
min-width:500px;
|
||||||
|
max-width:800px;
|
||||||
|
left:50%;
|
||||||
|
width:100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#mocha {
|
#mocha {
|
||||||
|
|
|
@ -1,10 +1,19 @@
|
||||||
|
function deletecookie(name) {
|
||||||
|
document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;';
|
||||||
|
}
|
||||||
|
|
||||||
describe("Language select and change", function(){
|
describe("Language select and change", function(){
|
||||||
|
// Destroy language cookies
|
||||||
|
deletecookie("language", null);
|
||||||
|
|
||||||
//create a new pad before each test run
|
//create a new pad before each test run
|
||||||
beforeEach(function(cb){
|
beforeEach(function(cb){
|
||||||
helper.newPad(cb);
|
helper.newPad(cb);
|
||||||
this.timeout(60000);
|
this.timeout(60000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Destroy language cookies
|
||||||
|
|
||||||
it("makes text german", function(done) {
|
it("makes text german", function(done) {
|
||||||
var inner$ = helper.padInner$;
|
var inner$ = helper.padInner$;
|
||||||
var chrome$ = helper.padChrome$;
|
var chrome$ = helper.padChrome$;
|
||||||
|
@ -21,7 +30,9 @@ describe("Language select and change", function(){
|
||||||
$languageoption.attr('selected','selected');
|
$languageoption.attr('selected','selected');
|
||||||
$language.change();
|
$language.change();
|
||||||
|
|
||||||
helper.waitFor(function() { return $language.val() == "de"})
|
helper.waitFor(function() {
|
||||||
|
return chrome$(".buttonicon-bold").parent()[0]["title"] = "Fett (Strg-B)";
|
||||||
|
})
|
||||||
.done(function(){
|
.done(function(){
|
||||||
//get the value of the bold button
|
//get the value of the bold button
|
||||||
var $boldButton = chrome$(".buttonicon-bold").parent();
|
var $boldButton = chrome$(".buttonicon-bold").parent();
|
||||||
|
@ -51,7 +62,10 @@ describe("Language select and change", function(){
|
||||||
$languageoption.attr('selected','selected');
|
$languageoption.attr('selected','selected');
|
||||||
$language.change();
|
$language.change();
|
||||||
|
|
||||||
helper.waitFor(function() { return $language.val() == "en";})
|
//get the value of the bold button
|
||||||
|
var $boldButton = chrome$(".buttonicon-bold").parent();
|
||||||
|
|
||||||
|
helper.waitFor(function() { return $boldButton[0]["title"] != "Fett (Strg-B)";})
|
||||||
.done(function(){
|
.done(function(){
|
||||||
|
|
||||||
//get the value of the bold button
|
//get the value of the bold button
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue