| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- ProjectGetter = require("./ProjectGetter")
- UserGetter = require("../User/UserGetter")
- Project = require('../../models/Project').Project
- ObjectId = require("mongojs").ObjectId
- logger = require("logger-sharelatex")
- tpdsUpdateSender = require '../ThirdPartyDataStore/TpdsUpdateSender'
- _ = require("underscore")
- PublicAccessLevels = require("../Authorization/PublicAccessLevels")
- Errors = require("../Errors/Errors")
- ProjectTokenGenerator = require('./ProjectTokenGenerator')
- module.exports = ProjectDetailsHandler =
- getDetails: (project_id, callback)->
- ProjectGetter.getProject project_id, {name:true, description:true, compiler:true, features:true, owner_ref:true, overleaf:true}, (err, project)->
- if err?
- logger.err err:err, project_id:project_id, "error getting project"
- return callback(err)
- return callback(new Errors.NotFoundError("project not found")) if !project?
- UserGetter.getUser project.owner_ref, (err, user) ->
- return callback(err) if err?
- details =
- name : project.name
- description: project.description
- compiler: project.compiler
- features: user.features
- if project.overleaf?
- details.overleaf = project.overleaf
- logger.log project_id:project_id, details: details, "getting project details"
- callback(err, details)
- getProjectDescription: (project_id, callback)->
- ProjectGetter.getProject project_id, description: true, (err, project)->
- callback(err, project?.description)
- setProjectDescription: (project_id, description, callback)->
- conditions = _id:project_id
- update = description:description
- logger.log conditions:conditions, update:update, project_id:project_id, description:description, "setting project description"
- Project.update conditions, update, (err)->
- if err?
- logger.err err:err, "something went wrong setting project description"
- callback(err)
- renameProject: (project_id, newName, callback = ->)->
- ProjectDetailsHandler.validateProjectName newName, (error) ->
- return callback(error) if error?
- logger.log project_id: project_id, newName:newName, "renaming project"
- ProjectGetter.getProject project_id, {name:true}, (err, project)->
- if err? or !project?
- logger.err err:err, project_id:project_id, "error getting project or could not find it todo project rename"
- return callback(err)
- oldProjectName = project.name
- Project.update _id:project_id, {name: newName}, (err, project)=>
- if err?
- return callback(err)
- tpdsUpdateSender.moveEntity {project_id:project_id, project_name:oldProjectName, newProjectName:newName}, callback
- MAX_PROJECT_NAME_LENGTH: 150
- validateProjectName: (name, callback = (error) ->) ->
- if !name? or name.length == 0
- return callback(new Errors.InvalidNameError("Project name cannot be blank"))
- else if name.length > @MAX_PROJECT_NAME_LENGTH
- return callback(new Errors.InvalidNameError("Project name is too long"))
- else if name.indexOf("/") > -1
- return callback(new Errors.InvalidNameError("Project name cannot contain / characters"))
- else if name.indexOf("\\") > -1
- return callback(new Errors.InvalidNameError("Project name cannot contain \\ characters"))
- else
- return callback()
- generateUniqueName: (user_id, name, suffixes = [], callback = (error, newName) -> ) ->
- if arguments.length is 3 && typeof suffixes is 'function' # make suffixes an optional argument
- callback = suffixes
- suffixes = []
- ProjectDetailsHandler.ensureProjectNameIsUnique user_id, name, suffixes, callback
- # FIXME: we should put a lock around this to make it completely safe, but we would need to do that at
- # the point of project creation, rather than just checking the name at the start of the import.
- # If we later move this check into ProjectCreationHandler we can ensure all new projects are created
- # with a unique name. But that requires thinking through how we would handle incoming projects from
- # dropbox for example.
- ensureProjectNameIsUnique: (user_id, name, suffixes = [], callback = (error, name, changed)->) ->
- ProjectGetter.findAllUsersProjects user_id, {name: 1}, (error, allUsersProjectNames) ->
- return callback(error) if error?
- # allUsersProjectNames is returned as a hash {owned: [name1, name2, ...], readOnly: [....]}
- # collect all of the names and flatten them into a single array
- projectNameList = _.pluck(_.flatten(_.values(allUsersProjectNames)),'name')
- # create a set of all project names
- allProjectNames = new Set()
- for projectName in projectNameList
- allProjectNames.add(projectName)
- isUnique = (x) -> !allProjectNames.has(x)
- # check if the supplied name is already unique
- if isUnique(name)
- return callback(null, name, false)
- # the name already exists, try adding the user-supplied suffixes to generate a unique name
- for suffix in suffixes
- candidateName = ProjectDetailsHandler._addSuffixToProjectName(name, suffix)
- if isUnique(candidateName)
- return callback(null, candidateName, true)
- # if there are no (more) suffixes, use a numeric one
- uniqueName = ProjectDetailsHandler._addNumericSuffixToProjectName(name, allProjectNames)
- if uniqueName?
- callback(null, uniqueName, true)
- else
- callback(new Error("Failed to generate a unique name for file: #{name}"))
-
- fixProjectName: (name) ->
- if name == "" || !name
- name = "Untitled"
- if name.indexOf('/') > -1
- # v2 does not allow / in a project name
- name = name.replace(/\//g, '-')
- if name.indexOf('\\') > -1
- # backslashes in project name will prevent syncing to dropbox
- name = name.replace(/\\/g, '')
- if name.length > @MAX_PROJECT_NAME_LENGTH
- name = name.substr(0, @MAX_PROJECT_NAME_LENGTH)
- return name
- setPublicAccessLevel : (project_id, newAccessLevel, callback = ->)->
- logger.log project_id: project_id, level: newAccessLevel, "set public access level"
- # DEPRECATED: `READ_ONLY` and `READ_AND_WRITE` are still valid in, but should no longer
- # be passed here. Remove after token-based access has been live for a while
- if project_id? && newAccessLevel? and _.include [
- PublicAccessLevels.READ_ONLY,
- PublicAccessLevels.READ_AND_WRITE,
- PublicAccessLevels.PRIVATE,
- PublicAccessLevels.TOKEN_BASED
- ], newAccessLevel
- Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)->
- callback(err)
- ensureTokensArePresent: (project_id, callback=(err, tokens)->) ->
- ProjectGetter.getProject project_id, {tokens: 1}, (err, project) ->
- return callback(err) if err?
- if project.tokens? and project.tokens.readOnly? and project.tokens.readAndWrite?
- logger.log {project_id}, "project already has tokens"
- return callback(null, project.tokens)
- else
- logger.log {
- project_id,
- has_tokens: project.tokens?,
- has_readOnly: project?.tokens?.readOnly?,
- has_readAndWrite: project?.tokens?.readAndWrite?
- }, "generating tokens for project"
- tokens = project.tokens || {}
- if !tokens.readOnly?
- tokens.readOnly = ProjectTokenGenerator.readOnlyToken()
- if !tokens.readAndWrite?
- tokens.readAndWrite = ProjectTokenGenerator.readAndWriteToken()
- Project.update {_id: project_id}, {$set: {tokens: tokens}}, (err) ->
- return callback(err) if err?
- callback(null, tokens)
- _addSuffixToProjectName: (name, suffix = '') ->
- # append the suffix and truncate the project title if needed
- truncatedLength = ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH - suffix.length
- return name.substr(0, truncatedLength) + suffix
- _addNumericSuffixToProjectName: (name, allProjectNames) ->
- NUMERIC_SUFFIX_MATCH = / \((\d+)\)$/
- suffixedName = (basename, number) ->
- suffix = " (#{number})"
- return basename.substr(0, ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH - suffix.length) + suffix
- match = name.match(NUMERIC_SUFFIX_MATCH)
- basename = name
- n = 1
- last = allProjectNames.size + n
- if match?
- basename = name.replace(NUMERIC_SUFFIX_MATCH, '')
- n = parseInt(match[1])
- while n <= last
- candidate = suffixedName(basename, n)
- return candidate unless allProjectNames.has(candidate)
- n += 1
- return null
|