mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-15 15:37:36 +00:00
Hook up front end to restore documents
This commit is contained in:
parent
075db1e5b9
commit
0a1c543841
11 changed files with 175 additions and 84 deletions
|
@ -77,6 +77,9 @@ module.exports = HistoryController =
|
|||
{project_id} = req.params
|
||||
{version, pathname} = req.body
|
||||
user_id = AuthenticationController.getLoggedInUserId req
|
||||
RestoreManager.restoreFile user_id, project_id, version, pathname, (error) ->
|
||||
RestoreManager.restoreFile user_id, project_id, version, pathname, (error, entity) ->
|
||||
return next(error) if error?
|
||||
res.send 204
|
||||
res.json {
|
||||
type: entity.type,
|
||||
id: entity._id
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ Errors = require '../Errors/Errors'
|
|||
moment = require 'moment'
|
||||
|
||||
module.exports = RestoreManager =
|
||||
restoreFile: (user_id, project_id, version, pathname, callback = (error) ->) ->
|
||||
restoreFile: (user_id, project_id, version, pathname, callback = (error, entity) ->) ->
|
||||
RestoreManager._writeFileVersionToDisk project_id, version, pathname, (error, fsPath) ->
|
||||
return callback(error) if error?
|
||||
basename = Path.basename(pathname)
|
||||
|
@ -18,6 +18,7 @@ module.exports = RestoreManager =
|
|||
return callback(error) if error?
|
||||
RestoreManager._addEntityWithUniqueName user_id, project_id, parent_folder_id, basename, fsPath, callback
|
||||
|
||||
|
||||
_findFolderOrRootFolderId: (project_id, dirname, callback = (error, folder_id) ->) ->
|
||||
# We're going to try to recover the file into the folder it was in previously,
|
||||
# but this is historical, so the folder may not exist anymore. Fallback to the
|
||||
|
@ -46,7 +47,7 @@ module.exports = RestoreManager =
|
|||
else
|
||||
callback(error)
|
||||
else
|
||||
callback()
|
||||
callback(null, entity)
|
||||
|
||||
_writeFileVersionToDisk: (project_id, version, pathname, callback = (error, fsPath) ->) ->
|
||||
url = "#{Settings.apis.project_history.url}/project/#{project_id}/version/#{version}/#{encodeURIComponent(pathname)}"
|
||||
|
|
|
@ -11,7 +11,7 @@ aside.file-tree.file-tree-history(ng-controller="FileTreeController", ng-class="
|
|||
.entity
|
||||
.entity-name.entity-name-history(
|
||||
ng-click="history.selection.pathname = pathname",
|
||||
ng-class="{ 'deleted': doc.deleted }"
|
||||
ng-class="{ 'deleted': !!doc.deletedAtV }"
|
||||
)
|
||||
i.fa.fa-fw.fa-pencil
|
||||
span {{ pathname }}
|
||||
|
|
|
@ -123,78 +123,8 @@ div#history(ng-show="ui.view == 'history'")
|
|||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}...
|
||||
|
||||
.diff-panel.full-size(ng-controller="HistoryDiffController")
|
||||
.diff(
|
||||
ng-if="!!history.diff && !history.diff.loading && !history.diff.deleted && !history.diff.error && !history.diff.binary"
|
||||
)
|
||||
.toolbar.toolbar-alt
|
||||
span.name
|
||||
| <strong>{{history.diff.highlights.length}} </strong>
|
||||
ng-pluralize(
|
||||
count="history.diff.highlights.length",
|
||||
when="{\
|
||||
'one': 'change',\
|
||||
'other': 'changes'\
|
||||
}"
|
||||
)
|
||||
| in <strong>{{history.diff.pathname}}</strong>
|
||||
.toolbar-right(ng-if="!history.isV2")
|
||||
a.btn.btn-danger.btn-sm(
|
||||
href,
|
||||
ng-click="openRestoreDiffModal()"
|
||||
) #{translate("restore_to_before_these_changes")}
|
||||
.toolbar-right(ng-if="history.isV2")
|
||||
button.btn.btn-danger.btn-sm(
|
||||
href,
|
||||
ng-if="history.selection.docs[history.selection.pathname].deleted"
|
||||
ng-click=""
|
||||
)
|
||||
i.fa.fa-fw.fa-step-backward
|
||||
| Restore this deleted file
|
||||
.diff-editor.hide-ace-cursor(
|
||||
ace-editor="history",
|
||||
theme="settings.theme",
|
||||
font-size="settings.fontSize",
|
||||
text="history.diff.text",
|
||||
highlights="history.diff.highlights",
|
||||
read-only="true",
|
||||
resize-on="layout:main:resize",
|
||||
navigate-highlights="true"
|
||||
)
|
||||
|
||||
.diff.diff-binary(ng-show="history.diff.binary")
|
||||
.toolbar.toolbar-alt
|
||||
span.name
|
||||
strong {{history.diff.pathname}}
|
||||
.alert.alert-info We're still working on showing image and binary changes, sorry. Stay tuned!
|
||||
|
||||
.diff-deleted.text-centered(
|
||||
ng-show="history.diff.deleted && !history.diff.restoreDeletedSuccess"
|
||||
)
|
||||
p.text-serif #{translate("file_has_been_deleted", {filename:"{{ history.diff.doc.name }} "})}
|
||||
p
|
||||
a.btn.btn-primary.btn-lg(
|
||||
href,
|
||||
ng-click="restoreDeletedDoc()",
|
||||
ng-disabled="history.diff.restoreInProgress"
|
||||
) #{translate("restore")}
|
||||
|
||||
.diff-deleted.text-centered(
|
||||
ng-show="history.diff.deleted && history.diff.restoreDeletedSuccess"
|
||||
)
|
||||
p.text-serif #{translate("file_restored", {filename:"{{ history.diff.doc.name }} "})}
|
||||
p.text-serif #{translate("file_restored_back_to_editor")}
|
||||
p
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
ng-click="backToEditorAfterRestore()",
|
||||
) #{translate("file_restored_back_to_editor_btn")}
|
||||
|
||||
.loading-panel(ng-show="history.diff.loading")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}...
|
||||
.error-panel(ng-show="history.diff.error")
|
||||
.alert.alert-danger #{translate("generic_something_went_wrong")}
|
||||
include ./history/diffPanelV1
|
||||
include ./history/diffPanelV2
|
||||
|
||||
script(type="text/ng-template", id="historyRestoreDiffModalTemplate")
|
||||
.modal-header
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
.diff-panel.full-size(ng-if="history.isV2", ng-controller="HistoryDiffController")
|
||||
.diff(
|
||||
ng-if="!!history.diff && !history.diff.loading && !history.diff.deleted && !history.diff.error && !history.diff.binary"
|
||||
)
|
||||
.toolbar.toolbar-alt
|
||||
span.name
|
||||
| <strong>{{history.diff.highlights.length}} </strong>
|
||||
ng-pluralize(
|
||||
count="history.diff.highlights.length",
|
||||
when="{\
|
||||
'one': 'change',\
|
||||
'other': 'changes'\
|
||||
}"
|
||||
)
|
||||
| in <strong>{{history.diff.pathname}}</strong>
|
||||
.toolbar-right
|
||||
a.btn.btn-danger.btn-sm(
|
||||
href,
|
||||
ng-click="openRestoreDiffModal()"
|
||||
) #{translate("restore_to_before_these_changes")}
|
||||
.diff-editor.hide-ace-cursor(
|
||||
ace-editor="history",
|
||||
theme="settings.theme",
|
||||
font-size="settings.fontSize",
|
||||
text="history.diff.text",
|
||||
highlights="history.diff.highlights",
|
||||
read-only="true",
|
||||
resize-on="layout:main:resize",
|
||||
navigate-highlights="true"
|
||||
)
|
||||
|
||||
.diff-deleted.text-centered(
|
||||
ng-show="history.diff.deleted && !history.diff.restoreDeletedSuccess"
|
||||
)
|
||||
p.text-serif #{translate("file_has_been_deleted", {filename:"{{ history.diff.doc.name }} "})}
|
||||
p
|
||||
a.btn.btn-primary.btn-lg(
|
||||
href,
|
||||
ng-click="restoreDeletedDoc()",
|
||||
ng-disabled="history.diff.restoreInProgress"
|
||||
) #{translate("restore")}
|
||||
|
||||
.diff-deleted.text-centered(
|
||||
ng-show="history.diff.deleted && history.diff.restoreDeletedSuccess"
|
||||
)
|
||||
p.text-serif #{translate("file_restored", {filename:"{{ history.diff.doc.name }} "})}
|
||||
p.text-serif #{translate("file_restored_back_to_editor")}
|
||||
p
|
||||
a.btn.btn-default(
|
||||
href,
|
||||
ng-click="backToEditorAfterRestore()",
|
||||
) #{translate("file_restored_back_to_editor_btn")}
|
||||
|
||||
.loading-panel(ng-show="history.diff.loading")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}...
|
||||
.error-panel(ng-show="history.diff.error")
|
||||
.alert.alert-danger #{translate("generic_something_went_wrong")}
|
|
@ -0,0 +1,50 @@
|
|||
.diff-panel.full-size(ng-if="history.isV2", ng-controller="HistoryV2DiffController")
|
||||
.diff(
|
||||
ng-if="!!history.diff && !history.diff.loading && !history.diff.error && !history.diff.binary"
|
||||
)
|
||||
.toolbar.toolbar-alt
|
||||
span.name
|
||||
| <strong>{{history.diff.highlights.length}} </strong>
|
||||
ng-pluralize(
|
||||
count="history.diff.highlights.length",
|
||||
when="{\
|
||||
'one': 'change',\
|
||||
'other': 'changes'\
|
||||
}"
|
||||
)
|
||||
| in <strong>{{history.diff.pathname}}</strong>
|
||||
.toolbar-right(ng-if="history.selection.docs[history.selection.pathname].deletedAtV")
|
||||
button.btn.btn-danger.btn-sm(
|
||||
ng-click="restoreDeletedFile()"
|
||||
ng-show="!restoreState.error"
|
||||
ng-disabled="restoreState.inflight"
|
||||
)
|
||||
i.fa.fa-fw.fa-step-backward
|
||||
span(ng-show="!restoreState.inflight")
|
||||
| Restore this deleted file
|
||||
span(ng-show="restoreState.inflight")
|
||||
| Restoring...
|
||||
span.text-danger(ng-show="restoreState.error")
|
||||
| Error restoring, sorry
|
||||
.diff-editor.hide-ace-cursor(
|
||||
ace-editor="history",
|
||||
theme="settings.theme",
|
||||
font-size="settings.fontSize",
|
||||
text="history.diff.text",
|
||||
highlights="history.diff.highlights",
|
||||
read-only="true",
|
||||
resize-on="layout:main:resize",
|
||||
navigate-highlights="true"
|
||||
)
|
||||
|
||||
.diff.diff-binary(ng-show="history.diff.binary")
|
||||
.toolbar.toolbar-alt
|
||||
span.name
|
||||
strong {{history.diff.pathname}}
|
||||
.alert.alert-info We're still working on showing image and binary changes, sorry. Stay tuned!
|
||||
|
||||
.loading-panel(ng-show="history.diff.loading")
|
||||
i.fa.fa-spin.fa-refresh
|
||||
| #{translate("loading")}...
|
||||
.error-panel(ng-show="history.diff.error")
|
||||
.alert.alert-danger #{translate("generic_something_went_wrong")}
|
|
@ -5,7 +5,7 @@
|
|||
],
|
||||
"verbose": true,
|
||||
"legacyWatch": true,
|
||||
"exec": "make compile",
|
||||
"exec": "make compile || exit 1",
|
||||
"watch": [
|
||||
"public/coffee/",
|
||||
"public/stylesheets/"
|
||||
|
|
|
@ -4,6 +4,7 @@ define [
|
|||
"ide/history/util/displayNameForUser"
|
||||
"ide/history/controllers/HistoryListController"
|
||||
"ide/history/controllers/HistoryDiffController"
|
||||
"ide/history/controllers/HistoryV2DiffController"
|
||||
"ide/history/directives/infiniteScroll"
|
||||
], (moment, ColorManager, displayNameForUser) ->
|
||||
class HistoryManager
|
||||
|
|
|
@ -49,6 +49,13 @@ define [
|
|||
diff: null
|
||||
}
|
||||
|
||||
restoreFile: (version, pathname) ->
|
||||
url = "/project/#{@$scope.project_id}/restore_file"
|
||||
@ide.$http.post(url, {
|
||||
version, pathname,
|
||||
_csrf: window.csrfToken
|
||||
})
|
||||
|
||||
MAX_RECENT_UPDATES_TO_SELECT: 5
|
||||
autoSelectRecentUpdates: () ->
|
||||
return if @$scope.history.updates.length == 0
|
||||
|
@ -204,7 +211,7 @@ define [
|
|||
# Map of original pathname -> doc summary
|
||||
docs_summary = Object.create(null)
|
||||
|
||||
updatePathnameWithUpdateVersions = (pathname, update, deleted) ->
|
||||
updatePathnameWithUpdateVersions = (pathname, update, deletedAtV) ->
|
||||
# docs_summary is indexed by the original pathname the doc
|
||||
# had at the start, so we have to look this up from the current
|
||||
# pathname via original_pathname first
|
||||
|
@ -222,8 +229,8 @@ define [
|
|||
doc_summary.toV,
|
||||
update.toV
|
||||
)
|
||||
if deleted?
|
||||
doc_summary.deleted = true
|
||||
if deletedAtV?
|
||||
doc_summary.deletedAtV = deletedAtV
|
||||
|
||||
# Put updates in ascending chronological order
|
||||
updates = updates.slice().reverse()
|
||||
|
@ -241,7 +248,7 @@ define [
|
|||
updatePathnameWithUpdateVersions(add.pathname, update)
|
||||
if project_op.remove?
|
||||
remove = project_op.remove
|
||||
updatePathnameWithUpdateVersions(remove.pathname, update, true)
|
||||
updatePathnameWithUpdateVersions(remove.pathname, update, remove.atV)
|
||||
|
||||
return docs_summary
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
define [
|
||||
"base"
|
||||
], (App) ->
|
||||
App.controller "HistoryV2DiffController", ($scope, ide, event_tracking) ->
|
||||
console.log "HistoryV2DiffController!"
|
||||
|
||||
$scope.restoreState =
|
||||
inflight: false
|
||||
error: false
|
||||
|
||||
$scope.restoreDeletedFile = () ->
|
||||
pathname = $scope.history.selection.pathname
|
||||
return if !pathname?
|
||||
version = $scope.history.selection.docs[pathname]?.deletedAtV
|
||||
return if !version?
|
||||
event_tracking.sendMB "history-v2-restore-deleted"
|
||||
$scope.restoreState.inflight = true
|
||||
$scope.restoreState.error = false
|
||||
ide.historyManager
|
||||
.restoreFile(version, pathname)
|
||||
.then (response) ->
|
||||
{ data } = response
|
||||
$scope.restoreState.inflight = false
|
||||
if data.type == 'doc'
|
||||
openDoc(data.id)
|
||||
.catch () ->
|
||||
$scope.restoreState.error = true
|
||||
|
||||
openDoc = (id) ->
|
||||
iterations = 0
|
||||
do tryOpen = () ->
|
||||
if iterations > 5
|
||||
return
|
||||
doc = ide.fileTreeManager.findEntityById(id)
|
||||
if doc?
|
||||
ide.editorManager.openDoc(doc)
|
||||
else
|
||||
setTimeout(tryOpen, 500)
|
||||
|
|
@ -130,12 +130,13 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
|
|||
project_ops: [{
|
||||
remove:
|
||||
pathname: "main.tex"
|
||||
atV: 2
|
||||
}]
|
||||
fromV: 1, toV: 2
|
||||
}])
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
"main.tex": { fromV: 0, toV: 2, deleted: true }
|
||||
"main.tex": { fromV: 0, toV: 2, deletedAtV: 2 }
|
||||
})
|
||||
|
||||
it "should track single deletions", ->
|
||||
|
@ -143,10 +144,11 @@ define ['ide/history/HistoryV2Manager'], (HistoryV2Manager) ->
|
|||
project_ops: [{
|
||||
remove:
|
||||
pathname: "main.tex"
|
||||
atV: 1
|
||||
}]
|
||||
fromV: 0, toV: 1
|
||||
}])
|
||||
|
||||
expect(result).to.deep.equal({
|
||||
"main.tex": { fromV: 0, toV: 1, deleted: true }
|
||||
"main.tex": { fromV: 0, toV: 1, deletedAtV: 1 }
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue