| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- _ = require 'lodash'
- async = require 'async'
- logger = require('logger-sharelatex')
- path = require('path')
- Doc = require('../../models/Doc').Doc
- DocstoreManager = require('../Docstore/DocstoreManager')
- DocumentUpdaterHandler = require('../../Features/DocumentUpdater/DocumentUpdaterHandler')
- Errors = require '../Errors/Errors'
- File = require('../../models/File').File
- FileStoreHandler = require('../FileStore/FileStoreHandler')
- LockManager = require('../../infrastructure/LockManager')
- Project = require('../../models/Project').Project
- ProjectEntityHandler = require('./ProjectEntityHandler')
- ProjectGetter = require('./ProjectGetter')
- ProjectLocator = require('./ProjectLocator')
- ProjectUpdateHandler = require('./ProjectUpdateHandler')
- ProjectEntityMongoUpdateHandler = require('./ProjectEntityMongoUpdateHandler')
- SafePath = require './SafePath'
- TpdsUpdateSender = require('../ThirdPartyDataStore/TpdsUpdateSender')
- LOCK_NAMESPACE = "sequentialProjectStructureUpdateLock"
- wrapWithLock = (methodWithoutLock) ->
- # This lock is used to make sure that the project structure updates are made
- # sequentially. In particular the updates must be made in mongo and sent to
- # the doc-updater in the same order.
- if typeof methodWithoutLock is 'function'
- methodWithLock = (project_id, args..., callback) ->
- LockManager.runWithLock LOCK_NAMESPACE, project_id,
- (cb) -> methodWithoutLock project_id, args..., cb
- callback
- methodWithLock.withoutLock = methodWithoutLock
- methodWithLock
- else
- # handle case with separate setup and locked stages
- wrapWithSetup = methodWithoutLock.beforeLock # a function to set things up before the lock
- mainTask = methodWithoutLock.withLock # function to execute inside the lock
- methodWithLock = wrapWithSetup (project_id, args..., callback) ->
- LockManager.runWithLock(LOCK_NAMESPACE, project_id, (cb) ->
- mainTask(project_id, args..., cb)
- callback)
- methodWithLock.withoutLock = wrapWithSetup mainTask
- methodWithLock.beforeLock = methodWithoutLock.beforeLock
- methodWithLock.mainTask = methodWithoutLock.withLock
- methodWithLock
- module.exports = ProjectEntityUpdateHandler = self =
- copyFileFromExistingProjectWithProject: wrapWithLock
- beforeLock: (next) ->
- (project, folder_id, originalProject_id, origonalFileRef, userId, callback = (error, fileRef, folder_id) ->)->
- project_id = project._id
- logger.log { project_id, folder_id, originalProject_id, origonalFileRef }, "copying file in s3 with project"
- ProjectEntityMongoUpdateHandler._confirmFolder project, folder_id, (folder_id) ->
- if !origonalFileRef?
- logger.err { project_id, folder_id, originalProject_id, origonalFileRef }, "file trying to copy is null"
- return callback()
- # convert any invalid characters in original file to '_'
- fileProperties = name : SafePath.clean(origonalFileRef.name)
- if origonalFileRef.linkedFileData?
- fileProperties.linkedFileData = origonalFileRef.linkedFileData
- fileRef = new File(fileProperties)
- FileStoreHandler.copyFile originalProject_id, origonalFileRef._id, project._id, fileRef._id, (err, fileStoreUrl)->
- if err?
- logger.err { err, project_id, folder_id, originalProject_id, origonalFileRef }, "error coping file in s3"
- return callback(err)
- next(project, folder_id, originalProject_id, origonalFileRef, userId, fileRef, fileStoreUrl, callback)
- withLock: (project, folder_id, originalProject_id, origonalFileRef, userId, fileRef, fileStoreUrl, callback = (error, fileRef, folder_id) ->)->
- project_id = project._id
- projectHistoryId = project.overleaf?.history?.id
- ProjectEntityMongoUpdateHandler._putElement project, folder_id, fileRef, "file", (err, result, newProject) ->
- if err?
- logger.err { err, project_id, folder_id }, "error putting element as part of copy"
- return callback(err)
- TpdsUpdateSender.addFile { project_id, file_id:fileRef._id, path:result?.path?.fileSystem, rev:fileRef.rev, project_name:project.name}, (err) ->
- if err?
- logger.err { err, project_id, folder_id, originalProject_id, origonalFileRef }, "error sending file to tpds worker"
- newFiles = [
- file: fileRef
- path: result?.path?.fileSystem
- url: fileStoreUrl
- ]
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, {newFiles, newProject}, (error) ->
- return callback(error) if error?
- callback null, fileRef, folder_id
- updateDocLines: (project_id, doc_id, lines, version, ranges, callback = (error) ->)->
- ProjectGetter.getProjectWithoutDocLines project_id, (err, project)->
- return callback(err) if err?
- return callback(new Errors.NotFoundError("project not found")) if !project?
- logger.log project_id: project_id, doc_id: doc_id, "updating doc lines"
- ProjectLocator.findElement {project:project, element_id:doc_id, type:"docs"}, (err, doc, path)->
- isDeletedDoc = false
- if err?
- if err instanceof Errors.NotFoundError
- # We need to be able to update the doclines of deleted docs. This is
- # so the doc-updater can flush a doc's content to the doc-store after
- # the doc is deleted.
- isDeletedDoc = true
- doc = _.find project.deletedDocs, (doc) ->
- doc._id.toString() == doc_id.toString()
- else
- return callback(err)
- if !doc?
- # Do not allow an update to a doc which has never exist on this project
- logger.error {doc_id, project_id, lines}, "doc not found while updating doc lines"
- return callback(new Errors.NotFoundError('doc not found'))
- logger.log {project_id, doc_id}, "telling docstore manager to update doc"
- DocstoreManager.updateDoc project_id, doc_id, lines, version, ranges, (err, modified, rev) ->
- if err?
- logger.error {err, doc_id, project_id, lines}, "error sending doc to docstore"
- return callback(err)
- logger.log {project_id, doc_id, modified}, "finished updating doc lines"
- # path will only be present if the doc is not deleted
- if modified && !isDeletedDoc
- # Don't need to block for marking as updated
- ProjectUpdateHandler.markAsUpdated project_id
- TpdsUpdateSender.addDoc {project_id:project_id, path:path.fileSystem, doc_id:doc_id, project_name:project.name, rev:rev}, callback
- else
- callback()
- setRootDoc: (project_id, newRootDocID, callback = (error) ->)->
- logger.log project_id: project_id, rootDocId: newRootDocID, "setting root doc"
- Project.update {_id:project_id}, {rootDoc_id:newRootDocID}, {}, callback
- unsetRootDoc: (project_id, callback = (error) ->) ->
- logger.log project_id: project_id, "removing root doc"
- Project.update {_id:project_id}, {$unset: {rootDoc_id: true}}, {}, callback
- addDoc: wrapWithLock (project_id, folder_id, docName, docLines, userId, callback = (error, doc, folder_id) ->)=>
- if not SafePath.isCleanFilename docName
- return callback new Errors.InvalidNameError("invalid element name")
- self.addDocWithoutUpdatingHistory.withoutLock project_id, folder_id, docName, docLines, userId, (error, doc, folder_id, path, project) ->
- return callback(error) if error?
- projectHistoryId = project.overleaf?.history?.id
- newDocs = [
- doc: doc
- path: path
- docLines: docLines.join('\n')
- ]
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, {newDocs}, (error) ->
- return callback(error) if error?
- callback null, doc, folder_id
- _uploadFile: (project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback = (error, fileRef, fileStoreUrl) ->)->
- if not SafePath.isCleanFilename fileName
- return callback new Errors.InvalidNameError("invalid element name")
- fileRef = new File(
- name: fileName
- linkedFileData: linkedFileData
- )
- FileStoreHandler.uploadFileFromDisk project_id, fileRef._id, fsPath, (err, fileStoreUrl)->
- if err?
- logger.err err:err, project_id: project_id, folder_id: folder_id, file_name: fileName, fileRef:fileRef, "error uploading image to s3"
- return callback(err)
- callback(null, fileRef, fileStoreUrl)
- _addFileAndSendToTpds: (project_id, folder_id, fileName, fileRef, callback = (error) ->)->
- ProjectEntityMongoUpdateHandler.addFile project_id, folder_id, fileRef, (err, result, project) ->
- if err?
- logger.err err:err, project_id: project_id, folder_id: folder_id, file_name: fileName, fileRef:fileRef, "error adding file with project"
- return callback(err)
- TpdsUpdateSender.addFile {project_id:project_id, file_id:fileRef._id, path:result?.path?.fileSystem, project_name:project.name, rev:fileRef.rev}, (err) ->
- return callback(err) if err?
- callback(null, result, project)
- addFile: wrapWithLock
- beforeLock: (next) ->
- (project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback) ->
- if not SafePath.isCleanFilename fileName
- return callback new Errors.InvalidNameError("invalid element name")
- ProjectEntityUpdateHandler._uploadFile project_id, folder_id, fileName, fsPath, linkedFileData, userId, (error, fileRef, fileStoreUrl) ->
- return callback(error) if error?
- next(project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback)
- withLock: (project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback = (error, fileRef, folder_id) ->)->
- ProjectEntityUpdateHandler._addFileAndSendToTpds project_id, folder_id, fileName, fileRef, (err, result, project) ->
- return callback(err) if err?
- projectHistoryId = project.overleaf?.history?.id
- newFiles = [
- file: fileRef
- path: result?.path?.fileSystem
- url: fileStoreUrl
- ]
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, {newFiles}, (error) ->
- return callback(error) if error?
- callback(null, fileRef, folder_id)
- replaceFile: wrapWithLock
- beforeLock: (next) ->
- (project_id, file_id, fsPath, linkedFileData, userId, callback)->
- # create a new file
- fileRef = new File(
- name: "dummy-upload-filename"
- linkedFileData: linkedFileData
- )
- FileStoreHandler.uploadFileFromDisk project_id, fileRef._id, fsPath, (err, fileStoreUrl)->
- return callback(err) if err?
- next project_id, file_id, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback
- withLock: (project_id, file_id, fsPath, linkedFileData, userId, newFileRef, fileStoreUrl, callback)->
- ProjectEntityMongoUpdateHandler.replaceFileWithNew project_id, file_id, newFileRef, (err, oldFileRef, project, path) ->
- return callback(err) if err?
- oldFiles = [
- file: oldFileRef
- path: path.fileSystem
- ]
- newFiles = [
- file: newFileRef
- path: path.fileSystem
- url: fileStoreUrl
- ]
- projectHistoryId = project.overleaf?.history?.id
- TpdsUpdateSender.addFile {project_id:project._id, file_id:newFileRef._id, path:path.fileSystem, rev:newFileRef.rev+1, project_name:project.name}, (err) ->
- return callback(err) if err?
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, {oldFiles, newFiles}, callback
- addDocWithoutUpdatingHistory: wrapWithLock (project_id, folder_id, docName, docLines, userId, callback = (error, doc, folder_id) ->)=>
- # This method should never be called directly, except when importing a project
- # from Overleaf. It skips sending updates to the project history, which will break
- # the history unless you are making sure it is updated in some other way.
- if not SafePath.isCleanFilename docName
- return callback new Errors.InvalidNameError("invalid element name")
- # Put doc in docstore first, so that if it errors, we don't have a doc_id in the project
- # which hasn't been created in docstore.
- doc = new Doc name: docName
- DocstoreManager.updateDoc project_id.toString(), doc._id.toString(), docLines, 0, {}, (err, modified, rev) ->
- return callback(err) if err?
- ProjectEntityMongoUpdateHandler.addDoc project_id, folder_id, doc, (err, result, project) ->
- return callback(err) if err?
- TpdsUpdateSender.addDoc {
- project_id: project_id,
- doc_id: doc?._id
- path: result?.path?.fileSystem,
- project_name: project.name,
- rev: 0
- }, (err) ->
- return callback(err) if err?
- callback(null, doc, folder_id, result?.path?.fileSystem, project)
- addFileWithoutUpdatingHistory: wrapWithLock
- # This method should never be called directly, except when importing a project
- # from Overleaf. It skips sending updates to the project history, which will break
- # the history unless you are making sure it is updated in some other way.
- beforeLock: (next) ->
- (project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback) ->
- if not SafePath.isCleanFilename fileName
- return callback(new Errors.InvalidNameError("invalid element name"))
- ProjectEntityUpdateHandler._uploadFile project_id, folder_id, fileName, fsPath, linkedFileData, userId, (error, fileRef, fileStoreUrl) ->
- return callback(error) if error?
- next(project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback)
- withLock: (project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback = (error, fileRef, folder_id, path, fileStoreUrl) ->)->
- ProjectEntityUpdateHandler._addFileAndSendToTpds project_id, folder_id, fileName, fileRef, (err, result, project) ->
- return callback(err) if err?
- callback(null, fileRef, folder_id, result?.path?.fileSystem, fileStoreUrl)
- upsertDoc: wrapWithLock (project_id, folder_id, docName, docLines, source, userId, callback = (err, doc, folder_id, isNewDoc)->)->
- if not SafePath.isCleanFilename docName
- return callback new Errors.InvalidNameError("invalid element name")
- ProjectLocator.findElement project_id: project_id, element_id: folder_id, type: "folder", (error, folder) ->
- return callback(error) if error?
- return callback(new Error("Couldn't find folder")) if !folder?
- existingDoc = null
- for doc in folder.docs
- if doc.name == docName
- existingDoc = doc
- break
- if existingDoc?
- DocumentUpdaterHandler.setDocument project_id, existingDoc._id, userId, docLines, source, (err)=>
- logger.log project_id:project_id, doc_id:existingDoc._id, "notifying users that the document has been updated"
- DocumentUpdaterHandler.flushDocToMongo project_id, existingDoc._id, (err) ->
- return callback(err) if err?
- callback null, existingDoc, !existingDoc?
- else
- self.addDoc.withoutLock project_id, folder_id, docName, docLines, userId, (err, doc) ->
- return callback(err) if err?
- callback null, doc, !existingDoc?
- upsertFile: wrapWithLock
- beforeLock: (next) ->
- (project_id, folder_id, fileName, fsPath, linkedFileData, userId, callback)->
- if not SafePath.isCleanFilename fileName
- return callback new Errors.InvalidNameError("invalid element name")
- # create a new file
- fileRef = new File(
- name: fileName
- linkedFileData: linkedFileData
- )
- FileStoreHandler.uploadFileFromDisk project_id, fileRef._id, fsPath, (err, fileStoreUrl)->
- return callback(err) if err?
- next(project_id, folder_id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback)
- withLock: (project_id, folder_id, fileName, fsPath, linkedFileData, userId, newFileRef, fileStoreUrl, callback = (err, file, isNewFile, existingFile)->)->
- ProjectLocator.findElement project_id: project_id, element_id: folder_id, type: "folder", (error, folder) ->
- return callback(error) if error?
- return callback(new Error("Couldn't find folder")) if !folder?
- existingFile = null
- for fileRef in folder.fileRefs
- if fileRef.name == fileName
- existingFile = fileRef
- break
- if existingFile?
- # this calls directly into the replaceFile main task (without the beforeLock part)
- self.replaceFile.mainTask project_id, existingFile._id, fsPath, linkedFileData, userId, newFileRef, fileStoreUrl, (err) ->
- return callback(err) if err?
- callback null, newFileRef, !existingFile?, existingFile
- else
- # this calls directly into the addFile main task (without the beforeLock part)
- self.addFile.mainTask project_id, folder_id, fileName, fsPath, linkedFileData, userId, newFileRef, fileStoreUrl, (err) ->
- return callback(err) if err?
- callback null, newFileRef, !existingFile?, existingFile
- upsertDocWithPath: wrapWithLock (project_id, elementPath, docLines, source, userId, callback) ->
- if not SafePath.isCleanPath elementPath
- return callback new Errors.InvalidNameError("invalid element name")
- docName = path.basename(elementPath)
- folderPath = path.dirname(elementPath)
- self.mkdirp.withoutLock project_id, folderPath, (err, newFolders, folder) ->
- return callback(err) if err?
- self.upsertDoc.withoutLock project_id, folder._id, docName, docLines, source, userId, (err, doc, isNewDoc) ->
- return callback(err) if err?
- callback null, doc, isNewDoc, newFolders, folder
- upsertFileWithPath: wrapWithLock
- beforeLock: (next) ->
- (project_id, elementPath, fsPath, linkedFileData, userId, callback)->
- if not SafePath.isCleanPath elementPath
- return callback new Errors.InvalidNameError("invalid element name")
- fileName = path.basename(elementPath)
- folderPath = path.dirname(elementPath)
- # create a new file
- fileRef = new File(
- name: fileName
- linkedFileData: linkedFileData
- )
- FileStoreHandler.uploadFileFromDisk project_id, fileRef._id, fsPath, (err, fileStoreUrl)->
- return callback(err) if err?
- next project_id, folderPath, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback
- withLock: (project_id, folderPath, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, callback) ->
- self.mkdirp.withoutLock project_id, folderPath, (err, newFolders, folder) ->
- return callback(err) if err?
- # this calls directly into the upsertFile main task (without the beforeLock part)
- self.upsertFile.mainTask project_id, folder._id, fileName, fsPath, linkedFileData, userId, fileRef, fileStoreUrl, (err, newFile, isNewFile, existingFile) ->
- return callback(err) if err?
- callback null, newFile, isNewFile, existingFile, newFolders, folder
- deleteEntity: wrapWithLock (project_id, entity_id, entityType, userId, callback = (error) ->)->
- logger.log entity_id:entity_id, entityType:entityType, project_id:project_id, "deleting project entity"
- if !entityType?
- logger.err err: "No entityType set", project_id: project_id, entity_id: entity_id
- return callback("No entityType set")
- entityType = entityType.toLowerCase()
- ProjectEntityMongoUpdateHandler.deleteEntity project_id, entity_id, entityType, (error, entity, path, projectBeforeDeletion) ->
- return callback(error) if error?
- self._cleanUpEntity projectBeforeDeletion, entity, entityType, path.fileSystem, userId, (error) ->
- return callback(error) if error?
- TpdsUpdateSender.deleteEntity project_id:project_id, path:path.fileSystem, project_name:projectBeforeDeletion.name, (error) ->
- return callback(error) if error?
- callback null, entity_id
- deleteEntityWithPath: wrapWithLock (project_id, path, userId, callback) ->
- ProjectLocator.findElementByPath project_id: project_id, path: path, (err, element, type)->
- return callback(err) if err?
- return callback(new Errors.NotFoundError("project not found")) if !element?
- self.deleteEntity.withoutLock project_id, element._id, type, userId, callback
- mkdirp: wrapWithLock (project_id, path, callback = (err, newlyCreatedFolders, lastFolderInPath)->)->
- for folder in path.split('/')
- if folder.length > 0 and not SafePath.isCleanFilename folder
- return callback new Errors.InvalidNameError("invalid element name")
- ProjectEntityMongoUpdateHandler.mkdirp project_id, path, callback
- addFolder: wrapWithLock (project_id, parentFolder_id, folderName, callback) ->
- if not SafePath.isCleanFilename folderName
- return callback new Errors.InvalidNameError("invalid element name")
- ProjectEntityMongoUpdateHandler.addFolder project_id, parentFolder_id, folderName, callback
- moveEntity: wrapWithLock (project_id, entity_id, destFolderId, entityType, userId, callback = (error) ->)->
- logger.log {entityType, entity_id, project_id, destFolderId}, "moving entity"
- if !entityType?
- logger.err {err: "No entityType set", project_id, entity_id}
- return callback("No entityType set")
- entityType = entityType.toLowerCase()
- ProjectEntityMongoUpdateHandler.moveEntity project_id, entity_id, destFolderId, entityType, (err, project, startPath, endPath, rev, changes) ->
- return callback(err) if err?
- projectHistoryId = project.overleaf?.history?.id
- TpdsUpdateSender.moveEntity { project_id, project_name: project.name, startPath, endPath, rev }
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, changes, callback
- renameEntity: wrapWithLock (project_id, entity_id, entityType, newName, userId, callback)->
- if not SafePath.isCleanFilename newName
- return callback new Errors.InvalidNameError("invalid element name")
- logger.log(entity_id: entity_id, project_id: project_id, ('renaming '+entityType))
- if !entityType?
- logger.err err: "No entityType set", project_id: project_id, entity_id: entity_id
- return callback("No entityType set")
- entityType = entityType.toLowerCase()
- ProjectEntityMongoUpdateHandler.renameEntity project_id, entity_id, entityType, newName, (err, project, startPath, endPath, rev, changes) ->
- return callback(err) if err?
- projectHistoryId = project.overleaf?.history?.id
- TpdsUpdateSender.moveEntity { project_id, project_name: project.name, startPath, endPath, rev }
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, changes, callback
- # This doesn't directly update project structure but we need to take the lock
- # to prevent anything else being queued before the resync update
- resyncProjectHistory: wrapWithLock (project_id, callback) ->
- ProjectGetter.getProject project_id, rootFolder: true, overleaf: true, (error, project) ->
- return callback(error) if error?
- projectHistoryId = project?.overleaf?.history?.id
- if !projectHistoryId?
- error = new Errors.ProjectHistoryDisabledError("project history not enabled for #{project_id}")
- return callback(error)
- ProjectEntityHandler.getAllEntitiesFromProject project, (error, docs, files) ->
- return callback(error) if error?
- docs = _.map docs, (doc) ->
- doc: doc.doc._id
- path: doc.path
- files = _.map files, (file) ->
- file: file.file._id
- path: file.path
- url: FileStoreHandler._buildUrl(project_id, file.file._id)
- DocumentUpdaterHandler.resyncProjectHistory project_id, projectHistoryId, docs, files, callback
- _cleanUpEntity: (project, entity, entityType, path, userId, callback = (error) ->) ->
- self._updateProjectStructureWithDeletedEntity project, entity, entityType, path, userId, (error) ->
- return callback(error) if error?
- if(entityType.indexOf("file") != -1)
- self._cleanUpFile project, entity, path, userId, callback
- else if (entityType.indexOf("doc") != -1)
- self._cleanUpDoc project, entity, path, userId, callback
- else if (entityType.indexOf("folder") != -1)
- self._cleanUpFolder project, entity, path, userId, callback
- else
- callback()
- # Note: the _cleanUpEntity code and _updateProjectStructureWithDeletedEntity
- # methods both need to recursively iterate over the entities in folder.
- # These are currently using separate implementations of the recursion. In
- # future, these could be simplified using a common project entity iterator.
- _updateProjectStructureWithDeletedEntity: (project, entity, entityType, entityPath, userId, callback = (error) ->) ->
- # compute the changes to the project structure
- if(entityType.indexOf("file") != -1)
- changes = oldFiles: [ {file: entity, path: entityPath} ]
- else if (entityType.indexOf("doc") != -1)
- changes = oldDocs: [ {doc: entity, path: entityPath} ]
- else if (entityType.indexOf("folder") != -1)
- changes = {oldDocs: [], oldFiles: []}
- _recurseFolder = (folder, folderPath) ->
- for doc in folder.docs
- changes.oldDocs.push {doc, path: path.join(folderPath, doc.name)}
- for file in folder.fileRefs
- changes.oldFiles.push {file, path: path.join(folderPath, file.name)}
- for childFolder in folder.folders
- _recurseFolder(childFolder, path.join(folderPath, childFolder.name))
- _recurseFolder entity, entityPath
- # now send the project structure changes to the docupdater
- project_id = project._id.toString()
- projectHistoryId = project.overleaf?.history?.id
- DocumentUpdaterHandler.updateProjectStructure project_id, projectHistoryId, userId, changes, callback
- _cleanUpDoc: (project, doc, path, userId, callback = (error) ->) ->
- project_id = project._id.toString()
- doc_id = doc._id.toString()
- unsetRootDocIfRequired = (callback) =>
- if project.rootDoc_id? and project.rootDoc_id.toString() == doc_id
- @unsetRootDoc project_id, callback
- else
- callback()
- unsetRootDocIfRequired (error) ->
- return callback(error) if error?
- ProjectEntityMongoUpdateHandler._insertDeletedDocReference project._id, doc, (error) ->
- return callback(error) if error?
- DocumentUpdaterHandler.deleteDoc project_id, doc_id, (error) ->
- return callback(error) if error?
- DocstoreManager.deleteDoc project_id, doc_id, callback
- _cleanUpFile: (project, file, path, userId, callback = (error) ->) ->
- ProjectEntityMongoUpdateHandler._insertDeletedFileReference project._id, file, callback
- _cleanUpFolder: (project, folder, folderPath, userId, callback = (error) ->) ->
- jobs = []
- for doc in folder.docs
- do (doc) ->
- docPath = path.join(folderPath, doc.name)
- jobs.push (callback) -> self._cleanUpDoc project, doc, docPath, userId, callback
- for file in folder.fileRefs
- do (file) ->
- filePath = path.join(folderPath, file.name)
- jobs.push (callback) -> self._cleanUpFile project, file, filePath, userId, callback
- for childFolder in folder.folders
- do (childFolder) ->
- folderPath = path.join(folderPath, childFolder.name)
- jobs.push (callback) -> self._cleanUpFolder project, childFolder, folderPath, userId, callback
- async.series jobs, callback
|