diff --git a/services/web/app/coffee/Features/LinkedFiles/ProjectFileAgent.coffee b/services/web/app/coffee/Features/LinkedFiles/ProjectFileAgent.coffee index 9555d0ee4f..ed83379a60 100644 --- a/services/web/app/coffee/Features/LinkedFiles/ProjectFileAgent.coffee +++ b/services/web/app/coffee/Features/LinkedFiles/ProjectFileAgent.coffee @@ -81,6 +81,7 @@ module.exports = ProjectFileAgent = { ProjectLocator.findElementByPath { project_id: source_project_id, path: source_entity_path + exactCaseMatch: true }, (err, entity, type) -> if err? if /^not found.*/.test(err.toString()) diff --git a/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee index c25591706e..80d4795d22 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityMongoUpdateHandler.coffee @@ -71,7 +71,9 @@ module.exports = ProjectEntityMongoUpdateHandler = self = return callback(err) if err? callback null, fileRef, project, path - mkdirp: wrapWithLock (project_id, path, callback) -> + mkdirp: wrapWithLock (project_id, path, options, callback) -> + # defaults to case insensitive paths, use options {exactCaseMatch:true} + # to make matching case-sensitive folders = path.split('/') folders = _.select folders, (folder)-> return folder.length != 0 @@ -89,7 +91,7 @@ module.exports = ProjectEntityMongoUpdateHandler = self = if parentFolder? parentFolder_id = parentFolder._id builtUpPath = "#{builtUpPath}/#{folderName}" - ProjectLocator.findElementByPath project: project, path: builtUpPath, (err, foundFolder)=> + ProjectLocator.findElementByPath project: project, path: builtUpPath, exactCaseMatch: options?.exactCaseMatch, (err, foundFolder)=> if !foundFolder? logger.log path:path, project_id:project._id, folderName:folderName, "making folder from mkdirp" self.addFolder.withoutLock project_id, parentFolder_id, folderName, (err, newFolder, parentFolder_id)-> diff --git a/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee b/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee index def34bcf45..1d49b47934 100644 --- a/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee +++ b/services/web/app/coffee/Features/Project/ProjectEntityUpdateHandler.coffee @@ -350,7 +350,13 @@ module.exports = ProjectEntityUpdateHandler = self = for folder in path.split('/') if folder.length > 0 and not SafePath.isCleanFilename folder return callback new Errors.InvalidNameError("invalid element name") - ProjectEntityMongoUpdateHandler.mkdirp project_id, path, callback + ProjectEntityMongoUpdateHandler.mkdirp project_id, path, {exactCaseMatch: false}, callback + + mkdirpWithExactCase: wrapWithLock (project_id, path, callback = (err, newlyCreatedFolders, lastFolderInPath)->)-> + for folder in path.split('/') + if folder.length > 0 and not SafePath.isCleanFilename folder + return callback new Errors.InvalidNameError("invalid element name") + ProjectEntityMongoUpdateHandler.mkdirp project_id, path, {exactCaseMatch: true}, callback addFolder: wrapWithLock (project_id, parentFolder_id, folderName, callback) -> if not SafePath.isCleanFilename folderName diff --git a/services/web/app/coffee/Features/Project/ProjectLocator.coffee b/services/web/app/coffee/Features/Project/ProjectLocator.coffee index 80de0cb253..c23faeff90 100644 --- a/services/web/app/coffee/Features/Project/ProjectLocator.coffee +++ b/services/web/app/coffee/Features/Project/ProjectLocator.coffee @@ -83,25 +83,30 @@ module.exports = ProjectLocator = getRootDoc project findElementByPath: (options, callback = (err, foundEntity, type)->)-> - {project, project_id, path} = options + {project, project_id, path, exactCaseMatch} = options if !path? return new Error('no path provided for findElementByPath') if project? - ProjectLocator._findElementByPathWithProject project, path, callback + ProjectLocator._findElementByPathWithProject project, path, exactCaseMatch, callback else ProjectGetter.getProject project_id, {rootFolder:true, rootDoc_id:true}, (err, project)-> return callback(err) if err? - ProjectLocator._findElementByPathWithProject project, path, callback + ProjectLocator._findElementByPathWithProject project, path, exactCaseMatch, callback + + _findElementByPathWithProject: (project, needlePath, exactCaseMatch, callback = (err, foundEntity, type)->)-> + if exactCaseMatch + matchFn = (a, b) -> (a == b) + else + matchFn = (a, b) -> (a?.toLowerCase() == b?.toLowerCase()) - _findElementByPathWithProject: (project, needlePath, callback = (err, foundEntity, type)->)-> getParentFolder = (haystackFolder, foldersList, level, cb)-> if foldersList.length == 0 return cb null, haystackFolder needleFolderName = foldersList[level] found = false for folder in haystackFolder.folders - if folder.name.toLowerCase() == needleFolderName.toLowerCase() + if matchFn(folder.name, needleFolderName) found = true if level == foldersList.length-1 return cb null, folder @@ -114,15 +119,15 @@ module.exports = ProjectLocator = if !entityName? return cb null, folder, "folder" for file in folder.fileRefs or [] - if file?.name.toLowerCase() == entityName.toLowerCase() + if matchFn(file?.name, entityName) result = file type = "file" for doc in folder.docs or [] - if doc?.name.toLowerCase() == entityName.toLowerCase() + if matchFn(doc?.name, entityName) result = doc type = "doc" for childFolder in folder.folders or [] - if childFolder?.name.toLowerCase() == entityName.toLowerCase() + if matchFn(childFolder?.name, entityName) result = childFolder type = "folder" diff --git a/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee index 41505a2176..7dadc7dbb2 100644 --- a/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectEntityMongoUpdateHandlerTests.coffee @@ -155,7 +155,8 @@ describe 'ProjectEntityMongoUpdateHandler', -> @project = _id: project_id, rootFolder: [@rootFolder] @ProjectGetter.getProjectWithOnlyFolders = sinon.stub().yields(null, @project) - @ProjectLocator.findElementByPath = (options, cb) => + @ProjectLocator.findElementByPath = -> + sinon.stub @ProjectLocator, "findElementByPath", (options, cb) => {path} = options @parentFolder = {_id:"parentFolder_id_here"} lastFolder = path.substring(path.lastIndexOf("/")) @@ -169,14 +170,14 @@ describe 'ProjectEntityMongoUpdateHandler', -> it 'should return the root folder if the path is just a slash', (done)-> path = "/" - @subject.mkdirp project_id, path, (err, folders, lastFolder)=> + @subject.mkdirp project_id, path, {}, (err, folders, lastFolder)=> lastFolder.should.deep.equal @rootFolder assert.equal lastFolder.parentFolder_id, undefined done() it 'should make just one folder', (done)-> path = "/differentFolder/" - @subject.mkdirp project_id, path, (err, folders, lastFolder)=> + @subject.mkdirp project_id, path, {}, (err, folders, lastFolder)=> folders.length.should.equal 1 lastFolder.name.should.equal "differentFolder" lastFolder.parentFolder_id.should.equal @parentFolder_id @@ -184,7 +185,7 @@ describe 'ProjectEntityMongoUpdateHandler', -> it 'should make the final folder in path if it doesnt exist with one level', (done)-> path = "level1/level2" - @subject.mkdirp project_id, path, (err, folders, lastFolder)=> + @subject.mkdirp project_id, path, {}, (err, folders, lastFolder)=> folders.length.should.equal 1 lastFolder.name.should.equal "level2" lastFolder.parentFolder_id.should.equal @parentFolder_id @@ -193,7 +194,7 @@ describe 'ProjectEntityMongoUpdateHandler', -> it 'should make the final folder in path if it doesnt exist with mutliple levels', (done)-> path = "level1/level2/level3" - @subject.mkdirp project_id, path,(err, folders, lastFolder) => + @subject.mkdirp project_id, path, {}, (err, folders, lastFolder) => folders.length.should.equal 2 folders[0].name.should.equal "level2" folders[0].parentFolder_id.should.equal @parentFolder_id @@ -204,7 +205,7 @@ describe 'ProjectEntityMongoUpdateHandler', -> it 'should work with slashes either side', (done)-> path = "/level1/level2/level3/" - @subject.mkdirp project_id, path, (err, folders, lastFolder)=> + @subject.mkdirp project_id, path, {}, (err, folders, lastFolder)=> folders.length.should.equal 2 folders[0].name.should.equal "level2" folders[0].parentFolder_id.should.equal @parentFolder_id @@ -212,6 +213,20 @@ describe 'ProjectEntityMongoUpdateHandler', -> lastFolder.parentFolder_id.should.equal @parentFolder_id done() + it 'should use a case-insensitive match by default', (done)-> + path = "/differentFolder/" + @subject.mkdirp project_id, path, {}, (err, folders, lastFolder)=> + @ProjectLocator.findElementByPath.calledWithMatch({exactCaseMatch:undefined}) + .should.equal true + done() + + it 'should use a case-sensitive match if exactCaseMatch option is set', (done)-> + path = "/differentFolder/" + @subject.mkdirp project_id, path, {exactCaseMatch:true}, (err, folders, lastFolder)=> + @ProjectLocator.findElementByPath.calledWithMatch({exactCaseMatch:true}) + .should.equal true + done() + describe 'moveEntity', -> beforeEach -> @pathAfterMove = { diff --git a/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee b/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee index 2d73c7e6a0..46403328a7 100644 --- a/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee +++ b/services/web/test/unit/coffee/Project/ProjectEntityUpdateHandlerTests.coffee @@ -776,6 +776,17 @@ describe 'ProjectEntityUpdateHandler', -> .calledWith(project_id, @docPath) .should.equal true + describe 'mkdirpWithExactCase', -> + beforeEach -> + @docPath = '/folder/doc.tex' + @ProjectEntityMongoUpdateHandler.mkdirp = sinon.stub().yields() + @ProjectEntityUpdateHandler.mkdirpWithExactCase project_id, @docPath, @callback + + it 'calls ProjectEntityMongoUpdateHandler', -> + @ProjectEntityMongoUpdateHandler.mkdirp + .calledWith(project_id, @docPath, {exactCaseMatch: true}) + .should.equal true + describe 'addFolder', -> describe 'adding a folder', -> beforeEach ->