Allow projects to be restore after being deleted

This commit is contained in:
James Allen 2014-06-03 17:35:44 +01:00
parent bc6d1cb5be
commit 015fd6873f
11 changed files with 154 additions and 83 deletions

View file

@ -16,10 +16,19 @@ SecurityManager = require("../../managers/SecurityManager")
module.exports =
deleteProject: (req, res)->
deleteProject: (req, res) ->
project_id = req.params.Project_id
logger.log project_id:project_id, "recived request to delete project"
projectDeleter.deleteProject project_id, (err)->
logger.log project_id:project_id, "received request to delete project"
projectDeleter.archiveProject project_id, (err)->
if err?
res.send 500
else
res.send 200
restoreProject: (req, res) ->
project_id = req.params.Project_id
logger.log project_id:project_id, "received request to restore project"
projectDeleter.restoreProject project_id, (err)->
if err?
res.send 500
else
@ -73,8 +82,6 @@ module.exports =
timer = new metrics.Timer("project-list")
user_id = req.session.user._id
async.parallel {
subscription: (cb)->
SubscriptionLocator.getUsersSubscription user_id, cb
tags: (cb)->
TagsHandler.getAllTags user_id, cb
projects: (cb)->
@ -84,10 +91,17 @@ module.exports =
logger.err err:err, "error getting data for project list page"
return res.send 500
logger.log results:results, user_id:user_id, "rendering project list"
viewModel = _buildListViewModel results.projects[0], results.projects[1], results.projects[2], results.tags[0], results.tags[1], results.subscription?[0]
viewModel = _buildListViewModel results.projects[0], results.projects[1], results.projects[2], results.tags[0], results.tags[1]
res.render 'project/list', viewModel
timer.done()
archivedProjects: (req, res, next)->
user_id = req.session.user._id
projectDeleter.findArchivedProjects user_id, 'name lastUpdated publicAccesLevel', (error, projects) ->
return next(error) if error?
logger.log projects: projects, user_id:user_id, "rendering archived project list"
viewModel = _buildListViewModel projects, [], [], [], {}
res.render 'project/archived', viewModel
loadEditor: (req, res, next)->
timer = new metrics.Timer("load-editor")
@ -166,7 +180,6 @@ module.exports =
languages: Settings.languages
timer.done()
defaultSettingsForAnonymousUser = (user_id)->
id : user_id
ace:
@ -183,15 +196,7 @@ defaultSettingsForAnonymousUser = (user_id)->
dropbox: false
trackChanges: false
_buildListViewModel = (projects, collabertions, readOnlyProjects, tags, tagsGroupedByProject, subscription)->
# TODO: Remove this one month after the ability to start free trials was removed
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
freeTrial =
expired: !!subscription.freeTrial.downgraded
expiresAt: SubscriptionFormatters.formatDate(subscription.freeTrial.expiresAt)
_buildListViewModel = (projects, collabertions, readOnlyProjects, tags, tagsGroupedByProject)->
for project in projects
project.accessLevel = "owner"
for project in collabertions
@ -211,7 +216,6 @@ _buildListViewModel = (projects, collabertions, readOnlyProjects, tags, tagsGrou
title:'Your Projects'
priority_title: true
projects: sortedProjects
freeTrial: freeTrial
tags:tags
projectTabActive: true
}

View file

@ -20,8 +20,7 @@ module.exports =
logger.log owner_id:owner_id, "deleting users projects"
Project.remove owner_ref:owner_id, callback
deleteProject: (project_id, callback = (error) ->)->
archiveProject: (project_id, callback = (error) ->)->
logger.log project_id:project_id, "deleting project"
Project.findById project_id, (err, project)=>
if err? or !project?
@ -48,3 +47,12 @@ module.exports =
if err?
logger.err err:err, "problem deleting project"
callback(err)
restoreProject: (project_id, callback = (error) ->) ->
Project.update {_id:project_id}, { $unset: { archived: true }}, callback
findArchivedProjects: (owner_id, fields, callback = (error, projects) ->) ->
Project.find {
owner_ref: owner_id
archived: true
}, fields, callback

View file

@ -92,6 +92,7 @@ module.exports = class Router
app.get '/user/:user_id/personal_info', httpAuth, UserInfoController.getPersonalInfo
app.get '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
app.get '/project/archived', AuthenticationController.requireLogin(), ProjectController.archivedProjects
app.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
app.get '/project/new/template', TemplatesMiddlewear.saveTemplateDataInSession, AuthenticationController.requireLogin(), TemplatesController.createProjectFromZipTemplate
@ -115,7 +116,8 @@ module.exports = class Router
app.del '/Project/:Project_id', SecurityManager.requestIsOwner, ProjectController.deleteProject
app.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject
app.post '/Project/:Project_id/restore', SecurityManager.requestIsOwner, ProjectController.restoreProject
app.post '/Project/:Project_id/clone', SecurityManager.requestCanAccessProject, ProjectController.cloneProject
app.post '/project/:Project_id/rename', SecurityManager.requestIsOwner, ProjectController.renameProject

View file

@ -0,0 +1,46 @@
extends ../layout
block content
mixin projectList(projects)
-each project in projects
- project_id = project._id.toString()
.project_entry(id=project_id)
.btn-group.project-actions
a.btn(href='/project/'+project_id+'/restore', data-csrf=csrfToken, data-id=project_id).restoreProject Restore
.projectName #{project.name}
include ../general/sidebar
.content-with-navigation-sidebar
.box#projectListArea
.row-fluid
.span12
.page-header
h1 Archived Projects
- if (projects.length > 0)
.row-fluid
.span9
ul#projectList
mixin projectList(projects)
- else
.row-fluid
.span12 You don't have any archived projects
include ../general/small-footer
- locals.supressDefaultJs = true
script
window.requirejs = {
"paths" : {
"moment": "libs/moment"
}
};
script(
data-main=jsPath+'list.js?fingerprint='+fingerprint(jsPath + 'list.js'),
baseurl=jsPath,
src=jsPath+'libs/require.js?fingerprint='+fingerprint(jsPath + 'libs/require.js')
)

View file

@ -1,27 +0,0 @@
extends ../layout
block content
.container
.row
.span12.span-box
.page-header
h1 ShareLaTeX - Online LaTeX Editor : #{title}
//- .btn-group#newProject
//- a.btn.btn-success(href='/project/#{project_id}') go to editor mode
-each doc in resources
h2 #{doc.path}
if (doc.url != null)
img(src='/project/#{project_id}/file/#{doc.id}')
else
pre.prettyprint.lang-tex.linenums
-each line in doc.content
//leave the line below #{line}
span.
#{line}
hr
- locals.supressDefaultJs = true
script(data-main=jsPath+'codeprettifyer.js', src=jsPath+'libs/require.js', baseurl=jsPath)

View file

@ -124,23 +124,6 @@ block content
a(href="/learn") help guides
| .
if freeTrial && freeTrial.expired == false
.row-fluid
.span12
.alert.alert-info.alert-free-trial
p You are currently using a free trial which expires on #{freeTrial.expiresAt}.
p
a(href="/user/subscription").btn.btn-primary Upgrade now
if freeTrial && freeTrial.expired == true
.row-fluid
.span12
.alert.alert-danger.alert-free-trial
p Your free trial has expired! Upgrade now to continue using ShareLaTeX uninterrupted.
p
a(href="/user/subscription").btn.btn-danger Upgrade now
include ../general/small-footer
script(type="text/template")#tagTemplate
mixin tag('{{ project_id }}', '{{ tagName }}', true)

View file

@ -1,10 +0,0 @@
.container
table.table.table-striped.table-bordered#revisionList(cellpadding='0', cellspacing='0', border='0')
thead
tr
th Date
th Files Changed
tbody

View file

@ -169,6 +169,18 @@ require [
$modal.find('.cancel').click (e)->
$modal.modal('hide')
$('.restoreProject').click (event) ->
event.preventDefault()
id = $(@).data("id")
$.ajax
url: @href
type: 'POST'
data:
_csrf: $(@).data("csrf")
success: (data)->
$("##{id}").fadeOut(1000)
newProject = (template, fileToOpen) ->
$modal = $('#newProjectModal')
$confirm = $('#confirmNewProject')

View file

@ -7,6 +7,7 @@
&:hover {
background-color: #eaeaea;
}
min-height: 40px;
padding-left: 10px;
padding-top: 10px;

View file

@ -18,7 +18,9 @@ describe "ProjectController", ->
url:"chat.com"
siteUrl: "mysite.com"
@ProjectDeleter =
deleteProject: sinon.stub().callsArgWith(1)
archiveProject: sinon.stub().callsArgWith(1)
restoreProject: sinon.stub().callsArgWith(1)
findArchivedProjects: sinon.stub()
@ProjectDuplicator =
duplicate: sinon.stub().callsArgWith(3, null, {_id:@project_id})
@ProjectCreationHandler =
@ -68,18 +70,22 @@ describe "ProjectController", ->
jsPath:"js path here"
describe "deleteProject", ->
it "should tell the project deleter", (done)->
@res.send = (code)=>
@ProjectDeleter.deleteProject.calledWith(@project_id).should.equal true
@ProjectDeleter.archiveProject.calledWith(@project_id).should.equal true
code.should.equal 200
done()
@ProjectController.deleteProject @req, @res
describe "restoreProject", ->
it "should tell the project deleter", (done)->
@res.send = (code)=>
@ProjectDeleter.restoreProject.calledWith(@project_id).should.equal true
code.should.equal 200
done()
@ProjectController.restoreProject @req, @res
describe "cloneProject", ->
it "should call the project duplicator", (done)->
@res.send = (json)=>
@ProjectDuplicator.duplicate.calledWith(@user, @project_id, @projectName).should.equal true
@ -135,6 +141,23 @@ describe "ProjectController", ->
done()
@ProjectController.projectListPage @req, @res
describe "archivedProjects", ->
beforeEach ->
@projects = [{lastUpdated:1, _id:1}, {lastUpdated:2, _id:2}]
@ProjectDeleter.findArchivedProjects.callsArgWith(2, null, @projects)
it "should render the project/archived page", (done)->
@res.render = (pageName, opts)=>
pageName.should.equal "project/archived"
done()
@ProjectController.archivedProjects @req, @res
it "should send the projects", (done)->
@res.render = (pageName, opts)=>
opts.projects.length.should.equal (@projects.length)
done()
@ProjectController.archivedProjects @req, @res
describe "renameProject", ->
beforeEach ->
@newProjectName = "my supper great new project"

View file

@ -56,17 +56,17 @@ describe 'Project deleter', ->
done()
describe "deleteProject", ->
describe "archiveProject", ->
beforeEach ->
@Project.update.callsArgWith(2)
it "should flushProjectToMongoAndDelete in doc updater", (done)->
@deleter.deleteProject @project_id, =>
@deleter.archiveProject @project_id, =>
@documentUpdaterHandler.flushProjectToMongoAndDelete.calledWith(@project_id).should.equal true
done()
it "should remove the project", (done)->
@deleter.deleteProject @project_id, =>
@deleter.archiveProject @project_id, =>
@Project.update.calledWith({
_id:@project_id
}, {
@ -75,7 +75,7 @@ describe 'Project deleter', ->
done()
it "should removeProjectFromAllTags", (done)->
@deleter.deleteProject @project_id, =>
@deleter.archiveProject @project_id, =>
@TagsHandler.removeProjectFromAllTags.calledWith(@project.owner_ref, @project_id).should.equal true
@TagsHandler.removeProjectFromAllTags.calledWith(@project.collaberator_refs[0], @project_id).should.equal true
@TagsHandler.removeProjectFromAllTags.calledWith(@project.collaberator_refs[1], @project_id).should.equal true
@ -84,3 +84,32 @@ describe 'Project deleter', ->
done()
describe "restoreProject", ->
beforeEach ->
@Project.update.callsArgWith(2)
it "should unset the archive attribute", (done)->
@deleter.restoreProject @project_id, =>
@Project.update.calledWith({
_id: @project_id
}, {
$unset: { archived: true }
}).should.equal true
done()
describe "findArchivedProjects", ->
beforeEach ->
@projects = ["mock-project"]
@owner_id = "mock-owner-id"
@callback = sinon.stub()
@Project.find = sinon.stub().callsArgWith(2, null, @projects)
@deleter.findArchivedProjects @owner_id, @fields = "name lastModified", @callback
it "should find the archived projects for the owner", ->
@Project.find
.calledWith(owner_ref: @owner_id, archived: true, @fields)
.should.equal true
it "should return the projects", ->
@callback.calledWith(null, @projects).should.equal true