diff --git a/services/web/app/coffee/Features/Editor/EditorHttpController.coffee b/services/web/app/coffee/Features/Editor/EditorHttpController.coffee index 33bd97633f..72426b3ca8 100644 --- a/services/web/app/coffee/Features/Editor/EditorHttpController.coffee +++ b/services/web/app/coffee/Features/Editor/EditorHttpController.coffee @@ -36,3 +36,31 @@ module.exports = EditorHttpController = EditorController.addFolder project_id, parent_folder_id, name, (error, doc) -> return next(error) if error? res.json doc + + renameEntity: (req, res, next) -> + project_id = req.params.Project_id + entity_id = req.params.entity_id + entity_type = req.params.entity_type + name = req.body.name + EditorController.renameEntity project_id, entity_id, entity_type, name, (error) -> + return next(error) if error? + res.send 204 + + moveEntity: (req, res, next) -> + project_id = req.params.Project_id + entity_id = req.params.entity_id + entity_type = req.params.entity_type + folder_id = req.body.folder_id + EditorController.moveEntity project_id, entity_id, folder_id, entity_type, (error) -> + return next(error) if error? + res.send 204 + + deleteEntity: (req, res, next) -> + project_id = req.params.Project_id + entity_id = req.params.entity_id + entity_type = req.params.entity_type + EditorController.deleteEntity project_id, entity_id, entity_type, (error) -> + return next(error) if error? + res.send 204 + + diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 7f5439a3da..47c07ad7fe 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -90,6 +90,10 @@ module.exports = class Router app.post '/project/:Project_id/doc', SecurityManager.requestCanModifyProject, EditorHttpController.addDoc app.post '/project/:Project_id/folder', SecurityManager.requestCanModifyProject, EditorHttpController.addFolder + app.post '/project/:Project_id/:entity_type/:entity_id/rename', SecurityManager.requestCanModifyProject, EditorHttpController.renameEntity + app.post '/project/:Project_id/:entity_type/:entity_id/move', SecurityManager.requestCanModifyProject, EditorHttpController.moveEntity + app.post '/project/:Project_id/:entity_type/:entity_id/delete', SecurityManager.requestCanModifyProject, EditorHttpController.deleteEntity + app.post '/project/:Project_id/compile', SecurityManager.requestCanAccessProject, CompileController.compile app.get '/Project/:Project_id/output/output.pdf', SecurityManager.requestCanAccessProject, CompileController.downloadPdf app.get /^\/project\/([^\/]*)\/output\/(.*)$/, diff --git a/services/web/app/views/project/editor.jade b/services/web/app/views/project/editor.jade index 55df72976b..d196e0316a 100644 --- a/services/web/app/views/project/editor.jade +++ b/services/web/app/views/project/editor.jade @@ -15,7 +15,6 @@ block scripts block content .editor(ng-controller="IdeController") - .loading-screen(ng-show="state.loading") .container h3 Loading... @@ -41,7 +40,7 @@ block content #editor-content(ng-cloak, layout="main", ng-hide="state.loading") include ./editor/file-tree - + .ui-layout-center //- #loadingScreen diff --git a/services/web/app/views/project/editor/file-tree.jade b/services/web/app/views/project/editor/file-tree.jade index 1120f16906..d876c5e6bc 100644 --- a/services/web/app/views/project/editor/file-tree.jade +++ b/services/web/app/views/project/editor/file-tree.jade @@ -23,7 +23,12 @@ aside#file-tree.ui-layout-west(ng-controller="FileTreeController") i.fa.fa-upload .toolbar-right - a(href, tooltip="Rename", tooltip-placement="bottom") + a( + href, + ng-click="startRenamingSelected()", + tooltip="Rename", + tooltip-placement="bottom" + ) i.fa.fa-pencil a(href, tooltip="Delete", tooltip-placement="bottom", tooltip-append-to-body="true") i.fa.fa-trash-o @@ -41,11 +46,23 @@ script(type='text/ng-template', id='entityListItemTemplate') ng-controller="FileTreeEntityController" ) .entity(ng-if="entity.type == 'doc'") - .entity-name(ng-click="select()") + .entity-name( + ng-click="select()" + ng-dblclick="startRenaming()" + ) //- Just a spacer to align with folders i.fa.fa-fw.toggle i.fa.fa-fw.fa-file - | {{ entity.name }} + span( + ng-hide="entity.renaming" + ) {{ entity.name }} + input( + ng-show="entity.renaming", + ng-model="inputs.name", + ng-blur="finishRenaming()", + focus-input="entity.renaming", + on-enter="finishRenaming()" + ) .entity(ng-if="entity.type == 'file'") .entity-name(ng-click="select()") @@ -82,7 +99,7 @@ script(type='text/ng-template', id='newDocModalTemplate') placeholder="File Name", required, ng-model="inputs.name", - ng-enter="create()", + on-enter="create()", focus-on="open" ) .modal-footer @@ -107,7 +124,7 @@ script(type='text/ng-template', id='newFolderModalTemplate') placeholder="Folder Name", required, ng-model="inputs.name", - ng-enter="create()", + on-enter="create()", focus-on="open" ) .modal-footer diff --git a/services/web/app/views/project/list.jade b/services/web/app/views/project/list.jade index 0c103ff9e5..a519c4e3f1 100644 --- a/services/web/app/views/project/list.jade +++ b/services/web/app/views/project/list.jade @@ -312,7 +312,7 @@ block content placeholder="New Folder Name", required, ng-model="inputs.newTagName", - ng-enter="create()", + on-enter="create()", focus-on="open" ) .modal-footer @@ -332,7 +332,7 @@ block content placeholder="Project Name", ng-model="inputs.projectName", required, - ng-enter="rename()", + on-enter="rename()", focus-on="open" ) .modal-footer @@ -354,7 +354,7 @@ block content placeholder="New Project Name", required, ng-model="inputs.projectName", - ng-enter="clone()", + on-enter="clone()", focus-on="open" ) .modal-footer @@ -379,7 +379,7 @@ block content placeholder="Project Name", required, ng-model="inputs.projectName", - ng-enter="create()", + on-enter="create()", focus-on="open" ) .modal-footer diff --git a/services/web/public/coffee/app/directives/onEnter.coffee b/services/web/public/coffee/app/directives/onEnter.coffee new file mode 100644 index 0000000000..459d444b60 --- /dev/null +++ b/services/web/public/coffee/app/directives/onEnter.coffee @@ -0,0 +1,10 @@ +define [ + "base" +], (App) -> + App.directive 'onEnter', () -> + return (scope, element, attrs) -> + element.bind "keydown keypress", (event) -> + if event.which == 13 + scope.$apply () -> + scope.$eval(attrs.onEnter, event: event) + event.preventDefault() \ No newline at end of file diff --git a/services/web/public/coffee/app/ide.coffee b/services/web/public/coffee/app/ide.coffee index 9813673187..a327698710 100644 --- a/services/web/public/coffee/app/ide.coffee +++ b/services/web/public/coffee/app/ide.coffee @@ -4,7 +4,9 @@ define [ "ide/directives/layout" "ide/services/ide" "directives/focusOn" + "directives/focusInput" "directives/fineUpload" + "directives/onEnter" ], ( App FileTreeManager diff --git a/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee b/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee index 433db9e7c7..c1aace3508 100644 --- a/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/app/ide/file-tree/FileTreeManager.coffee @@ -154,3 +154,20 @@ define [ callback(null, folder) failure: (error) -> callback(error) } + + renameEntity: (entity, name, callback = (error) ->) -> + return if entity.name == name + entity.name = name + $.ajax { + url: "/project/#{@ide.project_id}/#{entity.type}/#{entity.id}/rename" + type: "POST" + contentType: "application/json; charset=utf-8" + data: JSON.stringify { + name: name, + _csrf: window.csrfToken + } + dataType: "json" + success: () -> callback() + failure: (error) -> callback(error) + } + diff --git a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee index 72911bf557..0ae8be1bd6 100644 --- a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee +++ b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeController.coffee @@ -24,6 +24,9 @@ define [ $scope.orderByFoldersFirst = (entity) -> return 0 if entity.type == "folder" return 1 + + $scope.startRenamingSelected = () -> + $scope.$broadcast "rename:selected" ] App.controller "NewDocModalController", [ diff --git a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee index b22a14c47a..a0bd84c32f 100644 --- a/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee +++ b/services/web/public/coffee/app/ide/file-tree/controllers/FileTreeEntityController.coffee @@ -2,8 +2,21 @@ define [ "base" ], (App) -> App.controller "FileTreeEntityController", ["$scope", "ide", ($scope, ide) -> - $scope.select = ($event) -> + $scope.select = () -> ide.fileTreeManager.forEachEntity (entity) -> entity.selected = false $scope.entity.selected = true + + $scope.inputs = + name: $scope.entity.name + + $scope.startRenaming = () -> + $scope.entity.renaming = true + + $scope.finishRenaming = () -> + delete $scope.entity.renaming + ide.fileTreeManager.renameEntity($scope.entity, $scope.inputs.name) + + $scope.$on "rename:selected", () -> + $scope.startRenaming() if $scope.entity.selected ] \ No newline at end of file diff --git a/services/web/public/coffee/app/main.coffee b/services/web/public/coffee/app/main.coffee index be794fd018..8da1c9268b 100644 --- a/services/web/public/coffee/app/main.coffee +++ b/services/web/public/coffee/app/main.coffee @@ -8,5 +8,6 @@ define [ "directives/focusOn" "directives/equals" "directives/fineUpload" + "directives/onEnter" ], () -> angular.bootstrap(document.body, ["SharelatexApp"]) \ No newline at end of file diff --git a/services/web/public/coffee/app/project-list.coffee b/services/web/public/coffee/app/project-list.coffee index 8e442dee10..009eea9a5b 100644 --- a/services/web/public/coffee/app/project-list.coffee +++ b/services/web/public/coffee/app/project-list.coffee @@ -1,14 +1,6 @@ define [ "base" ], (App) -> - App.directive 'ngEnter', () -> - return (scope, element, attrs) -> - element.bind "keydown keypress", (event) -> - if event.which == 13 - scope.$apply () -> - scope.$eval(attrs.ngEnter, event: event) - event.preventDefault() - App.filter "formatDate", () -> (date, format = "Do MMM YYYY, h:mm a") -> moment(date).format(format) diff --git a/services/web/public/stylesheets/app/editor.less b/services/web/public/stylesheets/app/editor.less index 35cff2dc31..e131febead 100644 --- a/services/web/public/stylesheets/app/editor.less +++ b/services/web/public/stylesheets/app/editor.less @@ -96,6 +96,9 @@ &:hover { background-color: @gray-lightest; } + input { + line-height: 1.6; + } } i.fa-folder-open, i.fa-folder { diff --git a/services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee b/services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee index f2facd0876..a18bf61d84 100644 --- a/services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Editor/EditorHttpControllerTests.coffee @@ -89,3 +89,57 @@ describe "EditorHttpController", -> .calledWith(@folder) .should.equal true + describe "renameEntity", -> + beforeEach -> + @req.params = + Project_id: @project_id + entity_id: @entity_id = "entity-id-123" + entity_type: @entity_type = "entity-type" + @req.body = + name: @name = "new-name" + @EditorController.renameEntity = sinon.stub().callsArg(4) + @EditorHttpController.renameEntity @req, @res + + it "should call EditorController.renameEntity", -> + @EditorController.renameEntity + .calledWith(@project_id, @entity_id, @entity_type, @name) + .should.equal true + + it "should send back a success response", -> + @res.send.calledWith(204).should.equal true + + describe "moveEntity", -> + beforeEach -> + @req.params = + Project_id: @project_id + entity_id: @entity_id = "entity-id-123" + entity_type: @entity_type = "entity-type" + @req.body = + folder_id: @folder_id = "folder-id-123" + @EditorController.moveEntity = sinon.stub().callsArg(4) + @EditorHttpController.moveEntity @req, @res + + it "should call EditorController.moveEntity", -> + @EditorController.moveEntity + .calledWith(@project_id, @entity_id, @folder_id, @entity_type) + .should.equal true + + it "should send back a success response", -> + @res.send.calledWith(204).should.equal true + + describe "deleteEntity", -> + beforeEach -> + @req.params = + Project_id: @project_id + entity_id: @entity_id = "entity-id-123" + entity_type: @entity_type = "entity-type" + @EditorController.deleteEntity = sinon.stub().callsArg(3) + @EditorHttpController.deleteEntity @req, @res + + it "should call EditorController.deleteEntity", -> + @EditorController.deleteEntity + .calledWith(@project_id, @entity_id, @entity_type) + .should.equal true + + it "should send back a success response", -> + @res.send.calledWith(204).should.equal true