mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-09 09:26:15 +00:00
Allow docs to be restored
This commit is contained in:
parent
1e31024bd7
commit
02f48be825
11 changed files with 159 additions and 6 deletions
|
@ -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
|
||||
}
|
||||
|
|
@ -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) ->
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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 ->
|
||||
|
|
Loading…
Add table
Reference in a new issue