ProjectDetailsHandler.coffee 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. ProjectGetter = require("./ProjectGetter")
  2. UserGetter = require("../User/UserGetter")
  3. Project = require('../../models/Project').Project
  4. logger = require("logger-sharelatex")
  5. tpdsUpdateSender = require '../ThirdPartyDataStore/TpdsUpdateSender'
  6. _ = require("underscore")
  7. PublicAccessLevels = require("../Authorization/PublicAccessLevels")
  8. Errors = require("../Errors/Errors")
  9. ProjectTokenGenerator = require('./ProjectTokenGenerator')
  10. module.exports = ProjectDetailsHandler =
  11. getDetails: (project_id, callback)->
  12. ProjectGetter.getProject project_id, {name:true, description:true, compiler:true, features:true, owner_ref:true, overleaf:true}, (err, project)->
  13. if err?
  14. logger.err err:err, project_id:project_id, "error getting project"
  15. return callback(err)
  16. return callback(new Errors.NotFoundError("project not found")) if !project?
  17. UserGetter.getUser project.owner_ref, (err, user) ->
  18. return callback(err) if err?
  19. details =
  20. name : project.name
  21. description: project.description
  22. compiler: project.compiler
  23. features: user.features
  24. if project.overleaf?
  25. details.overleaf = project.overleaf
  26. logger.log project_id:project_id, details: details, "getting project details"
  27. callback(err, details)
  28. getProjectDescription: (project_id, callback)->
  29. ProjectGetter.getProject project_id, description: true, (err, project)->
  30. callback(err, project?.description)
  31. setProjectDescription: (project_id, description, callback)->
  32. conditions = _id:project_id
  33. update = description:description
  34. logger.log conditions:conditions, update:update, project_id:project_id, description:description, "setting project description"
  35. Project.update conditions, update, (err)->
  36. if err?
  37. logger.err err:err, "something went wrong setting project description"
  38. callback(err)
  39. renameProject: (project_id, newName, callback = ->)->
  40. ProjectDetailsHandler.validateProjectName newName, (error) ->
  41. return callback(error) if error?
  42. logger.log project_id: project_id, newName:newName, "renaming project"
  43. ProjectGetter.getProject project_id, {name:true}, (err, project)->
  44. if err? or !project?
  45. logger.err err:err, project_id:project_id, "error getting project or could not find it todo project rename"
  46. return callback(err)
  47. oldProjectName = project.name
  48. Project.update _id:project_id, {name: newName}, (err, project)=>
  49. if err?
  50. return callback(err)
  51. tpdsUpdateSender.moveEntity {project_id:project_id, project_name:oldProjectName, newProjectName:newName}, callback
  52. MAX_PROJECT_NAME_LENGTH: 150
  53. validateProjectName: (name, callback = (error) ->) ->
  54. if !name? or name.length == 0
  55. return callback(new Errors.InvalidNameError("Project name cannot be blank"))
  56. else if name.length > @MAX_PROJECT_NAME_LENGTH
  57. return callback(new Errors.InvalidNameError("Project name is too long"))
  58. else if name.indexOf("/") > -1
  59. return callback(new Errors.InvalidNameError("Project name cannot not contain / characters"))
  60. else
  61. return callback()
  62. _addSuffixToProjectName: (name, suffix = '') ->
  63. # append the suffix and truncate the project title if needed
  64. truncatedLength = ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH - suffix.length
  65. return name.substr(0, truncatedLength) + suffix
  66. # FIXME: we should put a lock around this to make it completely safe, but we would need to do that at
  67. # the point of project creation, rather than just checking the name at the start of the import.
  68. # If we later move this check into ProjectCreationHandler we can ensure all new projects are created
  69. # with a unique name. But that requires thinking through how we would handle incoming projects from
  70. # dropbox for example.
  71. ensureProjectNameIsUnique: (user_id, name, suffixes = [], callback = (error, name, changed)->) ->
  72. ProjectGetter.findAllUsersProjects user_id, {name: 1}, (error, allUsersProjects) ->
  73. return callback(error) if error?
  74. {owned, readAndWrite, readOnly, tokenReadAndWrite, tokenReadOnly} = allUsersProjects
  75. # create a set of all project names
  76. allProjectNames = new Set()
  77. for projectName in owned.concat(readAndWrite, readOnly, tokenReadAndWrite, tokenReadOnly)
  78. allProjectNames.add(projectName)
  79. isUnique = (x) -> !allProjectNames.has(x)
  80. # check if the supplied name is already unique
  81. if isUnique(name)
  82. return callback(null, name, false)
  83. # the name already exists, try adding the user-supplied suffixes to generate a unique name
  84. for suffix in suffixes
  85. candidateName = ProjectDetailsHandler._addSuffixToProjectName(name, suffix)
  86. if isUnique(candidateName)
  87. return callback(null, candidateName, true)
  88. # we couldn't make the name unique, something is wrong
  89. return callback new Errors.InvalidNameError("Project name could not be made unique")
  90. setPublicAccessLevel : (project_id, newAccessLevel, callback = ->)->
  91. logger.log project_id: project_id, level: newAccessLevel, "set public access level"
  92. # DEPRECATED: `READ_ONLY` and `READ_AND_WRITE` are still valid in, but should no longer
  93. # be passed here. Remove after token-based access has been live for a while
  94. if project_id? && newAccessLevel? and _.include [
  95. PublicAccessLevels.READ_ONLY,
  96. PublicAccessLevels.READ_AND_WRITE,
  97. PublicAccessLevels.PRIVATE,
  98. PublicAccessLevels.TOKEN_BASED
  99. ], newAccessLevel
  100. Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)->
  101. callback(err)
  102. ensureTokensArePresent: (project_id, callback=(err, tokens)->) ->
  103. ProjectGetter.getProject project_id, {tokens: 1}, (err, project) ->
  104. return callback(err) if err?
  105. if project.tokens? and project.tokens.readOnly? and project.tokens.readAndWrite?
  106. logger.log {project_id}, "project already has tokens"
  107. return callback(null, project.tokens)
  108. else
  109. logger.log {
  110. project_id,
  111. has_tokens: project.tokens?,
  112. has_readOnly: project?.tokens?.readOnly?,
  113. has_readAndWrite: project?.tokens?.readAndWrite?
  114. }, "generating tokens for project"
  115. tokens = project.tokens || {}
  116. if !tokens.readOnly?
  117. tokens.readOnly = ProjectTokenGenerator.readOnlyToken()
  118. if !tokens.readAndWrite?
  119. tokens.readAndWrite = ProjectTokenGenerator.readAndWriteToken()
  120. Project.update {_id: project_id}, {$set: {tokens: tokens}}, (err) ->
  121. return callback(err) if err?
  122. callback(null, tokens)