diff options
author | Cristo <cristo.rabani@gmail.com> | 2014-11-08 01:12:40 +0100 |
---|---|---|
committer | Cristo <cristo.rabani@gmail.com> | 2014-11-08 01:12:40 +0100 |
commit | d246a191c6cc9a6e2d84451058e2252f7c552095 (patch) | |
tree | 8bfab7ad729818f0bf2497b426d03adf8d2573ef /src/node/db | |
parent | e1fe1f0f9cf45b193c49dd159cb15124bcd8e743 (diff) | |
download | etherpad-lite-d246a191c6cc9a6e2d84451058e2252f7c552095.zip |
Added option to restore revisions #1791
Diffstat (limited to 'src/node/db')
-rw-r--r-- | src/node/db/API.js | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/src/node/db/API.js b/src/node/db/API.js index 4a912368..2aadc483 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -576,6 +576,104 @@ exports.deletePad = function(padID, callback) }); } +exports.restoreRevision = function(padID, rev, callback) +{ + var Changeset = require("ep_etherpad-lite/static/js/Changeset"); + + //check if rev is a number + if(rev !== undefined && typeof rev != "number") + { + //try to parse the number + if(!isNaN(parseInt(rev))) + { + rev = parseInt(rev); + } + else + { + callback(new customError("rev is not a number", "apierror")); + return; + } + } + + //ensure this is not a negativ number + if(rev !== undefined && rev < 0) + { + callback(new customError("rev is a negativ number","apierror")); + return; + } + + //ensure this is not a float value + if(rev !== undefined && !is_int(rev)) + { + callback(new customError("rev is a float value","apierror")); + return; + } + + //get the pad + getPadSafe(padID, true, function(err, pad) + { + if(ERR(err, callback)) return; + + + //check if this is a valid revision + if(rev > pad.getHeadRevisionNumber()) + { + callback(new customError("rev is higher than the head revision of the pad","apierror")); + return; + } + + pad.getInternalRevisionAText(rev, function(err, atext) + { + if(ERR(err, callback)) return; + + var oldText = pad.text(); + atext.text += "\n"; + function eachAttribRun(attribs, func) + { + var attribsIter = Changeset.opIterator(attribs); + var textIndex = 0; + var newTextStart = 0; + var newTextEnd = atext.text.length; + while (attribsIter.hasNext()) + { + var op = attribsIter.next(); + var nextIndex = textIndex + op.chars; + if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) + { + func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); + } + textIndex = nextIndex; + } + } + + // create a new changeset with a helper builder object + var builder = Changeset.builder(oldText.length); + + // assemble each line into the builder + eachAttribRun(atext.attribs, function(start, end, attribs) + { + builder.insert(atext.text.substring(start, end), attribs); + }); + + var lastNewlinePos = oldText.lastIndexOf('\n'); + if (lastNewlinePos < 0) { + builder.remove(oldText.length-1,0); + } else { + builder.remove(lastNewlinePos, oldText.match(/\n/g).length-1); + builder.remove(oldText.length - lastNewlinePos-1,0); + } + + var changeset = builder.toString(); + + //append the changeset + pad.appendRevision(changeset); + // + callback(null, changeset); + }); + + }); +}; + /** copyPad(sourceID, destinationID[, force=false]) copies a pad. If force is true, the destination will be overwritten if it exists. |