Просмотр исходного кода

Add in restore button to track changes

James Allen 12 лет назад
Родитель
Сommit
afb8bb6a42

+ 1 - 1
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"

+ 1 - 0
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

+ 4 - 2
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...

+ 5 - 0
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: () ->

+ 3 - 0
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" })
 	}

+ 10 - 2
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")
 

+ 43 - 5
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 <strong>#{name}</strong> 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

+ 7 - 3
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
-			}
+			}
+			if change.meta.user?
+				model.user = User.findOrBuild(change.meta.user.id, change.meta.user)
+			else
+				model.user = User.getAnonymousUser()
+			return model

+ 5 - 2
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

+ 3 - 3
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)

+ 23 - 4
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;
+				}
+			}
+		}
 	}
 }

+ 8 - 5
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", ->