From afb8bb6a42609a199926da868893fa9aef5d5eec Mon Sep 17 00:00:00 2001 From: James Allen Date: Tue, 11 Mar 2014 12:13:46 +0000 Subject: [PATCH] Add in restore button to track changes --- .../TrackChangesController.coffee | 2 +- services/web/app/coffee/router.coffee | 1 + services/web/app/views/templates.jade | 6 ++- .../coffee/file-tree/FileTreeManager.coffee | 5 ++ services/web/public/coffee/models/User.coffee | 3 ++ .../track-changes/ChangeListView.coffee | 12 ++++- .../track-changes/TrackChangesManager.coffee | 48 +++++++++++++++++-- .../coffee/track-changes/models/Change.coffee | 10 ++-- .../coffee/track-changes/models/Diff.coffee | 7 ++- services/web/public/coffee/utils/Modal.coffee | 6 +-- .../public/stylesheets/less/trackchanges.less | 27 +++++++++-- .../TrackChangesControllerTests.coffee | 13 +++-- 12 files changed, 113 insertions(+), 27 deletions(-) diff --git a/services/web/app/coffee/Features/TrackChanges/TrackChangesController.coffee b/services/web/app/coffee/Features/TrackChanges/TrackChangesController.coffee index bb7193b5a7..1d7674e9da 100644 --- a/services/web/app/coffee/Features/TrackChanges/TrackChangesController.coffee +++ b/services/web/app/coffee/Features/TrackChanges/TrackChangesController.coffee @@ -6,7 +6,7 @@ module.exports = TrackChangesController = proxyToTrackChangesApi: (req, res, next = (error) ->) -> url = settings.apis.trackchanges.url + req.url logger.log url: url, "proxying to track-changes api" - getReq = request.get(url) + getReq = request(url: url, method: req.method) getReq.pipe(res) getReq.on "error", (error) -> logger.error err: error, "track-changes API error" diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 4c9f88b31a..46d702f13f 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -125,6 +125,7 @@ module.exports = class Router app.get "/project/:Project_id/doc/:doc_id/updates", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi app.get "/project/:Project_id/doc/:doc_id/diff", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi + app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecutiryManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi app.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject app.get '/project/:Project_id/collaborators', SecutiryManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators diff --git a/services/web/app/views/templates.jade b/services/web/app/views/templates.jade index ca7c07fa78..d63f2efeb0 100644 --- a/services/web/app/views/templates.jade +++ b/services/web/app/views/templates.jade @@ -140,8 +140,7 @@ button.btn.btn-primary ok script(type="text/template")#genericModalButtonTemplate - a(href="#",class="btn {{ class }}") {{ text }} - + button(class="btn {{ class }}") {{ text }} script(type="text/template")#editorPanelTemplate #editorArea(style='display: none;') @@ -445,6 +444,9 @@ div.color-square(style="background-color: hsl({{hue}}, 100%, 70%);") span {{name}} + div(class='restore') + a(href="#") Restore to here + script(type='text/template')#changeListTemplate ul.change-list.nav.nav-pills.nav-stacked li.loading-changes Loading... diff --git a/services/web/public/coffee/file-tree/FileTreeManager.coffee b/services/web/public/coffee/file-tree/FileTreeManager.coffee index 6d7364dd02..6526d91dad 100644 --- a/services/web/public/coffee/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/file-tree/FileTreeManager.coffee @@ -144,6 +144,11 @@ define [ # it's not the root folder so keep going path = entity.get("name") + "/" + path return path + + getNameOfEntityId: (entity_id) -> + entity = @getEntity(entity_id) + return if !entity? + return entity.get("name") # RENAMING renameSelected: () -> diff --git a/services/web/public/coffee/models/User.coffee b/services/web/public/coffee/models/User.coffee index 1e101bee34..f83df16139 100644 --- a/services/web/public/coffee/models/User.coffee +++ b/services/web/public/coffee/models/User.coffee @@ -49,4 +49,7 @@ define [ find: (id) -> @loadedModel ||= {} return @loadedModel[id] + + getAnonymousUser: () -> + return User.findOrBuild("anonymous", { first_name: "Anonymous", email: "anon@sharelatex.com" }) } diff --git a/services/web/public/coffee/track-changes/ChangeListView.coffee b/services/web/public/coffee/track-changes/ChangeListView.coffee index 6456e912b4..937542d43f 100644 --- a/services/web/public/coffee/track-changes/ChangeListView.coffee +++ b/services/web/public/coffee/track-changes/ChangeListView.coffee @@ -70,6 +70,9 @@ define [ delete @hoverFromIndex @resetHoverStates() + view.on "click:restore", (e) => + @trigger "restore", view.model + view.resetSelector(index, @selectedFromIndex, @selectedToIndex) resetAllSelectors: () -> @@ -77,11 +80,11 @@ define [ view.resetSelector(i, @selectedFromIndex, @selectedToIndex) resetHoverStates: () -> - if @hoverToIndex? + if @hoverToIndex? and @hoverToIndex != @selectedToIndex @$("ul").addClass("hover-state") for view, i in @itemViews view.resetHoverState(i, @selectedFromIndex, @hoverToIndex) - else if @hoverFromIndex? + else if @hoverFromIndex? and @hoverFromIndex != @selectedFromIndex @$("ul").addClass("hover-state") for view, i in @itemViews view.resetHoverState(i, @hoverFromIndex, @selectedToIndex) @@ -152,6 +155,7 @@ define [ @trigger "mouseenter:from", args... "mouseleave .change-selector-from": (args...) -> @trigger "mouseleave:from", args... + "click .restore a": "onRestoreClick" template : $("#changeListItemTemplate").html() @@ -183,6 +187,10 @@ define [ onFromSelectorClick: (e) -> @trigger "selected:from", e, @ + onRestoreClick: (e) -> + e.preventDefault() + @trigger "click:restore", e, @ + isSelectedFrom: () -> @$(".change-selector-from").is(":checked") diff --git a/services/web/public/coffee/track-changes/TrackChangesManager.coffee b/services/web/public/coffee/track-changes/TrackChangesManager.coffee index 58d470b51b..cd56626e56 100644 --- a/services/web/public/coffee/track-changes/TrackChangesManager.coffee +++ b/services/web/public/coffee/track-changes/TrackChangesManager.coffee @@ -3,21 +3,23 @@ define [ "track-changes/models/Diff" "track-changes/ChangeListView" "track-changes/DiffView" -], (ChangeList, Diff, ChangeListView, DiffView) -> + "utils/Modal" + "moment" +], (ChangeList, Diff, ChangeListView, DiffView, Modal, moment) -> class TrackChangesManager template: $("#trackChangesPanelTemplate").html() constructor: (@ide) -> @$el = $(@template) $("#editorWrapper").append(@$el) - @hideEl() + @hide() @ide.editor.on "change:doc", () => - @hideEl() + @hide() @$el.find(".track-changes-close").on "click", (e) => e.preventDefault - @hideEl() + @hide() show: () -> @project_id = window.userSettings.project_id @@ -44,14 +46,50 @@ define [ ) @diff.fetch() + @changeListView.on "restore", (change) => + @restore(change) + @showEl() showEl: -> @ide.editor.hide() @$el.show() - hideEl: () -> + hide: () -> @ide.editor.show() @$el.hide() + restore: (change) -> + name = @ide.fileTreeManager.getNameOfEntityId(@doc_id) + date = moment(change.get("start_ts")).format("Do MMM YYYY, h:mm:ss a") + modal = new Modal({ + title: "Restore document" + message: "Are you sure you want to restore #{name} to before the changes on #{date}" + buttons: [{ + text: "Cancel" + }, { + text: "Restore" + class: "btn-success" + close: false + callback: ($button) => + $button.text("Restoring...") + $button.prop("disabled", true) + @doRestore change.get("version"), (error) => + modal.remove() + @hide() + }] + }) + + doRestore: (version, callback = (error) ->) -> + $.ajax { + url: "/project/#{@project_id}/doc/#{@doc_id}/version/#{version}/restore" + type: "POST" + headers: + "X-CSRF-Token": window.csrfToken + success: () -> + callback() + error: (error) -> + callback(error) + } + return TrackChangesManager diff --git a/services/web/public/coffee/track-changes/models/Change.coffee b/services/web/public/coffee/track-changes/models/Change.coffee index fcefb43661..845c44d5ec 100644 --- a/services/web/public/coffee/track-changes/models/Change.coffee +++ b/services/web/public/coffee/track-changes/models/Change.coffee @@ -4,9 +4,13 @@ define [ ], (User)-> Change = Backbone.Model.extend parse: (change) -> - return { + model = { start_ts: change.meta.start_ts end_ts: change.meta.end_ts - user: User.findOrBuild(change.meta.user.id, change.meta.user) version: change.v - } \ No newline at end of file + } + if change.meta.user? + model.user = User.findOrBuild(change.meta.user.id, change.meta.user) + else + model.user = User.getAnonymousUser() + return model \ No newline at end of file diff --git a/services/web/public/coffee/track-changes/models/Diff.coffee b/services/web/public/coffee/track-changes/models/Diff.coffee index 7d301273e2..52ad05c439 100644 --- a/services/web/public/coffee/track-changes/models/Diff.coffee +++ b/services/web/public/coffee/track-changes/models/Diff.coffee @@ -8,6 +8,9 @@ define [ parse: (diff) -> for entry in diff.diff - if entry.meta? and entry.meta.user? - entry.meta.user = User.findOrBuild(entry.meta.user.id, entry.meta.user) + if entry.meta? + if entry.meta.user? + entry.meta.user = User.findOrBuild(entry.meta.user.id, entry.meta.user) + else + entry.meta.user = User.getAnonymousUser() return diff diff --git a/services/web/public/coffee/utils/Modal.coffee b/services/web/public/coffee/utils/Modal.coffee index 709445b6fd..d2adc70df8 100644 --- a/services/web/public/coffee/utils/Modal.coffee +++ b/services/web/public/coffee/utils/Modal.coffee @@ -19,8 +19,9 @@ define [ button.on "click", (e) -> e.preventDefault() if buttonOptions.callback? - buttonOptions.callback() - self.remove() + buttonOptions.callback(button) + if !buttonOptions.close? or buttonOptions.close + self.remove() @$el.modal # make sure we control when the modal is hidden @@ -39,7 +40,6 @@ define [ @$el.find('input').focus() - remove: () -> @$el.modal("hide") Backbone.View.prototype.remove.call(this) diff --git a/services/web/public/stylesheets/less/trackchanges.less b/services/web/public/stylesheets/less/trackchanges.less index 3dc4a94a42..031356225f 100644 --- a/services/web/public/stylesheets/less/trackchanges.less +++ b/services/web/public/stylesheets/less/trackchanges.less @@ -71,11 +71,9 @@ ul.change-list { li { - padding: 6px 4px; position: relative; border-bottom: 1px solid #ccc; cursor: pointer; - min-height: 38px; .change-selectors { .change-selector-from { position: absolute; @@ -98,7 +96,8 @@ } } .change-description { - padding-left: 26px; + padding: 6px 4px 6px 30px; + min-height: 38px; } .change-name { font-size: 0.9em; @@ -112,6 +111,15 @@ margin-right: 4px; margin-bottom: -1px; } + .restore { + a { + display: block; + padding: 4px; + text-align: center; + border-top: 1px solid #ccc; + } + display: none; + } &:hover { background-color: #eaeaea; } @@ -137,12 +145,16 @@ li.selected-from { .change-selectors { .range { - bottom: 10px; + bottom: 37px; } .change-selector-from { opacity: 1; + bottom: 32px; } } + .restore { + display: block; + } } } ul.change-list.hover-state { @@ -182,5 +194,12 @@ } } } + li.selected-from.hover-selected-from { + .change-selectors { + .range { + bottom: 37px; + } + } + } } } \ No newline at end of file diff --git a/services/web/test/UnitTests/coffee/TrackChanges/TrackChangesControllerTests.coffee b/services/web/test/UnitTests/coffee/TrackChanges/TrackChangesControllerTests.coffee index e98305b939..9aa558a77e 100644 --- a/services/web/test/UnitTests/coffee/TrackChanges/TrackChangesControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/TrackChanges/TrackChangesControllerTests.coffee @@ -7,13 +7,13 @@ SandboxedModule = require('sandboxed-module') describe "TrackChangesController", -> beforeEach -> @TrackChangesController = SandboxedModule.require modulePath, requires: - "request" : @request = {} + "request" : @request = sinon.stub() "settings-sharelatex": @settings = {} "logger-sharelatex": @logger = {log: sinon.stub(), error: sinon.stub()} describe "proxyToTrackChangesApi", -> beforeEach -> - @req = { url: "/mock/url" } + @req = { url: "/mock/url", method: "POST" } @res = "mock-res" @next = sinon.stub() @settings.apis = @@ -23,13 +23,16 @@ describe "TrackChangesController", -> events: {} pipe: sinon.stub() on: (event, handler) -> @events[event] = handler - @request.get = sinon.stub().returns @proxy + @request.returns @proxy @TrackChangesController.proxyToTrackChangesApi @req, @res, @next describe "successfully", -> it "should call the track changes api", -> - @request.get - .calledWith("#{@settings.apis.trackchanges.url}#{@req.url}") + @request + .calledWith({ + url: "#{@settings.apis.trackchanges.url}#{@req.url}" + method: @req.method + }) .should.equal true it "should pipe the response to the client", ->