ProjectLocator.coffee 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. Project = require('../../models/Project').Project
  2. ProjectGetter = require("./ProjectGetter")
  3. Errors = require "../Errors/Errors"
  4. _ = require('underscore')
  5. logger = require('logger-sharelatex')
  6. async = require('async')
  7. module.exports = ProjectLocator =
  8. findElement: (options, _callback = (err, element, path, parentFolder)->)->
  9. callback = (args...) ->
  10. _callback(args...)
  11. _callback = () ->
  12. {project, project_id, element_id, type} = options
  13. elementType = sanitizeTypeOfElement type
  14. count = 0
  15. endOfBranch = ->
  16. if --count == 0
  17. logger.warn "element #{element_id} could not be found for project #{project_id || project._id}"
  18. return callback(new Errors.NotFoundError("entity not found"))
  19. search = (searchFolder, path)->
  20. count++
  21. element = _.find searchFolder[elementType], (el)-> el?._id+'' == element_id+'' #need to ToString both id's for robustness
  22. if !element? && searchFolder.folders? && searchFolder.folders.length != 0
  23. _.each searchFolder.folders, (folder, index)->
  24. if !folder?
  25. return
  26. newPath = {}
  27. newPath[key] = value for own key,value of path #make a value copy of the string
  28. newPath.fileSystem += "/#{folder.name}"
  29. newPath.mongo += ".folders.#{index}"
  30. search folder, newPath
  31. endOfBranch()
  32. return
  33. else if element?
  34. elementPlaceInArray = getIndexOf(searchFolder[elementType], element_id)
  35. path.fileSystem += "/#{element.name}"
  36. path.mongo +=".#{elementType}.#{elementPlaceInArray}"
  37. callback(null, element, path, searchFolder)
  38. else if !element?
  39. return endOfBranch()
  40. path = {fileSystem:'',mongo:'rootFolder.0'}
  41. startSearch = (project)->
  42. if element_id+'' == project.rootFolder[0]._id+''
  43. callback(null, project.rootFolder[0], path, null)
  44. else
  45. search project.rootFolder[0], path
  46. if project?
  47. startSearch(project)
  48. else
  49. ProjectGetter.getProject project_id, {rootFolder:true, rootDoc_id:true}, (err, project)->
  50. return callback(err) if err?
  51. if !project?
  52. return callback(new Errors.NotFoundError("project not found"))
  53. startSearch project
  54. findRootDoc : (opts, callback)->
  55. getRootDoc = (project)=>
  56. if project.rootDoc_id?
  57. @findElement {project:project, element_id:project.rootDoc_id, type:"docs"}, (error, args...) ->
  58. if error?
  59. if error instanceof Errors.NotFoundError
  60. return callback null, null
  61. else
  62. return callback error
  63. return callback null, args...
  64. else
  65. callback null, null
  66. {project, project_id} = opts
  67. if project?
  68. getRootDoc project
  69. else
  70. ProjectGetter.getProject project_id, {rootFolder:true, rootDoc_id:true}, (err, project)->
  71. if err?
  72. logger.err err:err, "error getting project"
  73. return callback(err)
  74. else
  75. getRootDoc project
  76. findElementByPath: (options, callback = (err, foundEntity, type)->)->
  77. {project, project_id, path, exactCaseMatch} = options
  78. if !path?
  79. return new Error('no path provided for findElementByPath')
  80. if project?
  81. ProjectLocator._findElementByPathWithProject project, path, exactCaseMatch, callback
  82. else
  83. ProjectGetter.getProject project_id, {rootFolder:true, rootDoc_id:true}, (err, project)->
  84. return callback(err) if err?
  85. ProjectLocator._findElementByPathWithProject project, path, exactCaseMatch, callback
  86. _findElementByPathWithProject: (project, needlePath, exactCaseMatch, callback = (err, foundEntity, type)->)->
  87. if exactCaseMatch
  88. matchFn = (a, b) -> (a == b)
  89. else
  90. matchFn = (a, b) -> (a?.toLowerCase() == b?.toLowerCase())
  91. getParentFolder = (haystackFolder, foldersList, level, cb)->
  92. if foldersList.length == 0
  93. return cb null, haystackFolder
  94. needleFolderName = foldersList[level]
  95. found = false
  96. for folder in haystackFolder.folders
  97. if matchFn(folder.name, needleFolderName)
  98. found = true
  99. if level == foldersList.length-1
  100. return cb null, folder
  101. else
  102. return getParentFolder(folder, foldersList, level+1, cb)
  103. if !found
  104. cb("not found project: #{project._id} search path: #{needlePath}, folder #{foldersList[level]} could not be found")
  105. getEntity = (folder, entityName, cb)->
  106. if !entityName?
  107. return cb null, folder, "folder"
  108. for file in folder.fileRefs or []
  109. if matchFn(file?.name, entityName)
  110. result = file
  111. type = "file"
  112. for doc in folder.docs or []
  113. if matchFn(doc?.name, entityName)
  114. result = doc
  115. type = "doc"
  116. for childFolder in folder.folders or []
  117. if matchFn(childFolder?.name, entityName)
  118. result = childFolder
  119. type = "folder"
  120. if result?
  121. cb null, result, type
  122. else
  123. cb("not found project: #{project._id} search path: #{needlePath}, entity #{entityName} could not be found")
  124. if err?
  125. logger.err err:err, project_id:project._id, "error getting project for finding element"
  126. return callback(err)
  127. if !project?
  128. return callback("project could not be found for finding a element #{project._id}")
  129. if needlePath == '' || needlePath == '/'
  130. return callback(null, project.rootFolder[0], "folder")
  131. if needlePath.indexOf('/') == 0
  132. needlePath = needlePath.substring(1)
  133. foldersList = needlePath.split('/')
  134. needleName = foldersList.pop()
  135. rootFolder = project.rootFolder[0]
  136. logger.log project_id:project._id, path:needlePath, foldersList:foldersList, "looking for element by path"
  137. jobs = new Array()
  138. jobs.push(
  139. (cb)->
  140. getParentFolder rootFolder, foldersList, 0, cb
  141. )
  142. jobs.push(
  143. (folder, cb)->
  144. getEntity folder, needleName, cb
  145. )
  146. async.waterfall jobs, callback
  147. findUsersProjectByName: (user_id, projectName, callback)->
  148. ProjectGetter.findAllUsersProjects user_id, 'name archived', (err, allProjects)->
  149. return callback(error) if error?
  150. {owned, readAndWrite} = allProjects
  151. projects = owned.concat(readAndWrite)
  152. projectName = projectName.toLowerCase()
  153. project = _.find projects, (project)->
  154. project.name.toLowerCase() == projectName and project.archived != true
  155. logger.log user_id:user_id, projectName:projectName, totalProjects:projects.length, project:project, "looking for project by name"
  156. callback(null, project)
  157. sanitizeTypeOfElement = (elementType)->
  158. lastChar = elementType.slice -1
  159. if lastChar != "s"
  160. elementType +="s"
  161. if elementType == "files"
  162. elementType = "fileRefs"
  163. return elementType
  164. getIndexOf = (searchEntity, id)->
  165. length = searchEntity.length
  166. count = 0
  167. while(count < length)
  168. if searchEntity[count]?._id+"" == id+""
  169. return count
  170. count++