Import works now on the server side
parent
4b268f9579
commit
b13fbbfd73
|
@ -0,0 +1,117 @@
|
||||||
|
/**
|
||||||
|
* Handles the import requests
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2011 Peter 'Pita' Martischka
|
||||||
|
*
|
||||||
|
* 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 padManager = require("./PadManager");
|
||||||
|
var padMessageHandler = require("./PadMessageHandler");
|
||||||
|
var async = require("async");
|
||||||
|
var fs = require("fs");
|
||||||
|
var settings = require('./settings');
|
||||||
|
var formidable = require('formidable');
|
||||||
|
|
||||||
|
//load abiword only if its enabled
|
||||||
|
if(settings.abiword != null)
|
||||||
|
var abiword = require("./Abiword");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* do a requested import
|
||||||
|
*/
|
||||||
|
exports.doImport = function(req, res, padId)
|
||||||
|
{
|
||||||
|
//pipe to a file
|
||||||
|
//convert file to text via abiword
|
||||||
|
//set text in the pad
|
||||||
|
|
||||||
|
var srcFile, destFile;
|
||||||
|
var pad;
|
||||||
|
var text;
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//save the uploaded file to /tmp
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
var form = new formidable.IncomingForm();
|
||||||
|
form.keepExtensions = true;
|
||||||
|
|
||||||
|
form.parse(req, function(err, fields, files)
|
||||||
|
{
|
||||||
|
//save the path of the uploaded file
|
||||||
|
srcFile = files.file.path;
|
||||||
|
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
//convert file to text
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
var randNum = Math.floor(Math.random()*new Date().getTime());
|
||||||
|
destFile = "/tmp/eplite_import_" + randNum + ".txt";
|
||||||
|
abiword.convertFile(srcFile, destFile, "txt", callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
//get the pad object
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
padManager.getPad(padId, function(err, _pad)
|
||||||
|
{
|
||||||
|
pad = _pad;
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
//read the text
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
fs.readFile(destFile, "utf8", function(err, _text)
|
||||||
|
{
|
||||||
|
text = _text;
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
//change text of the pad and broadcast the changeset
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
pad.setText(text);
|
||||||
|
padMessageHandler.updatePadClients(pad, callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
//clean up temporary files
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
async.parallel([
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
fs.unlink(srcFile, callback);
|
||||||
|
},
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
fs.unlink(destFile, callback);
|
||||||
|
}
|
||||||
|
], callback);
|
||||||
|
}
|
||||||
|
], function(err)
|
||||||
|
{
|
||||||
|
//close the connection
|
||||||
|
res.send("ok");
|
||||||
|
|
||||||
|
if(err) throw err;
|
||||||
|
});
|
||||||
|
}
|
|
@ -191,6 +191,20 @@ Class('Pad', {
|
||||||
return this.atext.text;
|
return this.atext.text;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setText : function(newText)
|
||||||
|
{
|
||||||
|
//clean the new text
|
||||||
|
newText = exports.cleanText(newText);
|
||||||
|
|
||||||
|
var oldText = this.text();
|
||||||
|
|
||||||
|
//create the changeset
|
||||||
|
var changeset = Changeset.makeSplice(oldText, 0, oldText.length-1, newText);
|
||||||
|
|
||||||
|
//append the changeset
|
||||||
|
this.appendRevision(changeset);
|
||||||
|
},
|
||||||
|
|
||||||
appendChatMessage: function(text, userId, time)
|
appendChatMessage: function(text, userId, time)
|
||||||
{
|
{
|
||||||
this.chatHead++;
|
this.chatHead++;
|
||||||
|
|
|
@ -433,71 +433,7 @@ function handleUserChanges(client, message)
|
||||||
pad.appendRevision(nlChangeset);
|
pad.appendRevision(nlChangeset);
|
||||||
}
|
}
|
||||||
|
|
||||||
//ex. updatePadClients
|
exports.updatePadClients(pad, callback);
|
||||||
|
|
||||||
//go trough all sessions on this pad
|
|
||||||
async.forEach(pad2sessions[pad.id], function(session, callback)
|
|
||||||
{
|
|
||||||
var lastRev = sessioninfos[session].rev;
|
|
||||||
|
|
||||||
//https://github.com/caolan/async#whilst
|
|
||||||
//send them all new changesets
|
|
||||||
async.whilst(
|
|
||||||
function (){ return lastRev < pad.getHeadRevisionNumber()},
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
var author, revChangeset;
|
|
||||||
|
|
||||||
var r = ++lastRev;
|
|
||||||
|
|
||||||
async.parallel([
|
|
||||||
function (callback)
|
|
||||||
{
|
|
||||||
pad.getRevisionAuthor(r, function(err, value)
|
|
||||||
{
|
|
||||||
author = value;
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function (callback)
|
|
||||||
{
|
|
||||||
pad.getRevisionChangeset(r, function(err, value)
|
|
||||||
{
|
|
||||||
revChangeset = value;
|
|
||||||
callback(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], function(err)
|
|
||||||
{
|
|
||||||
if(err)
|
|
||||||
{
|
|
||||||
callback(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(author == sessioninfos[session].author)
|
|
||||||
{
|
|
||||||
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var forWire = Changeset.prepareForWire(revChangeset, pad.pool);
|
|
||||||
var wireMsg = {"type":"COLLABROOM","data":{type:"NEW_CHANGES", newRev:r,
|
|
||||||
changeset: forWire.translated,
|
|
||||||
apool: forWire.pool,
|
|
||||||
author: author}};
|
|
||||||
|
|
||||||
socketio.sockets.sockets[session].json.send(wireMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
callback
|
|
||||||
);
|
|
||||||
|
|
||||||
sessioninfos[session].rev = pad.getHeadRevisionNumber();
|
|
||||||
},callback);
|
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
|
@ -505,6 +441,73 @@ function handleUserChanges(client, message)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.updatePadClients = function(pad, callback)
|
||||||
|
{
|
||||||
|
//go trough all sessions on this pad
|
||||||
|
async.forEach(pad2sessions[pad.id], function(session, callback)
|
||||||
|
{
|
||||||
|
var lastRev = sessioninfos[session].rev;
|
||||||
|
|
||||||
|
//https://github.com/caolan/async#whilst
|
||||||
|
//send them all new changesets
|
||||||
|
async.whilst(
|
||||||
|
function (){ return lastRev < pad.getHeadRevisionNumber()},
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
var author, revChangeset;
|
||||||
|
|
||||||
|
var r = ++lastRev;
|
||||||
|
|
||||||
|
async.parallel([
|
||||||
|
function (callback)
|
||||||
|
{
|
||||||
|
pad.getRevisionAuthor(r, function(err, value)
|
||||||
|
{
|
||||||
|
author = value;
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function (callback)
|
||||||
|
{
|
||||||
|
pad.getRevisionChangeset(r, function(err, value)
|
||||||
|
{
|
||||||
|
revChangeset = value;
|
||||||
|
callback(err);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err)
|
||||||
|
{
|
||||||
|
if(err)
|
||||||
|
{
|
||||||
|
callback(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(author == sessioninfos[session].author)
|
||||||
|
{
|
||||||
|
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var forWire = Changeset.prepareForWire(revChangeset, pad.pool);
|
||||||
|
var wireMsg = {"type":"COLLABROOM","data":{type:"NEW_CHANGES", newRev:r,
|
||||||
|
changeset: forWire.translated,
|
||||||
|
apool: forWire.pool,
|
||||||
|
author: author}};
|
||||||
|
|
||||||
|
socketio.sockets.sockets[session].json.send(wireMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
callback
|
||||||
|
);
|
||||||
|
|
||||||
|
sessioninfos[session].rev = pad.getHeadRevisionNumber();
|
||||||
|
},callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copied from the Etherpad Source Code. Don't know what this methode does excatly...
|
* Copied from the Etherpad Source Code. Don't know what this methode does excatly...
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -32,6 +32,7 @@ var express = require('express');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var minify = require('./minify');
|
var minify = require('./minify');
|
||||||
var exportHandler;
|
var exportHandler;
|
||||||
|
var importHandler;
|
||||||
var exporthtml;
|
var exporthtml;
|
||||||
var readOnlyManager;
|
var readOnlyManager;
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ catch(e)
|
||||||
|
|
||||||
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
||||||
|
|
||||||
//cache a week
|
//cache 6 hours
|
||||||
exports.maxAge = 1000*60*60*6;
|
exports.maxAge = 1000*60*60*6;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
@ -70,6 +71,7 @@ async.waterfall([
|
||||||
readOnlyManager = require("./ReadOnlyManager");
|
readOnlyManager = require("./ReadOnlyManager");
|
||||||
exporthtml = require("./exporters/exporthtml");
|
exporthtml = require("./exporters/exporthtml");
|
||||||
exportHandler = require('./ExportHandler');
|
exportHandler = require('./ExportHandler');
|
||||||
|
importHandler = require('./ImportHandler');
|
||||||
|
|
||||||
//set logging
|
//set logging
|
||||||
if(settings.logHTTP)
|
if(settings.logHTTP)
|
||||||
|
@ -215,6 +217,20 @@ async.waterfall([
|
||||||
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//handle import requests
|
||||||
|
app.post('/p/:pad/import', function(req, res, next)
|
||||||
|
{
|
||||||
|
//if abiword is disabled, skip handling this request
|
||||||
|
if(settings.abiword == null)
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.header("Server", serverName);
|
||||||
|
importHandler.doImport(req, res, req.params.pad);
|
||||||
|
});
|
||||||
|
|
||||||
//serve index.html under /
|
//serve index.html under /
|
||||||
app.get('/', function(req, res)
|
app.get('/', function(req, res)
|
||||||
{
|
{
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
"keywords" : ["etherpad", "realtime", "collaborative", "editor"],
|
||||||
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com>",
|
"author" : "Peter 'Pita' Martischka <petermartischka@googlemail.com>",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
{ "name": "Hans Pinckaers"}
|
{ "name": "John McLear",
|
||||||
|
"name": "Hans Pinckaers"}
|
||||||
],
|
],
|
||||||
"dependencies" : {
|
"dependencies" : {
|
||||||
"socket.io" : "0.7.7",
|
"socket.io" : "0.7.7",
|
||||||
|
@ -15,7 +16,8 @@
|
||||||
"express" : "2.4.2",
|
"express" : "2.4.2",
|
||||||
"clean-css" : "0.2.4",
|
"clean-css" : "0.2.4",
|
||||||
"uglify-js" : "1.0.4",
|
"uglify-js" : "1.0.4",
|
||||||
"gzip" : "0.1.0"
|
"gzip" : "0.1.0",
|
||||||
|
"formidable" : "1.0.2"
|
||||||
},
|
},
|
||||||
"version" : "0.0.4"
|
"version" : "0.0.4"
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,9 +24,9 @@ var padimpexp = (function()
|
||||||
|
|
||||||
function addImportFrames()
|
function addImportFrames()
|
||||||
{
|
{
|
||||||
$("#impexp-import .importframe").remove();
|
$("#import .importframe").remove();
|
||||||
$('#impexp-import').append(
|
var iframe = $('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>');
|
||||||
$('<iframe style="display: none;" name="importiframe" class="importframe"></iframe>'));
|
$('#import').append(iframe);
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileInputUpdated()
|
function fileInputUpdated()
|
||||||
|
@ -64,7 +64,7 @@ var padimpexp = (function()
|
||||||
$('#importmessagefail').fadeOut("fast");
|
$('#importmessagefail').fadeOut("fast");
|
||||||
var ret = window.confirm("Importing a file will overwrite the current text of the pad." + " Are you sure you want to proceed?");
|
var ret = window.confirm("Importing a file will overwrite the current text of the pad." + " Are you sure you want to proceed?");
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
|
hidePanelCall = paddocbar.hideLaterIfNoOtherInteraction();
|
||||||
currentImportTimer = window.setTimeout(function()
|
currentImportTimer = window.setTimeout(function()
|
||||||
{
|
{
|
||||||
|
@ -88,6 +88,11 @@ var padimpexp = (function()
|
||||||
}, 0);
|
}, 0);
|
||||||
$('#importarrow').stop(true, true).hide();
|
$('#importarrow').stop(true, true).hide();
|
||||||
$('#importstatusball').show();
|
$('#importstatusball').show();
|
||||||
|
|
||||||
|
$("#import .importframe").load(function()
|
||||||
|
{
|
||||||
|
importDone();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -225,6 +230,8 @@ var padimpexp = (function()
|
||||||
var self = {
|
var self = {
|
||||||
init: function()
|
init: function()
|
||||||
{
|
{
|
||||||
|
$("#importform").get(0).setAttribute('action', document.location.href + "/import");
|
||||||
|
|
||||||
$("#impexp-close").click(function()
|
$("#impexp-close").click(function()
|
||||||
{
|
{
|
||||||
paddocbar.setShownPanel(null);
|
paddocbar.setShownPanel(null);
|
||||||
|
@ -249,13 +256,13 @@ var padimpexp = (function()
|
||||||
disable: function()
|
disable: function()
|
||||||
{
|
{
|
||||||
$("#impexp-disabled-clickcatcher").show();
|
$("#impexp-disabled-clickcatcher").show();
|
||||||
$("#impexp-import").css('opacity', 0.5);
|
$("#import").css('opacity', 0.5);
|
||||||
$("#impexp-export").css('opacity', 0.5);
|
$("#impexp-export").css('opacity', 0.5);
|
||||||
},
|
},
|
||||||
enable: function()
|
enable: function()
|
||||||
{
|
{
|
||||||
$("#impexp-disabled-clickcatcher").hide();
|
$("#impexp-disabled-clickcatcher").hide();
|
||||||
$("#impexp-import").css('opacity', 1);
|
$("#import").css('opacity', 1);
|
||||||
$("#impexp-export").css('opacity', 1);
|
$("#impexp-export").css('opacity', 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -208,7 +208,7 @@ We removed this feature cause its not worth the space it needs in the editbar
|
||||||
|
|
||||||
<div id="import">
|
<div id="import">
|
||||||
Import from text file, HTML, Word, or RTF:<br/><br/>
|
Import from text file, HTML, Word, or RTF:<br/><br/>
|
||||||
<form id="importform" method="post" action="import" target="imporiframe" enctype="multipart/form-data">
|
<form id="importform" method="post" action="" target="importiframe" enctype="multipart/form-data">
|
||||||
<div class="importformdiv" id="importformfilediv">
|
<div class="importformdiv" id="importformfilediv">
|
||||||
<input type="file" name="file" size="20" id="importfileinput" />
|
<input type="file" name="file" size="20" id="importfileinput" />
|
||||||
<div class="importmessage" id="importmessagefail"></div>
|
<div class="importmessage" id="importmessagefail"></div>
|
||||||
|
@ -222,7 +222,7 @@ We removed this feature cause its not worth the space it needs in the editbar
|
||||||
<img alt="" id="importarrow" src="/static/img/leftarrow.png" align="top" />
|
<img alt="" id="importarrow" src="/static/img/leftarrow.png" align="top" />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="importexportline"></div>
|
<div id="importexportline"></div>
|
||||||
|
|
Loading…
Reference in New Issue