diff --git a/services/web/app/coffee/Features/Compile/ClsiManager.coffee b/services/web/app/coffee/Features/Compile/ClsiManager.coffee
old mode 100644
new mode 100755
index b7930f5cf3..639511154e
--- a/services/web/app/coffee/Features/Compile/ClsiManager.coffee
+++ b/services/web/app/coffee/Features/Compile/ClsiManager.coffee
@@ -8,8 +8,8 @@ logger = require "logger-sharelatex"
url = require("url")
module.exports = ClsiManager =
- sendRequest: (project_id, callback = (error, success) ->) ->
- ClsiManager._buildRequest project_id, (error, req) ->
+ sendRequest: (project_id, settingsOverride = {}, callback = (error, success) ->) ->
+ ClsiManager._buildRequest project_id, settingsOverride, (error, req) ->
return callback(error) if error?
logger.log project_id: project_id, "sending compile to CLSI"
ClsiManager._postToClsi project_id, req, (error, response) ->
@@ -52,7 +52,7 @@ module.exports = ClsiManager =
return outputFiles
VALID_COMPILERS: ["pdflatex", "latex", "xelatex", "lualatex"]
- _buildRequest: (project_id, callback = (error, request) ->) ->
+ _buildRequest: (project_id, settingsOverride={}, callback = (error, request) ->) ->
Project.findById project_id, {compiler: 1, rootDoc_id: 1}, (error, project) ->
return callback(error) if error?
return callback(new Errors.NotFoundError("project does not exist: #{project_id}")) if !project?
@@ -67,6 +67,7 @@ module.exports = ClsiManager =
resources = []
rootResourcePath = null
+ rootResourcePathOverride = null
for path, doc of docs
path = path.replace(/^\//, "") # Remove leading /
@@ -75,6 +76,10 @@ module.exports = ClsiManager =
content: doc.lines.join("\n")
if project.rootDoc_id? and doc._id.toString() == project.rootDoc_id.toString()
rootResourcePath = path
+ if settingsOverride.rootDoc_id? and doc._id.toString() == settingsOverride.rootDoc_id.toString()
+ rootResourcePathOverride = path
+
+ rootResourcePath = rootResourcePathOverride if rootResourcePathOverride?
for path, file of files
path = path.replace(/^\//, "") # Remove leading /
diff --git a/services/web/app/coffee/Features/Compile/CompileController.coffee b/services/web/app/coffee/Features/Compile/CompileController.coffee
old mode 100644
new mode 100755
index aa4fab9e30..0d919fbd75
--- a/services/web/app/coffee/Features/Compile/CompileController.coffee
+++ b/services/web/app/coffee/Features/Compile/CompileController.coffee
@@ -11,9 +11,11 @@ module.exports = CompileController =
compile: (req, res, next = (error) ->) ->
project_id = req.params.Project_id
isAutoCompile = !!req.query?.auto_compile
+ settingsOverride = req.body?.settingsOverride ? {};
+ logger.log "root doc overriden" if settingsOverride.rootDoc_id?
AuthenticationController.getLoggedInUserId req, (error, user_id) ->
return next(error) if error?
- CompileManager.compile project_id, user_id, { isAutoCompile }, (error, status, outputFiles) ->
+ CompileManager.compile project_id, user_id, { isAutoCompile, settingsOverride }, (error, status, outputFiles) ->
return next(error) if error?
res.contentType("application/json")
res.send 200, JSON.stringify {
diff --git a/services/web/app/coffee/Features/Compile/CompileManager.coffee b/services/web/app/coffee/Features/Compile/CompileManager.coffee
old mode 100644
new mode 100755
index a0b242e95f..972b7d3286
--- a/services/web/app/coffee/Features/Compile/CompileManager.coffee
+++ b/services/web/app/coffee/Features/Compile/CompileManager.coffee
@@ -25,12 +25,12 @@ module.exports = CompileManager =
return callback(error) if error?
if recentlyCompiled
return callback new Error("project was recently compiled so not continuing")
-
+
CompileManager._ensureRootDocumentIsSet project_id, (error) ->
return callback(error) if error?
DocumentUpdaterHandler.flushProjectToMongo project_id, (error) ->
return callback(error) if error?
- ClsiManager.sendRequest project_id, (error, status, outputFiles) ->
+ ClsiManager.sendRequest project_id, opt.settingsOverride, (error, status, outputFiles) ->
return callback(error) if error?
logger.log files: outputFiles, "output files"
callback(null, status, outputFiles)
diff --git a/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee b/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee
index 44433d0f14..9a35e43c8b 100644
--- a/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee
+++ b/services/web/app/coffee/Features/Docstore/DocstoreManager.coffee
@@ -30,9 +30,14 @@ module.exports = DocstoreManager =
logger.error err: error, project_id: project_id, "error getting all docs from docstore"
callback(error)
- getDoc: (project_id, doc_id, callback = (error, lines, rev) ->) ->
- logger.log project_id: project_id, doc_id: doc_id, "getting doc in docstore api"
+ getDoc: (project_id, doc_id, options = {}, callback = (error, lines, rev) ->) ->
+ if typeof(options) == "function"
+ callback = options
+ options = {}
+ logger.log project_id: project_id, doc_id: doc_id, options: options, "getting doc in docstore api"
url = "#{settings.apis.docstore.url}/project/#{project_id}/doc/#{doc_id}"
+ if options.include_deleted
+ url += "?include_deleted=true"
request.get {
url: url
json: true
diff --git a/services/web/app/coffee/Features/Editor/EditorHttpController.coffee b/services/web/app/coffee/Features/Editor/EditorHttpController.coffee
new file mode 100644
index 0000000000..57b65d7355
--- /dev/null
+++ b/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
+ }
+
diff --git a/services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee
index 6c78a8e939..4ad3f19bff 100644
--- a/services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee
+++ b/services/web/app/coffee/Features/Project/ProjectEditorHandler.coffee
@@ -15,6 +15,7 @@ module.exports = ProjectEditorHandler =
description: project.description
spellCheckLanguage: project.spellCheckLanguage
deletedByExternalDataSource : project.deletedByExternalDataSource || false
+ deletedDocs: project.deletedDocs
if options.includeUsers
result.features =
diff --git a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
index 355bb298ec..8559e1f64c 100644
--- a/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
+++ b/services/web/app/coffee/Features/Project/ProjectEntityHandler.coffee
@@ -111,8 +111,11 @@ module.exports = ProjectEntityHandler =
logger.log sl_req_id: sl_req_id, project_id: project_id, "removing root doc"
Project.update {_id:project_id}, {$unset: {rootDoc_id: true}}, {}, callback
- getDoc: (project_id, doc_id, callback = (error, lines, rev) ->) ->
- DocstoreManager.getDoc project_id, doc_id, callback
+ getDoc: (project_id, doc_id, options = {}, callback = (error, lines, rev) ->) ->
+ if typeof(options) == "function"
+ callback = options
+ options = {}
+ DocstoreManager.getDoc project_id, doc_id, options, callback
addDoc: (project_or_id, folder_id, docName, docLines, sl_req_id, callback = (error, doc, folder_id) ->)=>
{callback, sl_req_id} = slReqIdHelper.getCallbackAndReqId(callback, sl_req_id)
@@ -135,6 +138,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, include_deleted: true, (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) ->
diff --git a/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee b/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee
index 80f2c70eb3..1c8202ac71 100644
--- a/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee
+++ b/services/web/app/coffee/Features/Project/ProjectRootDocManager.coffee
@@ -10,7 +10,8 @@ module.exports = ProjectRootDocManager =
root_doc_id = null
for path, doc of docs
for line in doc.lines || []
- if Path.extname(path).match(/\.R?tex$/) and line.match(/\\documentclass/)
+ match = line.match /(.*)\\documentclass/ # no lookbehind in js regexp :(
+ if Path.extname(path).match(/\.R?tex$/) and match and !match[1].match /%/
root_doc_id = doc._id
if root_doc_id?
ProjectEntityHandler.setRootDoc project_id, root_doc_id, sl_req_id, callback
diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee
index cae1e9e4c5..aa7d19c1c7 100644
--- a/services/web/app/coffee/router.coffee
+++ b/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
diff --git a/services/web/app/views/templates.jade b/services/web/app/views/templates.jade
index a20e02d473..b679299a5a 100644
--- a/services/web/app/views/templates.jade
+++ b/services/web/app/views/templates.jade
@@ -82,6 +82,12 @@
.dropdown-caret
i.icon-chevron-down
.entity-label.label.label-success
+
+ script(type="text/template")#deletedDocsFolderTemplate
+ .entity-list-item.entity-deleted-docs-folder(class="entity-{{ type }}", entity-type="{{ type }}", id="{{ id }}")
+ .clickable.js-clickable
+ span.name {{ name }}
+ .entity-label.label.label-success
script(type="text/template")#entityListTemplate
.contents
@@ -386,8 +392,14 @@
script(type='text/template')#trackChangesDiffTemplate
.track-changes-diff-toolbar.btn-toolbar
- .number-of-changes {{ changes }} in {{ name }}
- a(href="#").restore.btn.btn-small.btn-danger Restore to before these changes
+ .change-info
+ .number-of-changes {{ changes }} in {{ name }}
+ a(href="#").restore.btn.btn-small.btn-danger Restore to before these changes
+ .deleted-info(style="display:none;")
+ strong {{ name }}
+ .controls
+ 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
diff --git a/services/web/public/coffee/file-tree/DeletedDocsFolderView.coffee b/services/web/public/coffee/file-tree/DeletedDocsFolderView.coffee
new file mode 100644
index 0000000000..3ce1ec12fd
--- /dev/null
+++ b/services/web/public/coffee/file-tree/DeletedDocsFolderView.coffee
@@ -0,0 +1,29 @@
+define [
+ "file-tree/FolderView"
+], (FolderView) ->
+ DeletedDocsFolderView = FolderView.extend
+ template: $("#deletedDocsFolderTemplate").html()
+
+ render: () ->
+ @$el.append(Mustache.to_html @template, @model.attributes)
+ @_bindToDomElements()
+ @hideRenameBox()
+ @hideToggle()
+ @renderEntries()
+ @showEntries()
+ return @
+
+ onClick: () ->
+ e.preventDefault()
+
+ onToggle: () ->
+ e.preventDefault()
+
+ getContextMenuEntries: () -> null
+
+ hideToggle: () ->
+ @$(".js-toggle").hide()
+
+
+
+
diff --git a/services/web/public/coffee/file-tree/EntityView.coffee b/services/web/public/coffee/file-tree/EntityView.coffee
index b00c0cf017..c7b17e7419 100644
--- a/services/web/public/coffee/file-tree/EntityView.coffee
+++ b/services/web/public/coffee/file-tree/EntityView.coffee
@@ -112,6 +112,7 @@ define [
showContextMenu: (position) ->
entries = @getContextMenuEntries()
+ return if !entries?
@manager.trigger "contextmenu:beforeshow", @model, entries
diff --git a/services/web/public/coffee/file-tree/FileTreeManager.coffee b/services/web/public/coffee/file-tree/FileTreeManager.coffee
index d683a2a795..2f66bec030 100644
--- a/services/web/public/coffee/file-tree/FileTreeManager.coffee
+++ b/services/web/public/coffee/file-tree/FileTreeManager.coffee
@@ -3,11 +3,13 @@ define [
"models/File"
"models/Folder"
"file-tree/FileTreeView"
+ "file-tree/FolderView"
+ "file-tree/DeletedDocsFolderView"
"utils/Effects"
"utils/Modal"
"libs/backbone"
"libs/jquery.storage"
-], (Doc, File, Folder, FileTreeView, Effects, Modal) ->
+], (Doc, File, Folder, FileTreeView, FolderView, DeletedDocsFolderView, Effects, Modal) ->
class FileTreeManager
constructor: (@ide) ->
_.extend(@, Backbone.Events)
@@ -37,6 +39,13 @@ define [
populateFileTree: () ->
@view.bindToRootFolder(@project.get("rootFolder"))
+
+ if @deletedDocsView?
+ @deletedDocsView.$el.remove()
+ @deletedDocsView = new DeletedDocsFolderView(model: @project.get("deletedDocs"), manager: @)
+ @deletedDocsView.render()
+ $("#sections").append(@deletedDocsView.$el)
+ @hideDeletedDocs()
listenForUpdates: () ->
@ide.socket.on 'reciveNewDoc', (folder_id, doc) =>
@@ -100,8 +109,12 @@ define [
@ide.sideBarView.deselectAll()
@views[entity_id]?.select()
- getEntity: (entity_id) ->
- @views[entity_id]?.model
+ getEntity: (entity_id, options = {include_deleted: false}) ->
+ model = @views[entity_id]?.model
+ if !model? or (model.get("deleted") and !options.include_deleted)
+ return
+ else
+ return model
getSelectedEntity: () -> @getEntity(@selected_entity_id)
getSelectedEntityId: () -> @getSelectedEntity()?.id
@@ -283,6 +296,19 @@ 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)
+ @deletedDocsView.setLabels(labels)
+
+ showDeletedDocs: () ->
+ if @project.get("deletedDocs").get("children").length > 0
+ @deletedDocsView.$el.show()
+
+ hideDeletedDocs: () ->
+ @deletedDocsView.$el.hide()
diff --git a/services/web/public/coffee/models/Doc.coffee b/services/web/public/coffee/models/Doc.coffee
index 9d98dd2878..a80b3b6acf 100644
--- a/services/web/public/coffee/models/Doc.coffee
+++ b/services/web/public/coffee/models/Doc.coffee
@@ -9,3 +9,4 @@ define [
attributes =
id: rawAttributes._id
name: rawAttributes.name
+ deleted: !!rawAttributes.deleted
diff --git a/services/web/public/coffee/models/Folder.coffee b/services/web/public/coffee/models/Folder.coffee
index 8106800de9..d9c3df13ec 100644
--- a/services/web/public/coffee/models/Folder.coffee
+++ b/services/web/public/coffee/models/Folder.coffee
@@ -20,11 +20,11 @@ define [
id: rawAttributes._id
name: rawAttributes.name
children = []
- for childFolder in rawAttributes.folders
+ for childFolder in rawAttributes.folders or []
children.push new Folder(childFolder, parse: true)
- for file in rawAttributes.fileRefs
+ for file in rawAttributes.fileRefs or []
children.push new File(file, parse: true)
- for doc in rawAttributes.docs
+ for doc in rawAttributes.docs or []
children.push new Doc(doc, parse: true)
attributes.children = new FolderChildren(children)
return attributes
diff --git a/services/web/public/coffee/models/Project.coffee b/services/web/public/coffee/models/Project.coffee
index 92634352b2..f62098b154 100644
--- a/services/web/public/coffee/models/Project.coffee
+++ b/services/web/public/coffee/models/Project.coffee
@@ -2,8 +2,9 @@ define [
"models/User"
"models/ProjectMemberList"
"models/Folder"
+ "models/Doc"
"libs/backbone"
-], (User, ProjectMemberList, Folder) ->
+], (User, ProjectMemberList, Folder, Doc) ->
Project = Backbone.Model.extend
initialize: ->
@on "change:ide", (project, ide) =>
@@ -39,6 +40,15 @@ define [
member = User.findOrBuild rawMember._id, rawMember
members.add member
+ for doc in rawAttributes.deletedDocs
+ doc.deleted = true
+
+ attributes.deletedDocs = new Folder({
+ _id: "deleted-docs-folder"
+ name: "Deleted documents"
+ docs: rawAttributes.deletedDocs
+ }, parse: true)
+
return attributes
bindToRootDocId: ->
diff --git a/services/web/public/coffee/pdf/CompiledView.coffee b/services/web/public/coffee/pdf/CompiledView.coffee
old mode 100644
new mode 100755
index 4f12007379..74afc1e11b
--- a/services/web/public/coffee/pdf/CompiledView.coffee
+++ b/services/web/public/coffee/pdf/CompiledView.coffee
@@ -204,7 +204,12 @@ define [
recompilePdf: () ->
@options.manager.trigger "compile:pdf"
- @options.manager.refreshPdf()
+ rootDocOverride_id = null
+ for line in @ide.editor.getLines()
+ match = line.match /(.*)\\documentclass/
+ if match and !match[1].match /%/
+ rootDocOverride_id = @ide.editor.getCurrentDocId()
+ @options.manager.refreshPdf {rootDocOverride_id}
toggleFlatViewButton: () -> @$("#flatViewButton").button("toggle")
toggleSplitViewButton: () -> @$("#splitViewButton").button("toggle")
diff --git a/services/web/public/coffee/pdf/PdfManager.coffee b/services/web/public/coffee/pdf/PdfManager.coffee
index 04bb682bf9..d988797904 100644
--- a/services/web/public/coffee/pdf/PdfManager.coffee
+++ b/services/web/public/coffee/pdf/PdfManager.coffee
@@ -143,7 +143,7 @@ define [
@view.onCompiling()
@syncButtonsView?.hide()
@compiling = true
- @_doCompile opts.isAutoCompile, (error, status, outputFiles) =>
+ @_doCompile opts, (error, status, outputFiles) =>
@compiling = false
doneCompiling()
@@ -172,16 +172,19 @@ define [
if outputFiles?
@view.showOutputFileDownloadLinks(outputFiles)
- _doCompile: (isAutoCompile, callback = (error, status, outputFiles) ->) ->
+ _doCompile: (opts, callback = (error, status, outputFiles) ->) ->
url = "/project/#{@ide.project_id}/compile"
- if isAutoCompile
+ if opts.isAutoCompile
url += "?auto_compile=true"
$.ajax(
url: url
type: "POST"
headers:
"X-CSRF-Token": window.csrfToken
+ contentType: "application/json; charset=utf-8"
dataType: 'json'
+ data: JSON.stringify settingsOverride:
+ rootDoc_id: opts.rootDocOverride_id ? null
success: (body, status, response) ->
callback null, body.status, body.outputFiles
error: (error) ->
diff --git a/services/web/public/coffee/track-changes/DiffView.coffee b/services/web/public/coffee/track-changes/DiffView.coffee
index 0ac6002e8c..d704ae6bb0 100644
--- a/services/web/public/coffee/track-changes/DiffView.coffee
+++ b/services/web/public/coffee/track-changes/DiffView.coffee
@@ -10,17 +10,23 @@ 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()
+ if !@model.get("doc").get("deleted")
+ @model.on "change:diff", () => @render()
+ @model.fetch()
+ else
+ @render()
render: ->
- diff = @model.get("diff")
- return unless diff?
-
changes = @getNumberOfChanges()
html = Mustache.to_html @template, {
changes: "#{changes} change#{if changes == 1 then "" else "s"}"
@@ -28,18 +34,26 @@ define [
}
@$el.html(html)
- if !@model.get("from")? or !@model.get("to")? or changes == 0
- @$(".restore").hide()
+ if @model.get("doc").get("deleted")
+ @$(".change-info").hide()
+ @$(".deleted-info").show()
+ else
+ diff = @model.get("diff")
+ return unless diff?
+
+ if !@model.get("from")? or !@model.get("to")? or changes == 0
+ @$(".restore").hide()
+
+ @createAceEditor()
+ @aceEditor.setValue(@getPlainDiffContent())
+ @aceEditor.clearSelection()
+ @$ace = $(@aceEditor.renderer.container).find(".ace_scroller")
+ @insertMarkers()
+ @insertNameTag()
+ @insertMoreChangeLabels()
+ @bindToScrollEvents()
+ @scrollToFirstChange()
- @createAceEditor()
- @aceEditor.setValue(@getPlainDiffContent())
- @aceEditor.clearSelection()
- @$ace = $(@aceEditor.renderer.container).find(".ace_scroller")
- @insertMarkers()
- @insertNameTag()
- @insertMoreChangeLabels()
- @bindToScrollEvents()
- @scrollToFirstChange()
return @
remove: () ->
diff --git a/services/web/public/coffee/track-changes/TrackChangesManager.coffee b/services/web/public/coffee/track-changes/TrackChangesManager.coffee
index d1a8484a91..0c45197047 100644
--- a/services/web/public/coffee/track-changes/TrackChangesManager.coffee
+++ b/services/web/public/coffee/track-changes/TrackChangesManager.coffee
@@ -71,6 +71,9 @@ define [
@ide.mainAreaManager.change "trackChanges"
@ide.editor.disable()
@ide.fileViewManager.disable()
+
+ @ide.fileTreeManager.showDeletedDocs()
+
@enable()
showUpgradeView: () ->
@@ -93,6 +96,7 @@ define [
@ide.fileTreeManager.openDoc(@doc_id)
@ide.tabManager.show "code"
@resetLabels()
+ @ide.fileTreeManager.hideDeletedDocs()
autoSelectDiff: () ->
if @changes.models.length == 0
@@ -160,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(
@@ -171,7 +175,14 @@ define [
@diffView.on "restore", () =>
@restoreDiff(@diff)
- @diff.fetch()
+ @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
@ide.fileTreeManager.selectEntity(@doc_id)
@@ -217,6 +228,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
diff --git a/services/web/public/coffee/track-changes/models/Change.coffee b/services/web/public/coffee/track-changes/models/Change.coffee
index 7e2d10315f..ac48997583 100644
--- a/services/web/public/coffee/track-changes/models/Change.coffee
+++ b/services/web/public/coffee/track-changes/models/Change.coffee
@@ -21,6 +21,6 @@ define [
toV: data.toV
# TODO: We should not use a global reference here, but
# it's hard to get @ide into Backbone at this point.
- entity: ide.fileTreeManager.getEntity(doc_id)
+ entity: ide.fileTreeManager.getEntity(doc_id, include_deleted: true)
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 d8ee48726f..e0c108ac66 100644
--- a/services/web/public/coffee/track-changes/models/Diff.coffee
+++ b/services/web/public/coffee/track-changes/models/Diff.coffee
@@ -5,7 +5,7 @@ define [
Diff = Backbone.Model.extend
initialize: (attributes, options) ->
@ide = options.ide
- @set "doc", @ide.fileTreeManager.getEntity(@get("doc_id"))
+ @set "doc", @ide.fileTreeManager.getEntity(@get("doc_id"), include_deleted: true)
url: () ->
url = "/project/#{@get("project_id")}/doc/#{@get("doc_id")}/diff"
diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less
index f7228ebeb0..29debebbf7 100644
--- a/services/web/public/stylesheets/app/editor.less
+++ b/services/web/public/stylesheets/app/editor.less
@@ -414,6 +414,14 @@ body.editor {
}
}
+ .entity-deleted-docs-folder {
+ margin-top: 16px;
+ span.name {
+ padding: 6px;
+ border-bottom: 1px solid #ccc;
+ }
+ }
+
.entity-list {
padding-left: 0px;
}
diff --git a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee
index 6791443c9a..6915529b95 100644
--- a/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Compile/ClsiManagerTests.coffee
@@ -24,7 +24,7 @@ describe "ClsiManager", ->
describe "sendRequest", ->
beforeEach ->
- @ClsiManager._buildRequest = sinon.stub().callsArgWith(1, null, @request = "mock-request")
+ @ClsiManager._buildRequest = sinon.stub().callsArgWith(2, null, @request = "mock-request")
describe "with a successful compile", ->
beforeEach ->
@@ -39,7 +39,7 @@ describe "ClsiManager", ->
type: "log"
}]
})
- @ClsiManager.sendRequest @project_id, @callback
+ @ClsiManager.sendRequest @project_id, {}, @callback
it "should build the request", ->
@ClsiManager._buildRequest
@@ -67,7 +67,7 @@ describe "ClsiManager", ->
compile:
status: @status = "failure"
})
- @ClsiManager.sendRequest @project_id, @callback
+ @ClsiManager.sendRequest @project_id, {}, @callback
it "should call the callback with a failure statue", ->
@callback.calledWith(null, @status).should.equal true
@@ -121,7 +121,7 @@ describe "ClsiManager", ->
describe "with a valid project", ->
beforeEach (done) ->
- @ClsiManager._buildRequest @project_id, (error, request) =>
+ @ClsiManager._buildRequest @project_id, null, (error, request) =>
@request = request
done()
@@ -159,10 +159,32 @@ describe "ClsiManager", ->
}]
)
+
+ describe "when root doc override is valid", ->
+ beforeEach (done) ->
+ @ClsiManager._buildRequest @project_id, {rootDoc_id:"mock-doc-id-2"}, (error, request) =>
+ @request = request
+ done()
+
+ it "should change root path", ->
+ @request.compile.rootResourcePath.should.equal "chapters/chapter1.tex"
+
+
+ describe "when root doc override is invalid", ->
+ beforeEach (done) ->
+ @ClsiManager._buildRequest @project_id, {rootDoc_id:"invalid-id"}, (error, request) =>
+ @request = request
+ done()
+
+ it "should fallback to default root doc", ->
+ @request.compile.rootResourcePath.should.equal "main.tex"
+
+
+
describe "when the project has an invalid compiler", ->
beforeEach (done) ->
@project.compiler = "context"
- @ClsiManager._buildRequest @project, (error, request) =>
+ @ClsiManager._buildRequest @project, null, (error, request) =>
@request = request
done()
@@ -172,7 +194,7 @@ describe "ClsiManager", ->
describe "when there is no valid root document", ->
beforeEach (done) ->
@project.rootDoc_id = "not-valid"
- @ClsiManager._buildRequest @project, (@error, @request) =>
+ @ClsiManager._buildRequest @project, null, (@error, @request) =>
done()
it "should return an error", ->
diff --git a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee
index 0820c0431c..fe456995ad 100644
--- a/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Compile/CompileControllerTests.coffee
@@ -44,7 +44,7 @@ describe "CompileController", ->
it "should do the compile without the auto compile flag", ->
@CompileManager.compile
- .calledWith(@project_id, @user_id, { isAutoCompile: false })
+ .calledWith(@project_id, @user_id, { isAutoCompile: false, settingsOverride:{} })
.should.equal true
it "should set the content-type of the response to application/json", ->
@@ -71,7 +71,7 @@ describe "CompileController", ->
it "should do the compile with the auto compile flag", ->
@CompileManager.compile
- .calledWith(@project_id, @user_id, { isAutoCompile: true })
+ .calledWith(@project_id, @user_id, { isAutoCompile: true, settingsOverride:{} })
.should.equal true
describe "downloadPdf", ->
diff --git a/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee b/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee
index f1b963a8c5..c284341bf2 100644
--- a/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Compile/CompileManagerTests.coffee
@@ -36,7 +36,7 @@ describe "CompileManager", ->
@CompileManager._checkIfRecentlyCompiled = sinon.stub().callsArgWith(2, null, false)
@CompileManager._ensureRootDocumentIsSet = sinon.stub().callsArgWith(1, null)
@DocumentUpdaterHandler.flushProjectToMongo = sinon.stub().callsArgWith(1, null)
- @ClsiManager.sendRequest = sinon.stub().callsArgWith(1, null, @status = "mock-status")
+ @ClsiManager.sendRequest = sinon.stub().callsArgWith(2, null, @status = "mock-status")
describe "succesfully", ->
beforeEach ->
diff --git a/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee b/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee
index 164a5f656f..c9eda7d1df 100644
--- a/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Docstore/DocstoreManagerTests.coffee
@@ -131,6 +131,22 @@ describe "DocstoreManager", ->
}, "error getting doc from docstore")
.should.equal true
+ describe "with include_deleted=true", ->
+ beforeEach ->
+ @request.get = sinon.stub().callsArgWith(1, null, statusCode: 204, @doc)
+ @DocstoreManager.getDoc @project_id, @doc_id, include_deleted: true, @callback
+
+ it "should get the doc from the docstore api (including deleted)", ->
+ @request.get
+ .calledWith({
+ url: "#{@settings.apis.docstore.url}/project/#{@project_id}/doc/#{@doc_id}?include_deleted=true"
+ json: true
+ })
+ .should.equal true
+
+ it "should call the callback with the lines, version and rev", ->
+ @callback.calledWith(null, @lines, @rev).should.equal true
+
describe "getAllDocs", ->
describe "with a successful response code", ->
beforeEach ->
diff --git a/services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee b/services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee
new file mode 100644
index 0000000000..706476a6c4
--- /dev/null
+++ b/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
diff --git a/services/web/test/UnitTests/coffee/Project/ProjectEditorHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectEditorHandlerTests.coffee
index 02e23f6785..afb93e68ac 100644
--- a/services/web/test/UnitTests/coffee/Project/ProjectEditorHandlerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Project/ProjectEditorHandlerTests.coffee
@@ -55,6 +55,10 @@ describe "ProjectEditorHandler", ->
last_name : "Write"
email : "read-write@sharelatex.com"
}]
+ deletedDocs: [{
+ _id: "deleted-doc-id"
+ name: "main.tex"
+ }]
@handler = SandboxedModule.require modulePath
describe "buildProjectModelView", ->
@@ -85,6 +89,10 @@ describe "ProjectEditorHandler", ->
@result.owner.first_name.should.equal "Owner"
@result.owner.last_name.should.equal "ShareLaTeX"
@result.owner.privileges.should.equal "owner"
+
+ it "should include the deletedDocs", ->
+ should.exist @result.deletedDocs
+ @result.deletedDocs.should.equal @project.deletedDocs
it "should gather readOnly_refs and collaberators_refs into a list of members", ->
findMember = (id) =>
diff --git a/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee b/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee
index a66a8f9884..b16c7e1a03 100644
--- a/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee
+++ b/services/web/test/UnitTests/coffee/Project/ProjectEntityHandlerTests.coffee
@@ -286,7 +286,7 @@ describe 'ProjectEntityHandler', ->
beforeEach ->
@lines = ["mock", "doc", "lines"]
@rev = 5
- @DocstoreManager.getDoc = sinon.stub().callsArgWith(2, null, @lines, @rev)
+ @DocstoreManager.getDoc = sinon.stub().callsArgWith(3, null, @lines, @rev)
@ProjectEntityHandler.getDoc project_id, doc_id, @callback
it "should call the docstore", ->
@@ -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(3, 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, include_deleted: true)
+ .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 ->