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

+ 21 - 0
services/web/app/coffee/Features/Editor/EditorHttpController.coffee

@@ -0,0 +1,21 @@
+ProjectEntityHandler = require "../Project/ProjectEntityHandler"
+logger = require "logger-sharelatex"
+EditorRealTimeController = require "./EditorRealTimeController"
+
+module.exports = EditorHttpController =
+	restoreDoc: (req, res, next) ->
+		project_id = req.params.Project_id
+		doc_id = req.params.doc_id
+		name = req.body.name
+
+		if !name?
+			return res.send 400 # Malformed request
+
+		logger.log project_id: project_id, doc_id: doc_id, "restoring doc"
+		ProjectEntityHandler.restoreDoc project_id, doc_id, name, (err, doc, folder_id) =>
+			return next(error) if error?
+			EditorRealTimeController.emitToRoom(project_id, 'reciveNewDoc', folder_id, doc)
+			res.json {
+				doc_id: doc._id
+			}
+

+ 7 - 0
services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee

@@ -135,6 +135,13 @@ module.exports = ProjectEntityHandler =
 							return callback(err) if err?
 							callback(null, doc, folder_id)
 
+	restoreDoc: (project_id, doc_id, name, callback = (error, doc, folder_id) ->) ->
+		# getDoc will return the deleted doc's lines, but we don't actually remove
+		# the deleted doc, just create a new one from its lines.
+		ProjectEntityHandler.getDoc project_id, doc_id, (error, lines) ->
+			return callback(error) if error?
+			ProjectEntityHandler.addDoc project_id, null, name, lines, callback
+
 	addFile: (project_or_id, folder_id, fileName, path, sl_req_id, callback = (error, fileRef, folder_id) ->)->
 		{callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
 		Project.getProject project_or_id, "", (err, project) ->

+ 3 - 0
services/web/app/coffee/router.coffee

@@ -7,6 +7,7 @@ SpellingController = require('./Features/Spelling/SpellingController')
 SecurityManager = require('./managers/SecurityManager')
 AuthorizationManager = require('./Features/Security/AuthorizationManager')
 EditorController = require("./Features/Editor/EditorController")
+EditorHttpController = require("./Features/Editor/EditorHttpController")
 EditorUpdatesController = require("./Features/Editor/EditorUpdatesController")
 Settings = require('settings-sharelatex')
 TpdsController = require('./Features/ThirdPartyDataStore/TpdsController')
@@ -125,6 +126,8 @@ module.exports = class Router
 		app.get  "/project/:Project_id/doc/:doc_id/diff", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
 		app.post "/project/:Project_id/doc/:doc_id/version/:version_id/restore", SecurityManager.requestCanAccessProject, TrackChangesController.proxyToTrackChangesApi
 
+		app.post "/project/:Project_id/doc/:doc_id/restore", SecurityManager.requestCanAccessProject, EditorHttpController.restoreDoc
+
 		app.post '/project/:project_id/leave', AuthenticationController.requireLogin(), CollaboratorsController.removeSelfFromProject
 		app.get  '/project/:Project_id/collaborators', SecurityManager.requestCanAccessProject(allow_auth_token: true), CollaboratorsController.getCollaborators
 

+ 3 - 0
services/web/app/views/templates.jade

@@ -472,6 +472,9 @@
 		.track-changes-diff-toolbar.btn-toolbar
 			.number-of-changes {{ changes }} in <strong>{{ name }}</strong>
 			a(href="#").restore.btn.btn-small.btn-danger Restore to before these changes
+			.deleted-info(style="display:none;")
+				span This file has been deleted 
+				a(href="#").restore-deleted.btn.btn-small.btn-success Restore
 		.track-changes-diff-editor
 
 	script(type='text/template')#changeListItemTemplate

+ 1 - 0
services/web/public/coffee/file-tree/EntityView.coffee

@@ -9,6 +9,7 @@ define [
 		initialize: () ->
 			@ide = @options.manager.ide
 			@manager = @options.manager
+			console.log "Registering view", @model, @model.id, @
 			@manager.registerView(@model.id, @)
 			@bindToModel()
 

+ 9 - 2
services/web/public/coffee/file-tree/FileTreeManager.coffee

@@ -38,6 +38,9 @@ define [
 
 		populateFileTree: () ->
 			@view.bindToRootFolder(@project.get("rootFolder"))
+			
+			if @deletedDocsView?
+				@deletedDocsView.$el.remove()
 			@deletedDocsView = new FolderView(model: @project.get("deletedDocs"), manager: @)
 			@deletedDocsView.render()
 			$("#sections").append(@deletedDocsView.$el)
@@ -72,6 +75,7 @@ define [
 				@onMoveEntity(entity_id, folder_id)
 
 		registerView: (entity_id, view) ->
+			console.log "inside", entity_id, view
 			@views[entity_id] = view
 
 		addEntityToFolder: (entity, folder_id) ->
@@ -284,8 +288,6 @@ define [
 
 		_doDelete: (entity) ->
 			@ide.socket.emit 'deleteEntity', entity.id, entity.get("type")
-			if entity.get("type") == "doc"
-				@project.get("deletedDocs").get("children").add entity
 			@onDeleteEntity entity.id
 
 		onDeleteEntity: (entity_id) ->
@@ -294,6 +296,11 @@ define [
 			entity.set("deleted", true)
 			entity.collection?.remove(entity)
 			delete @views[entity_id]
+
+			# Do this after the remove so that it's never in two places at once 
+			# and so that it doesn't get reset by deleting from @views
+			if entity.get("type") == "doc"
+				@project.get("deletedDocs").get("children").add entity
 			
 		setLabels: (labels) ->
 			@view.setLabels(labels)

+ 11 - 2
services/web/public/coffee/track-changes/DiffView.coffee

@@ -10,9 +10,14 @@ define [
 		template: $("#trackChangesDiffTemplate").html()
 
 		events:
-			"click .restore": () ->
-				console.log "click"
+			"click .restore": (e) ->
+				e.preventDefault()
 				@trigger "restore"
+			"click .restore-deleted": (e) ->
+				e.preventDefault()
+				@$("a.restore-deleted").attr("disabled", true)
+				@$("a.restore-deleted").text("Restoring...")
+				@trigger "restore-deleted"
 
 		initialize: () ->
 			@model.on "change:diff", () => @render()
@@ -31,6 +36,10 @@ define [
 			if !@model.get("from")? or !@model.get("to")? or changes == 0
 				@$(".restore").hide()
 
+			if @model.get("doc").get("deleted")
+				@$(".restore").hide()
+				@$(".deleted-info").show()
+
 			@createAceEditor()
 			@aceEditor.setValue(@getPlainDiffContent())
 			@aceEditor.clearSelection()

+ 25 - 1
services/web/public/coffee/track-changes/TrackChangesManager.coffee

@@ -164,7 +164,7 @@ define [
 				@diffView.remove()
 
 			if !@diff.get("doc")?
-				console.log "This document has been deleted. What should we do?"
+				console.log "This document does not exist. What should we do?"
 				return
 
 			@diffView = new DiffView(
@@ -175,6 +175,15 @@ define [
 			@diffView.on "restore", () =>
 				@restoreDiff(@diff)
 
+			@diffView.on "restore-deleted", () =>
+				@restoreDeletedDoc @diff.get("doc"), (error, doc_id) =>
+					return if error? or !doc_id?
+					setTimeout () =>
+						# Give doc a chance to appear in file tree via socket.io
+						@hide()
+						@ide.fileTreeManager.openDoc(doc_id)
+					, 1000
+
 			@diff.fetch()
 
 			@ide.fileTreeManager.selectEntity(@doc_id)
@@ -221,6 +230,21 @@ define [
 				}]
 			})
 
+		restoreDeletedDoc: (doc, callback) ->
+			$.ajax {
+				url: "/project/#{@project_id}/doc/#{doc.get("id")}/restore"
+				type: "POST"
+				dataType: "json"
+				data:
+					name: doc.get("name")
+				headers:
+					"X-CSRF-Token": window.csrfToken
+				success: (body, status, response) ->
+					callback(null, body?.doc_id)
+				error: (error) ->
+					callback(error)
+			}
+
 		enable: () ->
 			@enabled = true
 

+ 8 - 1
services/web/public/stylesheets/less/trackchanges.less

@@ -30,7 +30,7 @@
 			background-color: #282828;
 			color: white;
 			border-right: 1px solid white;
-			.number-of-changes, .restore {
+			.number-of-changes, .restore, .deleted-info {
 				position: absolute;
 			}
 			.number-of-changes {
@@ -42,6 +42,13 @@
 				bottom: 5px;
 				padding: 3px 9px;
 			}
+			.deleted-info {
+				right: 10px;
+				bottom: 5px;
+				a {
+					padding: 3px 9px;
+				}
+			}
 		}
 	}
 

+ 46 - 0
services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee

@@ -0,0 +1,46 @@
+SandboxedModule = require('sandboxed-module')
+sinon = require('sinon')
+require('chai').should()
+modulePath = require('path').join __dirname, '../../../../app/js/Features/Editor/EditorHttpController'
+
+describe "EditorHttpController", ->
+	beforeEach ->
+		@EditorHttpController = SandboxedModule.require modulePath, requires:
+			'../Project/ProjectEntityHandler' : @ProjectEntityHandler = {}
+			"./EditorRealTimeController": @EditorRealTimeController = {}
+			"logger-sharelatex": @logger = { log: sinon.stub(), error: sinon.stub() }
+		@project_id = "mock-project-id"
+		@doc_id = "mock-doc-id"
+		@req = {}
+		@res =
+			send: sinon.stub()
+			json: sinon.stub()
+
+	describe "restoreDoc", ->
+		beforeEach ->
+			@req.params =
+				Project_id: @project_id
+				doc_id: @doc_id
+			@req.body =
+				name: @name = "doc-name"
+			@ProjectEntityHandler.restoreDoc = sinon.stub().callsArgWith(3, null,
+				@doc = { "mock": "doc", _id: @new_doc_id = "new-doc-id" }
+				@folder_id = "mock-folder-id"
+			)
+			@EditorRealTimeController.emitToRoom = sinon.stub()
+			@EditorHttpController.restoreDoc @req, @res
+
+		it "should restore the doc", ->
+			@ProjectEntityHandler.restoreDoc
+				.calledWith(@project_id, @doc_id, @name)
+				.should.equal true
+
+		it "should the real-time clients about the new doc", ->
+			@EditorRealTimeController.emitToRoom
+				.calledWith(@project_id, 'reciveNewDoc', @folder_id, @doc)
+				.should.equal true
+
+		it "should return the new doc id", ->
+			@res.json
+				.calledWith(doc_id: @new_doc_id)
+				.should.equal true

+ 25 - 0
services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee

@@ -339,6 +339,31 @@ describe 'ProjectEntityHandler', ->
 				.calledWith(project_id, @doc._id.toString(), @lines)
 				.should.equal true
 
+	describe "restoreDoc", ->
+		beforeEach ->
+			@name = "doc-name"
+			@lines = ['1234','abc']
+			@doc = { "mock": "doc" }
+			@folder_id = "mock-folder-id"
+			@callback = sinon.stub()
+			@ProjectEntityHandler.getDoc = sinon.stub().callsArgWith(2, null, @lines)
+			@ProjectEntityHandler.addDoc = sinon.stub().callsArgWith(4, null, @doc, @folder_id)
+
+			@ProjectEntityHandler.restoreDoc project_id, doc_id, @name, @callback
+
+		it 'should get the doc lines', ->
+			@ProjectEntityHandler.getDoc
+				.calledWith(project_id, doc_id)
+				.should.equal true
+
+		it "should add a new doc with these doc lines", ->
+			@ProjectEntityHandler.addDoc
+				.calledWith(project_id, null, @name, @lines)
+				.should.equal true
+
+		it "should call the callback with the new folder and doc", ->
+			@callback.calledWith(null, @doc, @folder_id).should.equal true
+
 	describe 'adding file', ->
 		fileName = "something.jpg"
 		beforeEach ->