Просмотр исходного кода

Allow projects to be restore after being deleted

James Allen 12 лет назад
Родитель
Сommit
015fd6873f

+ 21 - 17
services/web/app/coffee/Features/Project/ProjectController.coffee

@@ -16,10 +16,19 @@ SecurityManager = require("../../managers/SecurityManager")
 
 
 module.exports =
 module.exports =
 
 
-	deleteProject: (req, res)->
+	deleteProject: (req, res) ->
 		project_id = req.params.Project_id
 		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?
 			if err?
 				res.send 500
 				res.send 500
 			else
 			else
@@ -73,8 +82,6 @@ module.exports =
 		timer = new metrics.Timer("project-list")
 		timer = new metrics.Timer("project-list")
 		user_id = req.session.user._id
 		user_id = req.session.user._id
 		async.parallel {
 		async.parallel {
-			subscription: (cb)->
-				SubscriptionLocator.getUsersSubscription user_id, cb
 			tags: (cb)->
 			tags: (cb)->
 				TagsHandler.getAllTags user_id, cb
 				TagsHandler.getAllTags user_id, cb
 			projects: (cb)->
 			projects: (cb)->
@@ -84,10 +91,17 @@ module.exports =
 					logger.err err:err, "error getting data for project list page"
 					logger.err err:err, "error getting data for project list page"
 					return res.send 500
 					return res.send 500
 				logger.log results:results, user_id:user_id, "rendering project list"
 				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
 				res.render 'project/list', viewModel
 				timer.done()
 				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)->
 	loadEditor: (req, res, next)->
 		timer = new metrics.Timer("load-editor")
 		timer = new metrics.Timer("load-editor")
@@ -166,7 +180,6 @@ module.exports =
 					languages: Settings.languages
 					languages: Settings.languages
 					timer.done()
 					timer.done()
 
 
-
 defaultSettingsForAnonymousUser = (user_id)->
 defaultSettingsForAnonymousUser = (user_id)->
 	id : user_id
 	id : user_id
 	ace:
 	ace:
@@ -183,15 +196,7 @@ defaultSettingsForAnonymousUser = (user_id)->
 		dropbox: false
 		dropbox: false
 		trackChanges: 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
 	for project in projects
 		project.accessLevel = "owner"
 		project.accessLevel = "owner"
 	for project in collabertions
 	for project in collabertions
@@ -211,7 +216,6 @@ _buildListViewModel = (projects, collabertions, readOnlyProjects, tags, tagsGrou
 		title:'Your Projects'
 		title:'Your Projects'
 		priority_title: true
 		priority_title: true
 		projects: sortedProjects
 		projects: sortedProjects
-		freeTrial: freeTrial
 		tags:tags
 		tags:tags
 		projectTabActive: true
 		projectTabActive: true
 	}
 	}

+ 10 - 2
services/web/app/coffee/Features/Project/ProjectDeleter.coffee

@@ -20,8 +20,7 @@ module.exports =
 		logger.log owner_id:owner_id, "deleting users projects"
 		logger.log owner_id:owner_id, "deleting users projects"
 		Project.remove owner_ref:owner_id, callback
 		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"
 		logger.log project_id:project_id, "deleting project"
 		Project.findById project_id, (err, project)=>
 		Project.findById project_id, (err, project)=>
 			if err? or !project?
 			if err? or !project?
@@ -48,3 +47,12 @@ module.exports =
 					if err?
 					if err?
 						logger.err err:err, "problem deleting project"
 						logger.err err:err, "problem deleting project"
 					callback(err)
 					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

+ 3 - 1
services/web/app/coffee/router.coffee

@@ -92,6 +92,7 @@ module.exports = class Router
 		app.get  '/user/:user_id/personal_info', httpAuth, UserInfoController.getPersonalInfo
 		app.get  '/user/:user_id/personal_info', httpAuth, UserInfoController.getPersonalInfo
 		
 		
 		app.get  '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
 		app.get  '/project', AuthenticationController.requireLogin(), ProjectController.projectListPage
+		app.get  '/project/archived', AuthenticationController.requireLogin(), ProjectController.archivedProjects
 		app.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
 		app.post '/project/new', AuthenticationController.requireLogin(), ProjectController.newProject
 
 
 		app.get '/project/new/template', TemplatesMiddlewear.saveTemplateDataInSession, AuthenticationController.requireLogin(), TemplatesController.createProjectFromZipTemplate
 		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.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
 		app.post '/project/:Project_id/rename', SecurityManager.requestIsOwner, ProjectController.renameProject
 
 

+ 46 - 0
services/web/app/views/project/archived.jade

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

+ 0 - 27
services/web/app/views/project/flat.jade

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

+ 0 - 17
services/web/app/views/project/list.jade

@@ -124,23 +124,6 @@ block content
 									a(href="/learn") help guides
 									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
 		include ../general/small-footer
 	script(type="text/template")#tagTemplate
 	script(type="text/template")#tagTemplate
 		mixin tag('{{ project_id }}', '{{ tagName }}', true)
 		mixin tag('{{ project_id }}', '{{ tagName }}', true)

+ 0 - 10
services/web/app/views/project/table.jade

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

+ 12 - 0
services/web/public/coffee/list.coffee

@@ -169,6 +169,18 @@ require [
 		$modal.find('.cancel').click (e)->
 		$modal.find('.cancel').click (e)->
 			$modal.modal('hide')
 			$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) ->
 	newProject = (template, fileToOpen) ->
 		$modal = $('#newProjectModal')
 		$modal = $('#newProjectModal')
 		$confirm = $('#confirmNewProject')
 		$confirm = $('#confirmNewProject')

+ 1 - 0
services/web/public/stylesheets/less/list.less

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

+ 28 - 5
services/web/test/UnitTests/coffee/Project/ProjectControllerTests.coffee

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

+ 33 - 4
services/web/test/UnitTests/coffee/Project/ProjectDeleterTests.coffee

@@ -56,17 +56,17 @@ describe 'Project deleter', ->
 				done()
 				done()
 
 
 
 
-	describe "deleteProject", ->
+	describe "archiveProject", ->
 		beforeEach ->
 		beforeEach ->
 			@Project.update.callsArgWith(2)
 			@Project.update.callsArgWith(2)
 
 
 		it "should flushProjectToMongoAndDelete in doc updater", (done)->
 		it "should flushProjectToMongoAndDelete in doc updater", (done)->
-			@deleter.deleteProject @project_id, =>
+			@deleter.archiveProject @project_id, =>
 				@documentUpdaterHandler.flushProjectToMongoAndDelete.calledWith(@project_id).should.equal true
 				@documentUpdaterHandler.flushProjectToMongoAndDelete.calledWith(@project_id).should.equal true
 				done()
 				done()
 
 
 		it "should remove the project", (done)->
 		it "should remove the project", (done)->
-			@deleter.deleteProject @project_id, =>
+			@deleter.archiveProject @project_id, =>
 				@Project.update.calledWith({
 				@Project.update.calledWith({
 					_id:@project_id
 					_id:@project_id
 				}, {
 				}, {
@@ -75,7 +75,7 @@ describe 'Project deleter', ->
 				done()
 				done()
 
 
 		it "should removeProjectFromAllTags", (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.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[0], @project_id).should.equal true
 				@TagsHandler.removeProjectFromAllTags.calledWith(@project.collaberator_refs[1], @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()
 				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
+