ProjectDetailsHandler.coffee 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. ProjectGetter = require("./ProjectGetter")
  2. UserGetter = require("../User/UserGetter")
  3. Project = require('../../models/Project').Project
  4. ObjectId = require("mongojs").ObjectId
  5. logger = require("logger-sharelatex")
  6. tpdsUpdateSender = require '../ThirdPartyDataStore/TpdsUpdateSender'
  7. _ = require("underscore")
  8. PublicAccessLevels = require("../Authorization/PublicAccessLevels")
  9. Errors = require("../Errors/Errors")
  10. ProjectTokenGenerator = require('./ProjectTokenGenerator')
  11. ProjectEntityHandler = require('./ProjectEntityHandler')
  12. ProjectHelper = require('./ProjectHelper')
  13. settings = require('settings-sharelatex')
  14. module.exports = ProjectDetailsHandler =
  15. getDetails: (project_id, callback)->
  16. ProjectGetter.getProject project_id, {name:true, description:true, compiler:true, features:true, owner_ref:true, overleaf:true}, (err, project)->
  17. if err?
  18. logger.err err:err, project_id:project_id, "error getting project"
  19. return callback(err)
  20. return callback(new Errors.NotFoundError("project not found")) if !project?
  21. UserGetter.getUser project.owner_ref, (err, user) ->
  22. return callback(err) if err?
  23. details =
  24. name : project.name
  25. description: project.description
  26. compiler: project.compiler
  27. features: user?.features or settings.defaultFeatures
  28. if project.overleaf?
  29. details.overleaf = project.overleaf
  30. logger.log project_id:project_id, details: details, "getting project details"
  31. callback(err, details)
  32. getProjectDescription: (project_id, callback)->
  33. ProjectGetter.getProject project_id, description: true, (err, project)->
  34. callback(err, project?.description)
  35. setProjectDescription: (project_id, description, callback)->
  36. conditions = _id:project_id
  37. update = description:description
  38. logger.log conditions:conditions, update:update, project_id:project_id, description:description, "setting project description"
  39. Project.update conditions, update, (err)->
  40. if err?
  41. logger.err err:err, "something went wrong setting project description"
  42. callback(err)
  43. transferOwnership: (project_id, user_id, suffix = "", callback)->
  44. if typeof suffix is 'function'
  45. callback = suffix
  46. suffix = ''
  47. ProjectGetter.getProject project_id, {owner_ref: true, name: true}, (err, project)->
  48. return callback(err) if err?
  49. return callback(new Errors.NotFoundError("project not found")) unless project?
  50. return callback() if project.owner_ref == user_id
  51. UserGetter.getUser user_id, (err, user) ->
  52. return callback(err) if err?
  53. return callback(new Errors.NotFoundError("user not found")) unless user?
  54. ProjectDetailsHandler.generateUniqueName user_id, project.name + suffix, (err, name) ->
  55. return callback(err) if err?
  56. Project.update {_id: project_id},
  57. {
  58. $set: {
  59. owner_ref: user_id,
  60. name: name
  61. },
  62. $pull: {
  63. readOnly_refs: user_id,
  64. collaberator_refs: user_id,
  65. tokenAccessReadAndWrite_refs: user_id,
  66. tokenAccessReadOnly_refs: user_id
  67. }}, (err) ->
  68. return callback(err) if err?
  69. ProjectEntityHandler.flushProjectToThirdPartyDataStore project_id, callback
  70. renameProject: (project_id, newName, callback = ->)->
  71. ProjectDetailsHandler.validateProjectName newName, (error) ->
  72. return callback(error) if error?
  73. logger.log project_id: project_id, newName:newName, "renaming project"
  74. ProjectGetter.getProject project_id, {name:true}, (err, project)->
  75. if err? or !project?
  76. logger.err err:err, project_id:project_id, "error getting project or could not find it todo project rename"
  77. return callback(err)
  78. oldProjectName = project.name
  79. Project.update _id:project_id, {name: newName}, (err, project)=>
  80. if err?
  81. return callback(err)
  82. tpdsUpdateSender.moveEntity {project_id:project_id, project_name:oldProjectName, newProjectName:newName}, callback
  83. MAX_PROJECT_NAME_LENGTH: 150
  84. validateProjectName: (name, callback = (error) ->) ->
  85. if !name? or name.length == 0
  86. return callback(new Errors.InvalidNameError("Project name cannot be blank"))
  87. else if name.length > @MAX_PROJECT_NAME_LENGTH
  88. return callback(new Errors.InvalidNameError("Project name is too long"))
  89. else if name.indexOf("/") > -1
  90. return callback(new Errors.InvalidNameError("Project name cannot contain / characters"))
  91. else if name.indexOf("\\") > -1
  92. return callback(new Errors.InvalidNameError("Project name cannot contain \\ characters"))
  93. else
  94. return callback()
  95. generateUniqueName: (user_id, name, suffixes = [], callback = (error, newName) -> ) ->
  96. if arguments.length is 3 && typeof suffixes is 'function' # make suffixes an optional argument
  97. callback = suffixes
  98. suffixes = []
  99. ProjectDetailsHandler.ensureProjectNameIsUnique user_id, name, suffixes, callback
  100. # FIXME: we should put a lock around this to make it completely safe, but we would need to do that at
  101. # the point of project creation, rather than just checking the name at the start of the import.
  102. # If we later move this check into ProjectCreationHandler we can ensure all new projects are created
  103. # with a unique name. But that requires thinking through how we would handle incoming projects from
  104. # dropbox for example.
  105. ensureProjectNameIsUnique: (user_id, name, suffixes = [], callback = (error, name, changed)->) ->
  106. ProjectGetter.findAllUsersProjects user_id, {name: 1}, (error, allUsersProjectNames) ->
  107. return callback(error) if error?
  108. # allUsersProjectNames is returned as a hash {owned: [name1, name2, ...], readOnly: [....]}
  109. # collect all of the names and flatten them into a single array
  110. projectNameList = _.pluck(_.flatten(_.values(allUsersProjectNames)),'name')
  111. ProjectHelper.ensureNameIsUnique projectNameList, name, suffixes, ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH, callback
  112. fixProjectName: (name) ->
  113. if name == "" || !name
  114. name = "Untitled"
  115. if name.indexOf('/') > -1
  116. # v2 does not allow / in a project name
  117. name = name.replace(/\//g, '-')
  118. if name.indexOf('\\') > -1
  119. # backslashes in project name will prevent syncing to dropbox
  120. name = name.replace(/\\/g, '')
  121. if name.length > @MAX_PROJECT_NAME_LENGTH
  122. name = name.substr(0, @MAX_PROJECT_NAME_LENGTH)
  123. return name
  124. setPublicAccessLevel : (project_id, newAccessLevel, callback = ->)->
  125. logger.log project_id: project_id, level: newAccessLevel, "set public access level"
  126. # DEPRECATED: `READ_ONLY` and `READ_AND_WRITE` are still valid in, but should no longer
  127. # be passed here. Remove after token-based access has been live for a while
  128. if project_id? && newAccessLevel? and _.include [
  129. PublicAccessLevels.READ_ONLY,
  130. PublicAccessLevels.READ_AND_WRITE,
  131. PublicAccessLevels.PRIVATE,
  132. PublicAccessLevels.TOKEN_BASED
  133. ], newAccessLevel
  134. Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)->
  135. callback(err)
  136. ensureTokensArePresent: (project_id, callback=(err, tokens)->) ->
  137. ProjectGetter.getProject project_id, {tokens: 1}, (err, project) ->
  138. return callback(err) if err?
  139. if project.tokens? and project.tokens.readOnly? and project.tokens.readAndWrite?
  140. logger.log {project_id}, "project already has tokens"
  141. return callback(null, project.tokens)
  142. else
  143. logger.log {
  144. project_id,
  145. has_tokens: project.tokens?,
  146. has_readOnly: project?.tokens?.readOnly?,
  147. has_readAndWrite: project?.tokens?.readAndWrite?
  148. }, "generating tokens for project"
  149. ProjectDetailsHandler._generateTokens project, (err) ->
  150. return callback(err) if err?
  151. Project.update {_id: project_id}, {$set: {tokens: project.tokens}}, (err) ->
  152. return callback(err) if err?
  153. callback(null, project.tokens)
  154. _generateTokens: (project, callback=(err)->) ->
  155. project.tokens ||= {}
  156. tokens = project.tokens
  157. if !tokens.readAndWrite?
  158. { token, numericPrefix } = ProjectTokenGenerator.readAndWriteToken()
  159. tokens.readAndWrite = token
  160. tokens.readAndWritePrefix = numericPrefix
  161. if !tokens.readOnly?
  162. ProjectTokenGenerator.generateUniqueReadOnlyToken (err, token) ->
  163. return callback(err) if err?
  164. tokens.readOnly = token
  165. callback()
  166. else
  167. callback()