ProjectDuplicator.coffee 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. projectCreationHandler = require('./ProjectCreationHandler')
  2. ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
  3. projectLocator = require('./ProjectLocator')
  4. projectOptionsHandler = require('./ProjectOptionsHandler')
  5. projectDeleter = require('./ProjectDeleter')
  6. DocumentUpdaterHandler = require("../DocumentUpdater/DocumentUpdaterHandler")
  7. DocstoreManager = require "../Docstore/DocstoreManager"
  8. ProjectGetter = require("./ProjectGetter")
  9. _ = require('underscore')
  10. async = require('async')
  11. logger = require("logger-sharelatex")
  12. module.exports = ProjectDuplicator =
  13. _copyDocs: (owner_id, newProject, originalRootDoc, originalFolder, desFolder, docContents, callback)->
  14. setRootDoc = _.once (doc_id)->
  15. ProjectEntityUpdateHandler.setRootDoc newProject._id, doc_id
  16. docs = originalFolder.docs or []
  17. jobs = docs.map (doc)->
  18. return (cb)->
  19. if !doc?._id?
  20. return callback()
  21. content = docContents[doc._id.toString()]
  22. ProjectEntityUpdateHandler.addDoc newProject._id, desFolder._id, doc.name, content.lines, owner_id, (err, newDoc)->
  23. if err?
  24. logger.err err:err, "error copying doc"
  25. return callback(err)
  26. if originalRootDoc? and newDoc.name == originalRootDoc.name
  27. setRootDoc newDoc._id
  28. cb()
  29. async.series jobs, callback
  30. _copyFiles: (owner_id, newProject, originalProject_id, originalFolder, desFolder, callback)->
  31. fileRefs = originalFolder.fileRefs or []
  32. firstError = null # track first error to exit gracefully from parallel copy
  33. jobs = fileRefs.map (file)->
  34. return (cb)->
  35. return async.setImmediate(cb) if firstError? # skip further copies if an error has occurred
  36. ProjectEntityUpdateHandler.copyFileFromExistingProjectWithProject newProject._id, newProject, desFolder._id, originalProject_id, file, owner_id, (err) ->
  37. firstError ||= err if err? # set the error flag if this copy failed
  38. return cb()
  39. # If one of these jobs fails then we wait until all running jobs have
  40. # finished, skipping those which have not started yet. We need to wait
  41. # for all the copy jobs to finish to avoid them writing to the project
  42. # entry in the background while we are deleting it.
  43. async.parallelLimit jobs, 5, (err) ->
  44. return callback(firstError) if firstError?
  45. return callback(err) if err? # shouldn't happen
  46. return callback()
  47. _copyFolderRecursivly: (owner_id, newProject_id, originalProject_id, originalRootDoc, originalFolder, desFolder, docContents, callback)->
  48. ProjectGetter.getProject newProject_id, {rootFolder:true, name:true}, (err, newProject)->
  49. if err?
  50. logger.err project_id:newProject_id, "could not get project"
  51. return callback(err)
  52. folders = originalFolder.folders or []
  53. jobs = folders.map (childFolder)->
  54. return (cb)->
  55. if !childFolder?._id?
  56. return cb()
  57. ProjectEntityUpdateHandler.addFolder newProject._id, desFolder?._id, childFolder.name, (err, newFolder)->
  58. return cb(err) if err?
  59. ProjectDuplicator._copyFolderRecursivly owner_id, newProject_id, originalProject_id, originalRootDoc, childFolder, newFolder, docContents, cb
  60. jobs.push (cb)->
  61. ProjectDuplicator._copyFiles owner_id, newProject, originalProject_id, originalFolder, desFolder, cb
  62. jobs.push (cb)->
  63. ProjectDuplicator._copyDocs owner_id, newProject, originalRootDoc, originalFolder, desFolder, docContents, cb
  64. async.series jobs, callback
  65. duplicate: (owner, originalProject_id, newProjectName, callback)->
  66. jobs =
  67. flush: (cb)->
  68. DocumentUpdaterHandler.flushProjectToMongo originalProject_id, cb
  69. originalProject: (cb)->
  70. ProjectGetter.getProject originalProject_id, {compiler:true, rootFolder:true, rootDoc_id:true}, cb
  71. originalRootDoc: (cb)->
  72. projectLocator.findRootDoc {project_id:originalProject_id}, cb
  73. docContentsArray: (cb)->
  74. DocstoreManager.getAllDocs originalProject_id, cb
  75. # Get the contents of the original project first
  76. async.series jobs, (err, results)->
  77. if err?
  78. logger.err err:err, originalProject_id:originalProject_id, "error duplicating project reading original project"
  79. return callback(err)
  80. {originalProject, originalRootDoc, docContentsArray} = results
  81. originalRootDoc = originalRootDoc?[0]
  82. docContents = {}
  83. for docContent in docContentsArray
  84. docContents[docContent._id] = docContent
  85. # Now create the new project, cleaning it up on failure if necessary
  86. projectCreationHandler.createBlankProject owner._id, newProjectName, (err, newProject) ->
  87. if err?
  88. logger.err err:err, originalProject_id:originalProject_id, "error duplicating project when creating new project"
  89. return callback(err)
  90. copyJobs =
  91. setCompiler: (cb) ->
  92. projectOptionsHandler.setCompiler newProject._id, originalProject.compiler, cb
  93. copyFiles: (cb) ->
  94. ProjectDuplicator._copyFolderRecursivly owner._id, newProject._id, originalProject_id, originalRootDoc, originalProject.rootFolder[0], newProject.rootFolder[0], docContents, cb
  95. # Copy the contents of the original project into the new project
  96. async.series copyJobs, (err) ->
  97. if err?
  98. logger.err err:err, originalProject_id:originalProject_id, newProjectName:newProjectName, newProject_id: newProject._id, "error cloning project, will delete broken clone"
  99. # Clean up broken clone on error.
  100. # Make sure we delete the new failed project, not the original one!
  101. projectDeleter.deleteProject newProject._id, (delete_err) ->
  102. if delete_err?
  103. logger.error newProject_id: newProject._id, delete_err:delete_err, "error deleting broken clone of project"
  104. callback(err)
  105. else
  106. callback(null, newProject)