diff --git a/src/node/db/API.js b/src/node/db/API.js index 5bb83f029..e48c14015 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -440,8 +440,8 @@ exports.getChatHistory = function(padID, start, end, callback) // fall back to getting the whole chat-history if a parameter is missing if(!start || !end) { - start = 0; - end = pad.chatHead; + start = 0; + end = pad.chatHead; } if(start >= chatHead && chatHead > 0) @@ -552,6 +552,46 @@ exports.deletePad = function(padID, callback) }); } +/** +copyPad(sourceID, destinationID[, force=false]) copies a pad. If force is true, + the destination will be overwritten if it exists. + +Example returns: + +{code: 0, message:"ok", data: {padID: destinationID}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.copyPad = function(sourceID, destinationID, force, callback) +{ + getPadSafe(sourceID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + pad.copy(destinationID, force, callback); + }); +} + +/** +movePad(sourceID, destinationID[, force=false]) moves a pad. If force is true, + the destination will be overwritten if it exists. + +Example returns: + +{code: 0, message:"ok", data: {padID: destinationID}} +{code: 1, message:"padID does not exist", data: null} +*/ +exports.movePad = function(sourceID, destinationID, force, callback) +{ + getPadSafe(sourceID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + pad.copy(destinationID, force, function(err) { + if(ERR(err, callback)) return; + pad.remove(callback); + }); + }); +} /** getReadOnlyLink(padID) returns the read only link of a pad diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index 4701e82a3..b6dee897f 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -13,6 +13,8 @@ var settings = require('../utils/Settings'); var authorManager = require("./AuthorManager"); var padManager = require("./PadManager"); var padMessageHandler = require("../handler/PadMessageHandler"); +var groupManager = require("./GroupManager"); +var customError = require("../utils/customError"); var readOnlyManager = require("./ReadOnlyManager"); var crypto = require("crypto"); var randomString = require("../utils/randomstring"); @@ -404,6 +406,152 @@ Pad.prototype.init = function init(text, callback) { }); }; +Pad.prototype.copy = function copy(destinationID, force, callback) { + var sourceID = this.id; + var _this = this; + + // make force optional + if (typeof force == "function") { + callback = force; + force = false; + } + else if (force == undefined || force.toLowerCase() != "true") { + force = false; + } + else force = true; + + //kick everyone from this pad + // TODO: this presents a message on the client saying that the pad was 'deleted'. Fix this? + padMessageHandler.kickSessionsFromPad(sourceID); + + // flush the source pad: + _this.saveToDatabase(); + + async.series([ + // if it's a group pad, let's make sure the group exists. + function(callback) + { + if (destinationID.indexOf("$") != -1) + { + groupManager.doesGroupExist(destinationID.split("$")[0], function (err, exists) + { + if(ERR(err, callback)) return; + + //group does not exist + if(exists == false) + { + callback(new customError("groupID does not exist for destinationID","apierror")); + return; + } + //everything is fine, continue + else + { + callback(); + } + }); + } + else + callback(); + }, + // if the pad exists, we should abort, unless forced. + function(callback) + { + console.log("destinationID", destinationID, force); + padManager.doesPadExists(destinationID, function (err, exists) + { + if(ERR(err, callback)) return; + + if(exists == true) + { + if (!force) + { + console.log("erroring out without force"); + callback(new customError("destinationID already exists","apierror")); + console.log("erroring out without force - after"); + return; + } + else // exists and forcing + { + padManager.getPad(destinationID, function(err, pad) { + if (ERR(err, callback)) return; + pad.remove(callback); + }); + } + } + else + { + callback(); + } + }); + }, + // copy the 'pad' entry + function(callback) + { + db.get("pad:"+sourceID, function(err, pad) { + db.set("pad:"+destinationID, pad); + }); + callback(); + }, + //copy all relations + function(callback) + { + async.parallel([ + //copy all chat messages + function(callback) + { + var chatHead = _this.chatHead; + + for(var i=0;i<=chatHead;i++) + { + db.get("pad:"+sourceID+":chat:"+i, function (err, chat) { + if (ERR(err, callback)) return; + db.set("pad:"+destinationID+":chat:"+i, chat); + }); + } + + callback(); + }, + //copy all revisions + function(callback) + { + var revHead = _this.head; + //console.log(revHead); + for(var i=0;i<=revHead;i++) + { + db.get("pad:"+sourceID+":revs:"+i, function (err, rev) { + //console.log("HERE"); + + if (ERR(err, callback)) return; + db.set("pad:"+destinationID+":revs:"+i, rev); + }); + } + + callback(); + }, + //add the new pad to all authors who contributed to the old one + function(callback) + { + var authorIDs = _this.getAllAuthors(); + + authorIDs.forEach(function (authorID) + { + console.log("authors"); + authorManager.addPad(authorID, destinationID); + }); + + callback(); + }, + // parallel + ], callback); + }, + // series + ], function(err) + { + if(ERR(err, callback)) return; + callback(null, {padID: destinationID}); + }); +}; + Pad.prototype.remove = function remove(callback) { var padID = this.id; var _this = this; diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js index cba13f9b7..f2746b067 100644 --- a/src/node/handler/APIHandler.js +++ b/src/node/handler/APIHandler.js @@ -240,6 +240,8 @@ var version = , "getRevisionChangeset" : ["padID", "rev"] , "getLastEdited" : ["padID"] , "deletePad" : ["padID"] + , "copyPad" : ["sourceID", "destinationID", "force"] + , "movePad" : ["sourceID", "destinationID", "force"] , "getReadOnlyID" : ["padID"] , "setPublicStatus" : ["padID", "publicStatus"] , "getPublicStatus" : ["padID"]