Prechádzať zdrojové kódy

Allow renaming of tags

James Allen 10 rokov pred
rodič
commit
1bd5aef85f

+ 12 - 0
services/web/app/coffee/Features/Tags/TagsController.coffee

@@ -27,3 +27,15 @@ module.exports =
 		TagsHandler.deleteTag user_id, tag_id, (error) ->
 			return next(error) if error?
 			res.status(204).end()
+	
+	renameTag: (req, res, next) ->
+		user_id = req.session.user._id
+		tag_id = req.params.tag_id
+		name = req.body?.name
+		if !name?
+			return res.status(400).end()
+		else
+			logger.log {user_id, tag_id, name}, "renaming tag"
+			TagsHandler.renameTag user_id, tag_id, name, (error) ->
+				return next(error) if error?
+				res.status(204).end()

+ 17 - 0
services/web/app/coffee/Features/Tags/TagsHandler.coffee

@@ -5,6 +5,23 @@ logger = require("logger-sharelatex")
 
 oneSecond = 1000
 module.exports = 
+	renameTag: (user_id, tag_id, name, callback = (error) ->) ->
+		url = "#{settings.apis.tags.url}/user/#{user_id}/tag/#{tag_id}/rename"
+		request.post {
+			url: url
+			json:
+				name: name
+		}, (err, res, body) ->
+			if err?
+				logger.err {err, user_id, tag_id, name}, "error renaming tag in tag api"
+				return callback(err)
+			else if res.statusCode >= 200 and res.statusCode < 300
+				return callback(null)
+			else
+				err = new Error("tags api returned a failure status code: #{res.statusCode}")
+				logger.err {err, user_id, tag_id, name}, "tags api returned failure status code: #{res.statusCode}"
+				return callback(err)
+
 	deleteTag: (user_id, tag_id, callback = (error) ->) ->
 		url = "#{settings.apis.tags.url}/user/#{user_id}/tag/#{tag_id}"
 		request.del url, (err, res, body) ->

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

@@ -134,6 +134,7 @@ module.exports = class Router
 		webRouter.get '/tag', AuthenticationController.requireLogin(), TagsController.getAllTags
 		webRouter.post '/project/:project_id/tag', AuthenticationController.requireLogin(), TagsController.processTagsUpdate
 		webRouter.delete '/tag/:tag_id', AuthenticationController.requireLogin(), TagsController.deleteTag
+		webRouter.post '/tag/:tag_id/rename', AuthenticationController.requireLogin(), TagsController.renameTag
 
 		# Deprecated in favour of /internal/project/:project_id but still used by versioning
 		apiRouter.get  '/project/:project_id/details', AuthenticationController.httpAuth, ProjectApiController.getProjectDetails

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

@@ -56,6 +56,35 @@ script(type='text/ng-template', id='deleteTagModalTemplate')
 			span(ng-show="state.inflight") #{translate("deleting")}...
 			span(ng-show="!state.inflight") #{translate("delete")}
 
+script(type='text/ng-template', id='renameTagModalTemplate')
+	.modal-header
+		button.close(
+			type="button"
+			data-dismiss="modal"
+			ng-click="cancel()"
+		) &times;
+		h3 #{translate("rename_tag")}
+	.modal-body
+		form(name="renameTagForm", novalidate)
+			input.form-control(
+				type="text",
+				placeholder="Tag Name",
+				ng-model="inputs.tagName",
+				required,
+				on-enter="rename()",
+				focus-on="open"
+			)
+	.modal-footer
+		.modal-footer-left
+			span.text-danger.error(ng-show="state.error") #{translate("generic_something_went_wrong")}
+		button.btn.btn-default(ng-click="cancel()") #{translate("cancel")}
+		button.btn.btn-primary(
+			ng-click="rename()",
+			ng-disabled="renameTagForm.$invalid || state.inflight"
+		)
+			span(ng-show="!state.inflight") #{translate("rename")}
+			span(ng-show="state.inflight") #{translate("renaming")}...
+
 script(type='text/ng-template', id='renameProjectModalTemplate')
 	.modal-header
 		button.close(

+ 2 - 2
services/web/app/views/project/list/side-bar.jade

@@ -74,10 +74,10 @@
 					role="menu"
 				)
 					li
