| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- const HttpErrors = require('@overleaf/o-error/http')
- const ProjectDeleter = require('../Project/ProjectDeleter')
- const EditorController = require('./EditorController')
- const ProjectGetter = require('../Project/ProjectGetter')
- const AuthorizationManager = require('../Authorization/AuthorizationManager')
- const ProjectEditorHandler = require('../Project/ProjectEditorHandler')
- const Metrics = require('metrics-sharelatex')
- const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
- const CollaboratorsInviteHandler = require('../Collaborators/CollaboratorsInviteHandler')
- const CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
- const PrivilegeLevels = require('../Authorization/PrivilegeLevels')
- const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
- const AuthenticationController = require('../Authentication/AuthenticationController')
- const Errors = require('../Errors/Errors')
- const HttpErrorHandler = require('../Errors/HttpErrorHandler')
- const ProjectEntityUpdateHandler = require('../Project/ProjectEntityUpdateHandler')
- const { expressify } = require('../../util/promises')
- module.exports = {
- joinProject: expressify(joinProject),
- addDoc: expressify(addDoc),
- addFolder: expressify(addFolder),
- renameEntity: expressify(renameEntity),
- moveEntity: expressify(moveEntity),
- deleteDoc: expressify(deleteDoc),
- deleteFile: expressify(deleteFile),
- deleteFolder: expressify(deleteFolder),
- deleteEntity: expressify(deleteEntity),
- convertDocToFile: expressify(convertDocToFile),
- _nameIsAcceptableLength
- }
- const unsupportedSpellcheckLanguages = [
- 'am',
- 'hy',
- 'bn',
- 'gu',
- 'he',
- 'hi',
- 'hu',
- 'is',
- 'kn',
- 'ml',
- 'mr',
- 'or',
- 'ss',
- 'ta',
- 'te',
- 'uk',
- 'uz',
- 'zu',
- 'fi'
- ]
- async function joinProject(req, res, next) {
- const projectId = req.params.Project_id
- let userId = req.query.user_id
- if (userId === 'anonymous-user') {
- userId = null
- }
- Metrics.inc('editor.join-project')
- const {
- project,
- privilegeLevel,
- isRestrictedUser
- } = await _buildJoinProjectView(req, projectId, userId)
- if (!project) {
- return res.sendStatus(403)
- }
- // Hide access tokens if this is not the project owner
- TokenAccessHandler.protectTokens(project, privilegeLevel)
- if (isRestrictedUser) {
- project.owner = { _id: project.owner._id }
- }
- // Only show the 'renamed or deleted' message once
- if (project.deletedByExternalDataSource) {
- await ProjectDeleter.promises.unmarkAsDeletedByExternalSource(projectId)
- }
- // disable spellchecking for currently unsupported spell check languages
- // preserve the value in the db so they can use it again once we add back
- // support.
- if (
- unsupportedSpellcheckLanguages.indexOf(project.spellCheckLanguage) !== -1
- ) {
- project.spellCheckLanguage = ''
- }
- res.json({
- project,
- privilegeLevel,
- isRestrictedUser
- })
- }
- async function _buildJoinProjectView(req, projectId, userId) {
- const project = await ProjectGetter.promises.getProjectWithoutDocLines(
- projectId
- )
- if (project == null) {
- throw new Errors.NotFoundError('project not found')
- }
- const members = await CollaboratorsGetter.promises.getInvitedMembersWithPrivilegeLevels(
- projectId
- )
- const token = TokenAccessHandler.getRequestToken(req, projectId)
- const privilegeLevel = await AuthorizationManager.promises.getPrivilegeLevelForProject(
- userId,
- projectId,
- token
- )
- if (privilegeLevel == null || privilegeLevel === PrivilegeLevels.NONE) {
- return { project: null, privilegeLevel: null, isRestrictedUser: false }
- }
- const invites = await CollaboratorsInviteHandler.promises.getAllInvites(
- projectId
- )
- const isTokenMember = await CollaboratorsHandler.promises.userIsTokenMember(
- userId,
- projectId
- )
- const isRestrictedUser = AuthorizationManager.isRestrictedUser(
- userId,
- privilegeLevel,
- isTokenMember
- )
- return {
- project: ProjectEditorHandler.buildProjectModelView(
- project,
- members,
- invites
- ),
- privilegeLevel,
- isRestrictedUser
- }
- }
- function _nameIsAcceptableLength(name) {
- return name != null && name.length < 150 && name.length !== 0
- }
- async function addDoc(req, res, next) {
- const projectId = req.params.Project_id
- const { name } = req.body
- const parentFolderId = req.body.parent_folder_id
- const userId = AuthenticationController.getLoggedInUserId(req)
- if (!_nameIsAcceptableLength(name)) {
- return res.sendStatus(400)
- }
- try {
- const doc = await EditorController.promises.addDoc(
- projectId,
- parentFolderId,
- name,
- [],
- 'editor',
- userId
- )
- res.json(doc)
- } catch (err) {
- if (err.message === 'project_has_too_many_files') {
- res.status(400).json(req.i18n.translate('project_has_too_many_files'))
- } else {
- next(err)
- }
- }
- }
- async function addFolder(req, res, next) {
- const projectId = req.params.Project_id
- const { name } = req.body
- const parentFolderId = req.body.parent_folder_id
- if (!_nameIsAcceptableLength(name)) {
- return res.sendStatus(400)
- }
- try {
- const doc = await EditorController.promises.addFolder(
- projectId,
- parentFolderId,
- name,
- 'editor'
- )
- res.json(doc)
- } catch (err) {
- if (err.message === 'project_has_too_many_files') {
- res.status(400).json(req.i18n.translate('project_has_too_many_files'))
- } else if (err.message === 'invalid element name') {
- res.status(400).json(req.i18n.translate('invalid_file_name'))
- } else {
- next(err)
- }
- }
- }
- async function renameEntity(req, res, next) {
- const projectId = req.params.Project_id
- const entityId = req.params.entity_id
- const entityType = req.params.entity_type
- const { name } = req.body
- if (!_nameIsAcceptableLength(name)) {
- return res.sendStatus(400)
- }
- const userId = AuthenticationController.getLoggedInUserId(req)
- await EditorController.promises.renameEntity(
- projectId,
- entityId,
- entityType,
- name,
- userId
- )
- res.sendStatus(204)
- }
- async function moveEntity(req, res, next) {
- const projectId = req.params.Project_id
- const entityId = req.params.entity_id
- const entityType = req.params.entity_type
- const folderId = req.body.folder_id
- const userId = AuthenticationController.getLoggedInUserId(req)
- await EditorController.promises.moveEntity(
- projectId,
- entityId,
- folderId,
- entityType,
- userId
- )
- res.sendStatus(204)
- }
- async function deleteDoc(req, res, next) {
- req.params.entity_type = 'doc'
- await deleteEntity(req, res, next)
- }
- async function deleteFile(req, res, next) {
- req.params.entity_type = 'file'
- await deleteEntity(req, res, next)
- }
- async function deleteFolder(req, res, next) {
- req.params.entity_type = 'folder'
- await deleteEntity(req, res, next)
- }
- async function deleteEntity(req, res, next) {
- const projectId = req.params.Project_id
- const entityId = req.params.entity_id
- const entityType = req.params.entity_type
- const userId = AuthenticationController.getLoggedInUserId(req)
- await EditorController.promises.deleteEntity(
- projectId,
- entityId,
- entityType,
- 'editor',
- userId
- )
- res.sendStatus(204)
- }
- async function convertDocToFile(req, res, next) {
- const projectId = req.params.Project_id
- const docId = req.params.entity_id
- const { userId } = req.body
- try {
- const fileRef = await ProjectEntityUpdateHandler.promises.convertDocToFile(
- projectId,
- docId,
- userId
- )
- res.json({ fileId: fileRef._id.toString() })
- } catch (err) {
- if (err instanceof Errors.NotFoundError) {
- throw new HttpErrors.NotFoundError({
- info: { public: { message: 'Document not found' } }
- })
- } else if (err instanceof Errors.DocHasRangesError) {
- return HttpErrorHandler.unprocessableEntity(
- req,
- res,
- 'Document has comments or tracked changes'
- )
- } else {
- throw err
- }
- }
- }
|