summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn McLear <john@mclear.co.uk>2014-11-15 15:32:24 +0000
committerJohn McLear <john@mclear.co.uk>2014-11-15 15:32:24 +0000
commita75f02cddf58406a98f1c11be84e9361fb2e1ed8 (patch)
treedb92679c9131e142e8ab81b1c5fdd4ef09ec19f4 /src
parente8fda27ead8bb550b027b9578199e2b034e388d1 (diff)
parent0253156dbb2e26a1a894a414c61ff8e521b3fece (diff)
downloadetherpad-lite-a75f02cddf58406a98f1c11be84e9361fb2e1ed8.zip
Merge pull request #2300 from cristo-rabani/patch-1
Added option to restore revisions #1791
Diffstat (limited to 'src')
-rw-r--r--src/node/db/API.js111
-rw-r--r--src/node/handler/APIHandler.js48
2 files changed, 158 insertions, 1 deletions
diff --git a/src/node/db/API.js b/src/node/db/API.js
index 4a912368..07d3703c 100644
--- a/src/node/db/API.js
+++ b/src/node/db/API.js
@@ -575,6 +575,117 @@ exports.deletePad = function(padID, callback)
pad.remove(callback);
});
}
+/**
+ restoreRevision(padID, [rev]) Restores revision from past as new changeset
+
+ Example returns:
+
+ {code:0, message:"ok", data:null}
+ {code: 1, message:"padID does not exist", data: null}
+ */
+exports.restoreRevision = function (padID, rev, callback)
+{
+ var Changeset = require("ep_etherpad-lite/static/js/Changeset");
+ var padMessage = require("ep_etherpad-lite/node/handler/PadMessageHandler.js");
+
+ //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);
+ //
+ padMessage.updatePadClients(pad, function ()
+ {
+ });
+ callback(null, null);
+ });
+
+ });
+};
/**
copyPad(sourceID, destinationID[, force=false]) copies a pad. If force is true,
diff --git a/src/node/handler/APIHandler.js b/src/node/handler/APIHandler.js
index 273a58a6..9adc2418 100644
--- a/src/node/handler/APIHandler.js
+++ b/src/node/handler/APIHandler.js
@@ -345,10 +345,56 @@ var version =
, "getChatHistory" : ["padID", "start", "end"]
, "getChatHead" : ["padID"]
}
+, "1.2.11":
+ { "createGroup" : []
+ , "createGroupIfNotExistsFor" : ["groupMapper"]
+ , "deleteGroup" : ["groupID"]
+ , "listPads" : ["groupID"]
+ , "listAllPads" : []
+ , "createDiffHTML" : ["padID", "startRev", "endRev"]
+ , "createPad" : ["padID", "text"]
+ , "createGroupPad" : ["groupID", "padName", "text"]
+ , "createAuthor" : ["name"]
+ , "createAuthorIfNotExistsFor": ["authorMapper" , "name"]
+ , "listPadsOfAuthor" : ["authorID"]
+ , "createSession" : ["groupID", "authorID", "validUntil"]
+ , "deleteSession" : ["sessionID"]
+ , "getSessionInfo" : ["sessionID"]
+ , "listSessionsOfGroup" : ["groupID"]
+ , "listSessionsOfAuthor" : ["authorID"]
+ , "getText" : ["padID", "rev"]
+ , "setText" : ["padID", "text"]
+ , "getHTML" : ["padID", "rev"]
+ , "setHTML" : ["padID", "html"]
+ , "getAttributePool" : ["padID"]
+ , "getRevisionsCount" : ["padID"]
+ , "getRevisionChangeset" : ["padID", "rev"]
+ , "getLastEdited" : ["padID"]
+ , "deletePad" : ["padID"]
+ , "copyPad" : ["sourceID", "destinationID", "force"]
+ , "movePad" : ["sourceID", "destinationID", "force"]
+ , "getReadOnlyID" : ["padID"]
+ , "getPadID" : ["roID"]
+ , "setPublicStatus" : ["padID", "publicStatus"]
+ , "getPublicStatus" : ["padID"]
+ , "setPassword" : ["padID", "password"]
+ , "isPasswordProtected" : ["padID"]
+ , "listAuthorsOfPad" : ["padID"]
+ , "padUsersCount" : ["padID"]
+ , "getAuthorName" : ["authorID"]
+ , "padUsers" : ["padID"]
+ , "sendClientsMessage" : ["padID", "msg"]
+ , "listAllGroups" : []
+ , "checkToken" : []
+ , "getChatHistory" : ["padID"]
+ , "getChatHistory" : ["padID", "start", "end"]
+ , "getChatHead" : ["padID"]
+ , "restoreRevision" : ["padID", "rev"]
+ }
};
// set the latest available API version here
-exports.latestApiVersion = '1.2.10';
+exports.latestApiVersion = '1.2.11';
// exports the versions so it can be used by the new Swagger endpoint
exports.version = version;