-						a(href)
+						a(href, ng-click="renameTag(tag)", stop-propagation="click")
 							| #{translate("rename")}
 					li
-						a(href, ng-click="deleteTag(tag)")
+						a(href, ng-click="deleteTag(tag)", stop-propagation="click")
 							| #{translate("delete")}
 		li(ng-cloak)
 			a.tag(href, ng-click="openNewTagModal()")

+ 0 - 42
services/web/public/coffee/main/project-list/modal-controllers.coffee

@@ -1,23 +1,6 @@
 define [
 	"base"
 ], (App) ->
-
-
-	App.controller 'NewTagModalController', ($scope, $modalInstance, $timeout) ->
-		$scope.inputs = 
-			newTagName: ""
-
-		$modalInstance.opened.then () ->
-			$timeout () ->
-				$scope.$broadcast "open"
-			, 200
-
-		$scope.create = () ->
-			$modalInstance.close($scope.inputs.newTagName)
-
-		$scope.cancel = () ->
-			$modalInstance.dismiss('cancel')
-
 	App.controller 'RenameProjectModalController', ($scope, $modalInstance, $timeout, projectName) ->
 		$scope.inputs = 
 			projectName: projectName
@@ -102,28 +85,3 @@ define [
 		$scope.onComplete = (error, name, response) ->
 			if response.project_id?
 				window.location = '/project/' + response.project_id
-
-	App.controller 'DeleteTagModalController', ($scope, $modalInstance, $http, tag) ->
-		$scope.tag = tag
-		$scope.state =
-			inflight: false
-			error: false
-		
-		$scope.delete = () ->
-			$scope.state.inflight = true
-			$scope.state.error = false
-			$http({
-				method: "DELETE"
-				url: "/tag/#{tag._id}"
-				headers:
-					"X-CSRF-Token": window.csrfToken
-			})
-				.success () ->
-					$scope.state.inflight = false
-					$modalInstance.close()
-				.error () ->
-					$scope.state.inflight = false
-					$scope.state.error = true
-		
-		$scope.cancel = () ->
-			$modalInstance.dismiss('cancel')

+ 83 - 0
services/web/public/coffee/main/project-list/tag-controllers.coffee

@@ -25,6 +25,17 @@ define [
 			)
 			modalInstance.result.then () ->
 				$scope.tags = $scope.tags.filter (t) -> t != tag
+		
+		$scope.renameTag = (tag) ->
+			modalInstance = $modal.open(
+				templateUrl: "renameTagModalTemplate"
+				controller: "RenameTagModalController"
+				resolve:
+					tag: () -> tag
+					existing_tags: () -> $scope.tags
+			)
+			modalInstance.result.then (new_name) ->
+				tag.name = new_name
 
 	App.controller "TagDropdownItemController", ($scope) ->
 		$scope.recalculateProjectsInTag = () ->
@@ -49,3 +60,75 @@ define [
 		$scope.$watch "selectedProjects", () ->
 			$scope.recalculateProjectsInTag()
 		$scope.recalculateProjectsInTag()
+	
+	App.controller 'NewTagModalController', ($scope, $modalInstance, $timeout) ->
+		$scope.inputs = 
+			newTagName: ""
+
+		$modalInstance.opened.then () ->
+			$timeout () ->
+				$scope.$broadcast "open"
+			, 200
+
+		$scope.create = () ->
+			$modalInstance.close($scope.inputs.newTagName)
+
+		$scope.cancel = () ->
+			$modalInstance.dismiss('cancel')
+	
+	App.controller 'RenameTagModalController', ($scope, $modalInstance, $timeout, $http, tag, existing_tags) ->
+		$scope.inputs = 
+			tagName: tag.name
+		
+		$scope.state =
+			inflight: false
+			error: false
+
+		$modalInstance.opened.then () ->
+			$timeout () ->
+				$scope.$broadcast "open"
+			, 200
+
+		$scope.rename = () ->
+			name = $scope.inputs.tagName
+			$scope.state.inflight = true
+			$scope.state.error = false
+			$http
+				.post "/tag/#{tag._id}/rename", {
+					_csrf: window.csrfToken,
+					name: name
+				}
+				.success () ->
+					$scope.state.inflight = false
+					$modalInstance.close(name)
+				.error () ->
+					$scope.state.inflight = false
+					$scope.state.error = true
+
+		$scope.cancel = () ->
+			$modalInstance.dismiss('cancel')
+		
+	App.controller 'DeleteTagModalController', ($scope, $modalInstance, $http, tag) ->
+		$scope.tag = tag
+		$scope.state =
+			inflight: false
+			error: false
+		
+		$scope.delete = () ->
+			$scope.state.inflight = true
+			$scope.state.error = false
+			$http({
+				method: "DELETE"
+				url: "/tag/#{tag._id}"
+				headers:
+					"X-CSRF-Token": window.csrfToken
+			})
+				.success () ->
+					$scope.state.inflight = false
+					$modalInstance.close()
+				.error () ->
+					$scope.state.inflight = false
+					$scope.state.error = true
+		
+		$scope.cancel = () ->
+			$modalInstance.dismiss('cancel')

+ 32 - 0
services/web/test/UnitTests/coffee/Tags/TagsControllerTests.coffee

@@ -15,6 +15,7 @@ describe 'Tags controller', ->
 			addTag: sinon.stub().callsArgWith(3)
 			removeProject: sinon.stub().callsArgWith(3)
 			deleteTag: sinon.stub().callsArg(2)
+			renameTag: sinon.stub().callsArg(3)
 		@controller = SandboxedModule.require modulePath, requires:
 			"./TagsHandler":@handler
 			'logger-sharelatex':
@@ -68,3 +69,34 @@ describe 'Tags controller', ->
 		it "should return 204 status code", ->
 			@res.status.calledWith(204).should.equal true
 			@res.end.called.should.equal true
+
+	describe "renameTag", ->
+		beforeEach ->
+			@req.params.tag_id = @tag_id = "tag-id-123"
+			@req.session.user._id = @user_id = "user-id-123"
+
+		describe "with a name", ->
+			beforeEach ->
+				@req.body = name: @name = "new-name"
+				@controller.renameTag @req, @res
+				
+			it "should delete the tag in the backend", ->
+				@handler.renameTag
+					.calledWith(@user_id, @tag_id, @name)
+					.should.equal true
+			
+			it "should return 204 status code", ->
+				@res.status.calledWith(204).should.equal true
+				@res.end.called.should.equal true
+		
+		describe "without a name", ->
+			beforeEach ->
+				@controller.renameTag @req, @res
+				
+			it "should not call the backend", ->
+				@handler.renameTag.called.should.equal false
+			
+			it "should return 400 (bad request) status code", ->
+				@res.status.calledWith(400).should.equal true
+				@res.end.called.should.equal true
+	

+ 25 - 0
services/web/test/UnitTests/coffee/Tags/TagsHandlerTests.coffee

@@ -126,4 +126,29 @@ describe 'TagsHandler', ->
 			
 			it "should call the callback with an Error", ->
 				@callback.calledWith(new Error()).should.equal true
+
+	describe "renameTag", ->
+		describe "successfully", ->
+			beforeEach ->
+				@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 204}, "")
+				@handler.renameTag user_id, tag_id, @name = "new-name", @callback
+			
+			it "should send a request to the tag backend", ->
+				@request.post
+					.calledWith({
+						url: "#{tagsUrl}/user/#{user_id}/tag/#{tag_id}/rename"
+						json:
+							name: @name
+					})
+					.should.equal true
+			
+			it "should call the callback with no error", ->
+				@callback.calledWith(null).should.equal true
 			
+		describe "with error", ->
+			beforeEach ->
+				@request.post = sinon.stub().callsArgWith(1, null, {statusCode: 500}, "")
+				@handler.renameTag user_id, tag_id, "name", @callback
+			
+			it "should call the callback with an Error", ->
+				@callback.calledWith(new Error()).should.equal true