diff --git a/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee b/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee index e7fdc4f3e3..4f5d3cb3d0 100644 --- a/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee +++ b/services/web/app/coffee/Features/Subscription/SubscriptionGroupController.coffee @@ -12,7 +12,7 @@ module.exports = addUserToGroup: (req, res)-> adminUserId = req.session.user._id - newEmail = req.body.email + newEmail = req.body?.email?.toLowerCase()?.trim() logger.log adminUserId:adminUserId, newEmail:newEmail, "adding user to group subscription" SubscriptionGroupHandler.addUserToGroup adminUserId, newEmail, (err, user)-> if err? diff --git a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee index e666b83f24..325df9369f 100644 --- a/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ArchiveManager.coffee @@ -1,6 +1,8 @@ child = require "child_process" logger = require "logger-sharelatex" metrics = require "../../infrastructure/Metrics" +fs = require "fs" +Path = require "path" module.exports = ArchiveManager = extractZipArchive: (source, destination, _callback = (err) ->) -> @@ -34,4 +36,18 @@ module.exports = ArchiveManager = error = new Error(error) logger.error err:error, source: source, destination: destination, "error unzipping file" callback(error) + + findTopLevelDirectory: (directory, callback = (error, topLevelDir) ->) -> + fs.readdir directory, (error, files) -> + return callback(error) if error? + if files.length == 1 + childPath = Path.join(directory, files[0]) + fs.stat childPath, (error, stat) -> + return callback(error) if error? + if stat.isDirectory() + return callback(null, childPath) + else + return callback(null, directory) + else + return callback(null, directory) diff --git a/services/web/app/coffee/Features/Uploads/FileSystemImportManager.coffee b/services/web/app/coffee/Features/Uploads/FileSystemImportManager.coffee index 4f0c4dcc0b..c7e4fbe03a 100644 --- a/services/web/app/coffee/Features/Uploads/FileSystemImportManager.coffee +++ b/services/web/app/coffee/Features/Uploads/FileSystemImportManager.coffee @@ -6,14 +6,28 @@ EditorController = require "../Editor/EditorController" ProjectLocator = require "../Project/ProjectLocator" module.exports = FileSystemImportManager = - addDoc: (project_id, folder_id, name, path, replace, callback = (error, doc)-> )-> + addDoc: (user_id, project_id, folder_id, name, path, replace, callback = (error, doc)-> )-> fs.readFile path, "utf8", (error, content = "") -> return callback(error) if error? content = content.replace(/\r/g, "") lines = content.split("\n") - EditorController.addDocWithoutLock project_id, folder_id, name, lines, "upload", callback + if replace + ProjectLocator.findElement project_id: project_id, element_id: folder_id, type: "folder", (error, folder) -> + return callback(error) if error? + return callback(new Error("Couldn't find folder")) if !folder? + existingDoc = null + for doc in folder.docs + if doc.name == name + existingDoc = doc + break + if existingDoc? + EditorController.setDoc project_id, existingDoc._id, user_id, lines, "upload", callback + else + EditorController.addDocWithoutLock project_id, folder_id, name, lines, "upload", callback + else + EditorController.addDocWithoutLock project_id, folder_id, name, lines, "upload", callback - addFile: (project_id, folder_id, name, path, replace, callback = (error, file)-> )-> + addFile: (user_id, project_id, folder_id, name, path, replace, callback = (error, file)-> )-> if replace ProjectLocator.findElement project_id: project_id, element_id: folder_id, type: "folder", (error, folder) -> return callback(error) if error? @@ -30,14 +44,14 @@ module.exports = FileSystemImportManager = else EditorController.addFileWithoutLock project_id, folder_id, name, path, "upload", callback - addFolder: (project_id, folder_id, name, path, replace, callback = (error)-> ) -> + addFolder: (user_id, project_id, folder_id, name, path, replace, callback = (error)-> ) -> EditorController.addFolderWithoutLock project_id, folder_id, name, "upload", (error, new_folder) => return callback(error) if error? - @addFolderContents project_id, new_folder._id, path, replace, (error) -> + @addFolderContents user_id, project_id, new_folder._id, path, replace, (error) -> return callback(error) if error? callback null, new_folder - addFolderContents: (project_id, parent_folder_id, folderPath, replace, callback = (error)-> ) -> + addFolderContents: (user_id, project_id, parent_folder_id, folderPath, replace, callback = (error)-> ) -> fs.readdir folderPath, (error, entries = []) => return callback(error) if error? jobs = _.map entries, (entry) => @@ -45,21 +59,21 @@ module.exports = FileSystemImportManager = FileTypeManager.shouldIgnore entry, (error, ignore) => return callback(error) if error? if !ignore - @addEntity project_id, parent_folder_id, entry, "#{folderPath}/#{entry}", replace, callback + @addEntity user_id, project_id, parent_folder_id, entry, "#{folderPath}/#{entry}", replace, callback else callback() async.parallelLimit jobs, 5, callback - addEntity: (project_id, folder_id, name, path, replace, callback = (error, entity)-> ) -> + addEntity: (user_id, project_id, folder_id, name, path, replace, callback = (error, entity)-> ) -> FileTypeManager.isDirectory path, (error, isDirectory) => return callback(error) if error? if isDirectory - @addFolder project_id, folder_id, name, path, replace, callback + @addFolder user_id, project_id, folder_id, name, path, replace, callback else FileTypeManager.isBinary name, path, (error, isBinary) => return callback(error) if error? if isBinary - @addFile project_id, folder_id, name, path, replace, callback + @addFile user_id, project_id, folder_id, name, path, replace, callback else - @addDoc project_id, folder_id, name, path, replace, callback + @addDoc user_id, project_id, folder_id, name, path, replace, callback diff --git a/services/web/app/coffee/Features/Uploads/ProjectUploadController.coffee b/services/web/app/coffee/Features/Uploads/ProjectUploadController.coffee index 83a8db5c2d..d2b720e8dc 100644 --- a/services/web/app/coffee/Features/Uploads/ProjectUploadController.coffee +++ b/services/web/app/coffee/Features/Uploads/ProjectUploadController.coffee @@ -34,7 +34,8 @@ module.exports = ProjectUploadController = if !name? or name.length == 0 or name.length > 150 logger.err project_id:project_id, name:name, "bad name when trying to upload file" return res.send success: false - FileSystemImportManager.addEntity project_id, folder_id, name, path, true, (error, entity) -> + user_id = req.session.user._id + FileSystemImportManager.addEntity user_id, project_id, folder_id, name, path, true, (error, entity) -> fs.unlink path, -> timer.done() if error? diff --git a/services/web/app/coffee/Features/Uploads/ProjectUploadManager.coffee b/services/web/app/coffee/Features/Uploads/ProjectUploadManager.coffee index b5d108d21d..127c6bc94f 100644 --- a/services/web/app/coffee/Features/Uploads/ProjectUploadManager.coffee +++ b/services/web/app/coffee/Features/Uploads/ProjectUploadManager.coffee @@ -9,19 +9,21 @@ module.exports = ProjectUploadHandler = createProjectFromZipArchive: (owner_id, name, zipPath, callback = (error, project) ->) -> ProjectCreationHandler.createBlankProject owner_id, name, (error, project) => return callback(error) if error? - @insertZipArchiveIntoFolder project._id, project.rootFolder[0]._id, zipPath, (error) -> + @insertZipArchiveIntoFolder owner_id, project._id, project.rootFolder[0]._id, zipPath, (error) -> return callback(error) if error? ProjectRootDocManager.setRootDocAutomatically project._id, (error) -> return callback(error) if error? callback(error, project) - insertZipArchiveIntoFolder: (project_id, folder_id, path, callback = (error) ->) -> + insertZipArchiveIntoFolder: (owner_id, project_id, folder_id, path, callback = (error) ->) -> destination = @_getDestinationDirectory path ArchiveManager.extractZipArchive path, destination, (error) -> return callback(error) if error? - FileSystemImportManager.addFolderContents project_id, folder_id, destination, false, (error) -> + ArchiveManager.findTopLevelDirectory destination, (error, topLevelDestination) -> return callback(error) if error? - rimraf(destination, callback) + FileSystemImportManager.addFolderContents owner_id, project_id, folder_id, topLevelDestination, false, (error) -> + return callback(error) if error? + rimraf(destination, callback) _getDestinationDirectory: (source) -> return path.join(path.dirname(source), "#{path.basename(source, ".zip")}-#{Date.now()}") diff --git a/services/web/app/views/project/editor/file-tree.jade b/services/web/app/views/project/editor/file-tree.jade index 69b2e2aed3..1bdc248039 100644 --- a/services/web/app/views/project/editor/file-tree.jade +++ b/services/web/app/views/project/editor/file-tree.jade @@ -360,6 +360,17 @@ script(type="text/ng-template", id="uploadFileModalTemplate") .alert.alert-warning.small.modal-alert(ng-if="tooManyFiles") #{translate("maximum_files_uploaded_together", {max:"{{max_files}}"})} .alert.alert-warning.small.modal-alert(ng-if="rateLimitHit") #{translate("too_many_files_uploaded_throttled_short_period")} .alert.alert-warning.small.modal-alert(ng-if="notLoggedIn") #{translate("session_expired_redirecting_to_login", {seconds:"{{secondsToRedirect}}"})} + .alert.alert-warning.small.modal-alert(ng-if="conflicts.length > 0") + p.text-center + | The following files already exist in this project: + ul.text-center.list-unstyled.row-spaced-small + li(ng-repeat="conflict in conflicts"): strong {{ conflict }} + p.text-center.row-spaced-small + | Do you want to overwrite them? + p.text-center + a(href, ng-click="doUpload()").btn.btn-primary Overwrite + |   + a(href, ng-click="cancel()").btn.btn-default Cancel .modal-body( fine-upload @@ -370,10 +381,14 @@ script(type="text/ng-template", id="uploadFileModalTemplate") drag-area-text="{{drag_files}}" hint-text="{{hint_press_and_hold_control_key}}" multiple="true" + auto-upload="false" on-complete-callback="onComplete" on-upload-callback="onUpload" on-validate-batch="onValidateBatch" on-error-callback="onError" + on-submit-callback="onSubmit" + on-cancel-callback="onCancel" + control="control" params="{'folder_id': parent_folder_id}" ) span #{translate("upload_files")} diff --git a/services/web/public/coffee/directives/fineUpload.coffee b/services/web/public/coffee/directives/fineUpload.coffee index 9856da5076..92cdf720f4 100644 --- a/services/web/public/coffee/directives/fineUpload.coffee +++ b/services/web/public/coffee/directives/fineUpload.coffee @@ -16,7 +16,11 @@ define [ onUploadCallback: "=" onValidateBatch: "=" onErrorCallback: "=" + onSubmitCallback: "=" + onCancelCallback: "=" + autoUpload: "=" params: "=" + control: "=" } link: (scope, element, attrs) -> multiple = scope.multiple or false @@ -37,12 +41,19 @@ define [ onUpload = scope.onUploadCallback or () -> onError = scope.onErrorCallback or () -> onValidateBatch = scope.onValidateBatch or () -> + onSubmit = scope.onSubmitCallback or () -> + onCancel = scope.onCancelCallback or () -> + if !scope.autoUpload? + autoUpload = true + else + autoUpload = scope.autoUpload params = scope.params or {} params._csrf = window.csrfToken q = new qq.FineUploader element: element[0] multiple: multiple + autoUpload: autoUpload disabledCancelForFormUploads: true validation: validation maxConnections: maxConnections @@ -56,6 +67,8 @@ define [ onUpload: onUpload onValidateBatch: onValidateBatch onError: onError + onSubmit: onSubmit + onCancel: onCancel text: text template: """
@@ -70,5 +83,7 @@ define [
""" + window.q = q + scope.control?.q = q return q } \ No newline at end of file diff --git a/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee b/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee index c93ed4f4c0..8c49d54c23 100644 --- a/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee +++ b/services/web/public/coffee/ide/file-tree/FileTreeManager.coffee @@ -135,6 +135,12 @@ define [ multiSelectSelectedEntity: () -> @findSelectedEntity()?.multiSelected = true + existsInFolder: (folder_id, name) -> + folder = @findEntityById(folder_id) + return false if !folder? + entity = @_findEntityByPathInFolder(folder, name) + return entity? + findSelectedEntity: () -> selected = null @forEachEntity (entity) -> diff --git a/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee b/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee index f3bdc3c9e6..17ac9da8dd 100644 --- a/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee +++ b/services/web/public/coffee/ide/file-tree/controllers/FileTreeController.coffee @@ -106,7 +106,8 @@ define [ $scope.rateLimitHit = false $scope.secondsToRedirect = 10 $scope.notLoggedIn = false - + $scope.conflicts = [] + $scope.control = {} needToLogBackIn = -> $scope.notLoggedIn = true @@ -121,11 +122,6 @@ define [ decreseTimeout() - - uploadCount = 0 - $scope.onUpload = () -> - uploadCount++ - $scope.max_files = 40 $scope.onComplete = (error, name, response) -> $timeout (() -> @@ -150,6 +146,34 @@ define [ else if reason.indexOf("403") != -1 needToLogBackIn() + _uploadTimer = null + uploadIfNoConflicts = () -> + if $scope.conflicts.length == 0 + $scope.doUpload() + + uploadCount = 0 + $scope.onSubmit = (id, name) -> + uploadCount++ + if ide.fileTreeManager.existsInFolder($scope.parent_folder_id, name) + $scope.conflicts.push name + $scope.$apply() + if !_uploadTimer? + _uploadTimer = setTimeout () -> + _uploadTimer = null + uploadIfNoConflicts() + , 0 + return true + + $scope.onCancel = (id, name) -> + uploadCount-- + index = $scope.conflicts.indexOf(name) + if index > -1 + $scope.conflicts.splice(index, 1) + $scope.$apply() + uploadIfNoConflicts() + + $scope.doUpload = () -> + $scope.control?.q?.uploadStoredFiles() $scope.cancel = () -> $modalInstance.dismiss('cancel') diff --git a/services/web/public/stylesheets/app/editor/toolbar.less b/services/web/public/stylesheets/app/editor/toolbar.less index bc1976764f..f83135972f 100644 --- a/services/web/public/stylesheets/app/editor/toolbar.less +++ b/services/web/public/stylesheets/app/editor/toolbar.less @@ -95,15 +95,16 @@ &.toolbar-small { height: 32px; > a, .toolbar-right > a { - padding: 4px 2px 2px; + padding: 2px 4px 1px 4px; margin: 0; + margin-top: 2px; } > a { - margin-left: 6px; + margin-left: 2px; } .toolbar-right > a { margin-left: 0; - margin-right: 6px; + margin-right: 2px; } } diff --git a/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupControllerTests.coffee b/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupControllerTests.coffee index dbaf751c70..455c441239 100644 --- a/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Subscription/SubscriptionGroupControllerTests.coffee @@ -54,11 +54,11 @@ describe "SubscriptionGroupController", -> describe "addUserToGroup", -> it "should use the admin id for the logged in user and take the email address from the body", (done)-> - newEmail = "31231" + newEmail = " boB@gmaiL.com " @req.body = email: newEmail res = json : (data)=> - @GroupHandler.addUserToGroup.calledWith(@adminUserId, newEmail).should.equal true + @GroupHandler.addUserToGroup.calledWith(@adminUserId, "bob@gmail.com").should.equal true data.user.should.deep.equal @user done() @Controller.addUserToGroup @req, res diff --git a/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee b/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee index d1f72a780e..079ab6d099 100644 --- a/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Uploads/ArchiveManagerTests.coffee @@ -22,6 +22,7 @@ describe "ArchiveManager", -> "child_process": @child "logger-sharelatex": @logger "../../infrastructure/Metrics": @metrics + "fs": @fs = {} describe "extractZipArchive", -> beforeEach -> @@ -69,4 +70,57 @@ describe "ArchiveManager", -> it "should log out the error", -> @logger.error.called.should.equal true + + describe "findTopLevelDirectory", -> + beforeEach -> + @fs.readdir = sinon.stub() + @fs.stat = sinon.stub() + @directory = "test/directory" + + describe "with multiple files", -> + beforeEach -> + @fs.readdir.callsArgWith(1, null, ["multiple", "files"]) + @ArchiveManager.findTopLevelDirectory(@directory, @callback) + + it "should find the files in the directory", -> + @fs.readdir + .calledWith(@directory) + .should.equal true + + it "should return the original directory", -> + @callback + .calledWith(null, @directory) + .should.equal true + + describe "with a single file (not folder)", -> + beforeEach -> + @fs.readdir.callsArgWith(1, null, ["foo.tex"]) + @fs.stat.callsArgWith(1, null, { isDirectory: () -> false }) + @ArchiveManager.findTopLevelDirectory(@directory, @callback) + + it "should check if the file is a directory", -> + @fs.stat + .calledWith(@directory + "/foo.tex") + .should.equal true + + it "should return the original directory", -> + @callback + .calledWith(null, @directory) + .should.equal true + + describe "with a single top-level folder", -> + beforeEach -> + @fs.readdir.callsArgWith(1, null, ["folder"]) + @fs.stat.callsArgWith(1, null, { isDirectory: () -> true }) + @ArchiveManager.findTopLevelDirectory(@directory, @callback) + + it "should check if the file is a directory", -> + @fs.stat + .calledWith(@directory + "/folder") + .should.equal true + + it "should return the child directory", -> + @callback + .calledWith(null, @directory + "/folder") + .should.equal true diff --git a/services/web/test/UnitTests/coffee/Uploads/FileSystemImportManagerTests.coffee b/services/web/test/UnitTests/coffee/Uploads/FileSystemImportManagerTests.coffee index fa385f7261..441dd8163d 100644 --- a/services/web/test/UnitTests/coffee/Uploads/FileSystemImportManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Uploads/FileSystemImportManagerTests.coffee @@ -11,43 +11,94 @@ describe "FileSystemImportManager", -> @name = "test-file.tex" @path_on_disk = "/path/to/file/#{@name}" @replace = "replace-boolean-flag-mock" + @user_id = "mock-user-123" + @callback = sinon.stub() @FileSystemImportManager = SandboxedModule.require modulePath, requires: "fs" : @fs = {} "../Editor/EditorController": @EditorController = {} "./FileTypeManager": @FileTypeManager = {} "../Project/ProjectLocator": @ProjectLocator = {} - + describe "addDoc", -> beforeEach -> @docContent = "one\ntwo\nthree" @docLines = @docContent.split("\n") @fs.readFile = sinon.stub().callsArgWith(2, null, @docContent) - @EditorController.addDocWithoutLock = sinon.stub().callsArg(5) - @FileSystemImportManager.addDoc @project_id, @folder_id, @name, @path_on_disk, false, @callback - it "should read the file from disk", -> - @fs.readFile.calledWith(@path_on_disk, "utf8").should.equal true + describe "with replace set to false", -> + beforeEach -> + @EditorController.addDocWithoutLock = sinon.stub().callsArg(5) + @FileSystemImportManager.addDoc @user_id, @project_id, @folder_id, @name, @path_on_disk, false, @callback - it "should insert the doc", -> - @EditorController.addDocWithoutLock.calledWith(@project_id, @folder_id, @name, @docLines, "upload") - .should.equal true + it "should read the file from disk", -> + @fs.readFile.calledWith(@path_on_disk, "utf8").should.equal true - describe "addDoc with windows line ending", -> - beforeEach -> - @docContent = "one\r\ntwo\r\nthree" - @docLines = ["one", "two", "three"] - @fs.readFile = sinon.stub().callsArgWith(2, null, @docContent) - @EditorController.addDocWithoutLock = sinon.stub().callsArg(5) - @FileSystemImportManager.addDoc @project_id, @folder_id, @name, @path_on_disk, false, @callback + it "should insert the doc", -> + @EditorController.addDocWithoutLock.calledWith(@project_id, @folder_id, @name, @docLines, "upload") + .should.equal true - it "should strip the \\r characters before adding", -> - @EditorController.addDocWithoutLock.calledWith(@project_id, @folder_id, @name, @docLines, "upload") - .should.equal true + describe "with windows line ending", -> + beforeEach -> + @docContent = "one\r\ntwo\r\nthree" + @docLines = ["one", "two", "three"] + @fs.readFile = sinon.stub().callsArgWith(2, null, @docContent) + @EditorController.addDocWithoutLock = sinon.stub().callsArg(5) + @FileSystemImportManager.addDoc @user_id, @project_id, @folder_id, @name, @path_on_disk, false, @callback + + it "should strip the \\r characters before adding", -> + @EditorController.addDocWithoutLock.calledWith(@project_id, @folder_id, @name, @docLines, "upload") + .should.equal true + + describe "with replace set to true", -> + describe "when the doc doesn't exist", -> + beforeEach -> + @folder = { + docs: [{ + _id: "doc-id-2" + name: "not-the-right-file.tex" + }] + } + @ProjectLocator.findElement = sinon.stub().callsArgWith(1, null, @folder) + @EditorController.addDocWithoutLock = sinon.stub().callsArg(5) + @FileSystemImportManager.addDoc @user_id, @project_id, @folder_id, @name, @path_on_disk, true, @callback + + it "should look up the folder", -> + @ProjectLocator.findElement + .calledWith(project_id: @project_id, element_id: @folder_id, type: "folder") + .should.equal true + + it "should insert the doc", -> + @EditorController.addDocWithoutLock.calledWith(@project_id, @folder_id, @name, @docLines, "upload") + .should.equal true + + describe "when the doc does exist", -> + beforeEach -> + @folder = { + docs: [{ + _id: @doc_id = "doc-id-1" + name: @name + }, { + _id: "doc-id-2" + name: "not-the-right-file.tex" + }] + } + @ProjectLocator.findElement = sinon.stub().callsArgWith(1, null, @folder) + @EditorController.setDoc = sinon.stub().callsArg(5) + @FileSystemImportManager.addDoc @user_id, @project_id, @folder_id, @name, @path_on_disk, true, @callback + + it "should look up the folder", -> + @ProjectLocator.findElement + .calledWith(project_id: @project_id, element_id: @folder_id, type: "folder") + .should.equal true + + it "should set the doc with the new doc lines", -> + @EditorController.setDoc.calledWith(@project_id, @doc_id, @user_id, @docLines, "upload") + .should.equal true describe "addFile with replace set to false", -> beforeEach -> @EditorController.addFileWithoutLock = sinon.stub().callsArg(5) - @FileSystemImportManager.addFile @project_id, @folder_id, @name, @path_on_disk, false, @callback + @FileSystemImportManager.addFile @user_id, @project_id, @folder_id, @name, @path_on_disk, false, @callback it "should add the file", -> @EditorController.addFileWithoutLock.calledWith(@project_id, @folder_id, @name, @path_on_disk, "upload") @@ -64,7 +115,7 @@ describe "FileSystemImportManager", -> } @ProjectLocator.findElement = sinon.stub().callsArgWith(1, null, @folder) @EditorController.addFileWithoutLock = sinon.stub().callsArg(5) - @FileSystemImportManager.addFile @project_id, @folder_id, @name, @path_on_disk, true, @callback + @FileSystemImportManager.addFile @user_id, @project_id, @folder_id, @name, @path_on_disk, true, @callback it "should look up the folder", -> @ProjectLocator.findElement @@ -88,7 +139,7 @@ describe "FileSystemImportManager", -> } @ProjectLocator.findElement = sinon.stub().callsArgWith(1, null, @folder) @EditorController.replaceFile = sinon.stub().callsArg(4) - @FileSystemImportManager.addFile @project_id, @folder_id, @name, @path_on_disk, true, @callback + @FileSystemImportManager.addFile @user_id, @project_id, @folder_id, @name, @path_on_disk, true, @callback it "should look up the folder", -> @ProjectLocator.findElement @@ -103,15 +154,15 @@ describe "FileSystemImportManager", -> beforeEach -> @new_folder_id = "new-folder-id" @EditorController.addFolderWithoutLock = sinon.stub().callsArgWith(4, null, _id: @new_folder_id) - @FileSystemImportManager.addFolderContents = sinon.stub().callsArg(4) - @FileSystemImportManager.addFolder @project_id, @folder_id, @name, @path_on_disk, @replace, @callback + @FileSystemImportManager.addFolderContents = sinon.stub().callsArg(5) + @FileSystemImportManager.addFolder @user_id, @project_id, @folder_id, @name, @path_on_disk, @replace, @callback it "should add a folder to the project", -> @EditorController.addFolderWithoutLock.calledWith(@project_id, @folder_id, @name, "upload") .should.equal true it "should add the folders contents", -> - @FileSystemImportManager.addFolderContents.calledWith(@project_id, @new_folder_id, @path_on_disk, @replace) + @FileSystemImportManager.addFolderContents.calledWith(@user_id, @project_id, @new_folder_id, @path_on_disk, @replace) .should.equal true describe "addFolderContents", -> @@ -119,19 +170,19 @@ describe "FileSystemImportManager", -> @folderEntries = ["path1", "path2", "path3"] @ignoredEntries = [".DS_Store"] @fs.readdir = sinon.stub().callsArgWith(1, null, @folderEntries.concat @ignoredEntries) - @FileSystemImportManager.addEntity = sinon.stub().callsArg(5) + @FileSystemImportManager.addEntity = sinon.stub().callsArg(6) @FileTypeManager.shouldIgnore = (path, callback) => callback null, @ignoredEntries.indexOf(require("path").basename(path)) != -1 - @FileSystemImportManager.addFolderContents @project_id, @folder_id, @path_on_disk, @replace, @callback + @FileSystemImportManager.addFolderContents @user_id, @project_id, @folder_id, @path_on_disk, @replace, @callback it "should call addEntity for each file in the folder which is not ignored", -> for name in @folderEntries - @FileSystemImportManager.addEntity.calledWith(@project_id, @folder_id, name, "#{@path_on_disk}/#{name}", @replace) + @FileSystemImportManager.addEntity.calledWith(@user_id, @project_id, @folder_id, name, "#{@path_on_disk}/#{name}", @replace) .should.equal true it "should not call addEntity for the ignored files", -> for name in @ignoredEntries - @FileSystemImportManager.addEntity.calledWith(@project_id, @folder_id, name, "#{@path_on_disk}/#{name}", @replace) + @FileSystemImportManager.addEntity.calledWith(@user_id, @project_id, @folder_id, name, "#{@path_on_disk}/#{name}", @replace) .should.equal false it "should look in the correct directory", -> @@ -141,33 +192,33 @@ describe "FileSystemImportManager", -> describe "with directory", -> beforeEach -> @FileTypeManager.isDirectory = sinon.stub().callsArgWith(1, null, true) - @FileSystemImportManager.addFolder = sinon.stub().callsArg(5) - @FileSystemImportManager.addEntity @project_id, @folder_id, @name, @path_on_disk, @replace, @callback + @FileSystemImportManager.addFolder = sinon.stub().callsArg(6) + @FileSystemImportManager.addEntity @user_id, @project_id, @folder_id, @name, @path_on_disk, @replace, @callback it "should call addFolder", -> - @FileSystemImportManager.addFolder.calledWith(@project_id, @folder_id, @name, @path_on_disk, @replace, @callback) + @FileSystemImportManager.addFolder.calledWith(@user_id, @project_id, @folder_id, @name, @path_on_disk, @replace) .should.equal true describe "with binary file", -> beforeEach -> @FileTypeManager.isDirectory = sinon.stub().callsArgWith(1, null, false) @FileTypeManager.isBinary = sinon.stub().callsArgWith(2, null, true) - @FileSystemImportManager.addFile = sinon.stub().callsArg(5) - @FileSystemImportManager.addEntity @project_id, @folder_id, @name, @path_on_disk, @replace, @callback + @FileSystemImportManager.addFile = sinon.stub().callsArg(6) + @FileSystemImportManager.addEntity @user_id, @project_id, @folder_id, @name, @path_on_disk, @replace, @callback it "should call addFile", -> - @FileSystemImportManager.addFile.calledWith(@project_id, @folder_id, @name, @path_on_disk, @replace, @callback) + @FileSystemImportManager.addFile.calledWith(@user_id, @project_id, @folder_id, @name, @path_on_disk, @replace, @callback) .should.equal true describe "with text file", -> beforeEach -> @FileTypeManager.isDirectory = sinon.stub().callsArgWith(1, null, false) @FileTypeManager.isBinary = sinon.stub().callsArgWith(2, null, false) - @FileSystemImportManager.addDoc = sinon.stub().callsArg(5) - @FileSystemImportManager.addEntity @project_id, @folder_id, @name, @path_on_disk, @replace, @callback + @FileSystemImportManager.addDoc = sinon.stub().callsArg(6) + @FileSystemImportManager.addEntity @user_id, @project_id, @folder_id, @name, @path_on_disk, @replace, @callback it "should call addFile", -> - @FileSystemImportManager.addDoc.calledWith(@project_id, @folder_id, @name, @path_on_disk, @replace, @callback) + @FileSystemImportManager.addDoc.calledWith(@user_id, @project_id, @folder_id, @name, @path_on_disk, @replace, @callback) .should.equal true diff --git a/services/web/test/UnitTests/coffee/Uploads/ProjectUploadControllerTests.coffee b/services/web/test/UnitTests/coffee/Uploads/ProjectUploadControllerTests.coffee index dd4240d1b8..176ae96d24 100644 --- a/services/web/test/UnitTests/coffee/Uploads/ProjectUploadControllerTests.coffee +++ b/services/web/test/UnitTests/coffee/Uploads/ProjectUploadControllerTests.coffee @@ -103,6 +103,9 @@ describe "ProjectUploadController", -> qqfile: path: @path originalname: @name + @req.session = + user: + _id: @user_id @req.params = Project_id: @project_id @req.query = @@ -115,27 +118,12 @@ describe "ProjectUploadController", -> beforeEach -> @entity = _id : "1234" - @FileSystemImportManager.addEntity = sinon.stub().callsArgWith(5, null, @entity) + @FileSystemImportManager.addEntity = sinon.stub().callsArgWith(6, null, @entity) @ProjectUploadController.uploadFile @req, @res - it "should insert the file into the correct project", -> + it "should insert the file", -> @FileSystemImportManager.addEntity - .calledWith(@project_id) - .should.equal true - - it "should insert the file into the provided folder", -> - @FileSystemImportManager.addEntity - .calledWith(sinon.match.any, @folder_id) - .should.equal true - - it "should insert the file with the correct name", -> - @FileSystemImportManager.addEntity - .calledWith(sinon.match.any, sinon.match.any, @name) - .should.equal true - - it "should insert the file from the uploaded path", -> - @FileSystemImportManager.addEntity - .calledWith(sinon.match.any, sinon.match.any, sinon.match.any, @path) + .calledWith(@user_id, @project_id, @folder_id, @name, @path) .should.equal true it "should return a successful response to the FileUploader client", -> @@ -143,7 +131,6 @@ describe "ProjectUploadController", -> success: true entity_id: @entity._id - it "should output a log line", -> @logger.log .calledWith(sinon.match.any, "uploaded file") @@ -158,7 +145,7 @@ describe "ProjectUploadController", -> describe "when FileSystemImportManager.addEntity returns an error", -> beforeEach -> @FileSystemImportManager.addEntity = sinon.stub() - .callsArgWith(5, new Error("Sorry something went wrong")) + .callsArgWith(6, new Error("Sorry something went wrong")) @ProjectUploadController.uploadFile @req, @res it "should return an unsuccessful response to the FileUploader client", -> diff --git a/services/web/test/UnitTests/coffee/Uploads/ProjectUploadManagerTests.coffee b/services/web/test/UnitTests/coffee/Uploads/ProjectUploadManagerTests.coffee index 53b7cb2064..d4571f0474 100644 --- a/services/web/test/UnitTests/coffee/Uploads/ProjectUploadManagerTests.coffee +++ b/services/web/test/UnitTests/coffee/Uploads/ProjectUploadManagerTests.coffee @@ -8,6 +8,7 @@ describe "ProjectUploadManager", -> beforeEach -> @project_id = "project-id-123" @folder_id = "folder-id-123" + @owner_id = "onwer-id-123" @callback = sinon.stub() @ProjectUploadManager = SandboxedModule.require modulePath, requires: "./FileSystemImportManager" : @FileSystemImportManager = {} @@ -26,7 +27,7 @@ describe "ProjectUploadManager", -> _id: @project_id rootFolder: [ _id: @root_folder_id ] @ProjectCreationHandler.createBlankProject = sinon.stub().callsArgWith(2, null, @project) - @ProjectUploadManager.insertZipArchiveIntoFolder = sinon.stub().callsArg(3) + @ProjectUploadManager.insertZipArchiveIntoFolder = sinon.stub().callsArg(4) @ProjectRootDocManager.setRootDocAutomatically = sinon.stub().callsArg(1) @ProjectUploadManager.createProjectFromZipArchive @owner_id, @name, @source, @callback @@ -45,7 +46,7 @@ describe "ProjectUploadManager", -> it "should insert the zip file contents into the root folder", -> @ProjectUploadManager .insertZipArchiveIntoFolder - .calledWith(@project_id, @root_folder_id, @source) + .calledWith(@owner_id, @project_id, @root_folder_id, @source) .should.equal true it "should automatically set the root doc", -> @@ -63,9 +64,10 @@ describe "ProjectUploadManager", -> @destination = "/path/to/zile/file-extracted" @ProjectUploadManager._getDestinationDirectory = sinon.stub().returns @destination @ArchiveManager.extractZipArchive = sinon.stub().callsArg(2) - @FileSystemImportManager.addFolderContents = sinon.stub().callsArg(4) + @ArchiveManager.findTopLevelDirectory = sinon.stub().callsArgWith(1, null, @topLevelDestination = "/path/to/zip/file-extracted/nested") + @FileSystemImportManager.addFolderContents = sinon.stub().callsArg(5) - @ProjectUploadManager.insertZipArchiveIntoFolder @project_id, @folder_id, @source, @callback + @ProjectUploadManager.insertZipArchiveIntoFolder @owner_id, @project_id, @folder_id, @source, @callback it "should set up the directory to extract the archive to", -> @ProjectUploadManager._getDestinationDirectory.calledWith(@source).should.equal true @@ -73,8 +75,11 @@ describe "ProjectUploadManager", -> it "should extract the archive", -> @ArchiveManager.extractZipArchive.calledWith(@source, @destination).should.equal true + it "should find the top level directory", -> + @ArchiveManager.findTopLevelDirectory.calledWith(@destination).should.equal true + it "should insert the extracted archive into the folder", -> - @FileSystemImportManager.addFolderContents.calledWith(@project_id, @folder_id, @destination, false) + @FileSystemImportManager.addFolderContents.calledWith(@owner_id, @project_id, @folder_id, @topLevelDestination, false) .should.equal true it "should return the callback", ->