Allow docs to be restored

This commit is contained in:
James Allen 2014-06-05 16:18:25 +01:00
parent 1e31024bd7
commit 02f48be825
11 changed files with 159 additions and 6 deletions

View file

@ -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
}

View file

@ -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) ->

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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) ->
@ -295,6 +297,11 @@ define [
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)
@deletedDocsView.setLabels(labels)

View file

@ -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()

View file

@ -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

View file

@ -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;
}
}
}
}

View file

@ -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

View file

@ -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 ->