ProjectCreationHandler.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. max-len,
  5. no-path-concat,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS102: Remove unnecessary code created because of implicit returns
  12. * DS207: Consider shorter variations of null checks
  13. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  14. */
  15. const logger = require('logger-sharelatex')
  16. const OError = require('@overleaf/o-error')
  17. const async = require('async')
  18. const metrics = require('@overleaf/metrics')
  19. const Settings = require('settings-sharelatex')
  20. const { ObjectId } = require('mongodb')
  21. const { Project } = require('../../models/Project')
  22. const { Folder } = require('../../models/Folder')
  23. const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
  24. const ProjectDetailsHandler = require('./ProjectDetailsHandler')
  25. const HistoryManager = require('../History/HistoryManager')
  26. const { User } = require('../../models/User')
  27. const fs = require('fs')
  28. const Path = require('path')
  29. const { promisify } = require('util')
  30. const _ = require('underscore')
  31. const AnalyticsManager = require('../Analytics/AnalyticsManager')
  32. const ProjectCreationHandler = {
  33. createBlankProject(owner_id, projectName, attributes, callback) {
  34. if (callback == null) {
  35. callback = function(error, project) {}
  36. }
  37. metrics.inc('project-creation')
  38. if (arguments.length === 3) {
  39. callback = attributes
  40. attributes = {}
  41. }
  42. return ProjectDetailsHandler.validateProjectName(projectName, function(
  43. error
  44. ) {
  45. if (error != null) {
  46. return callback(error)
  47. }
  48. if (attributes.overleaf !== undefined && attributes.overleaf != null) {
  49. return ProjectCreationHandler._createBlankProject(
  50. owner_id,
  51. projectName,
  52. attributes,
  53. function(error, project) {
  54. if (error != null) {
  55. return callback(error)
  56. }
  57. AnalyticsManager.recordEvent(owner_id, 'project-imported', {
  58. projectId: project._id,
  59. attributes
  60. })
  61. return callback(error, project)
  62. }
  63. )
  64. } else {
  65. return HistoryManager.initializeProject(function(error, history) {
  66. if (error != null) {
  67. return callback(error)
  68. }
  69. attributes.overleaf = {
  70. history: { id: history != null ? history.overleaf_id : undefined }
  71. }
  72. return ProjectCreationHandler._createBlankProject(
  73. owner_id,
  74. projectName,
  75. attributes,
  76. function(error, project) {
  77. if (error != null) {
  78. return callback(error)
  79. }
  80. AnalyticsManager.recordEvent(owner_id, 'project-created', {
  81. projectId: project._id,
  82. attributes
  83. })
  84. return callback(error, project)
  85. }
  86. )
  87. })
  88. }
  89. })
  90. },
  91. _createBlankProject(owner_id, projectName, attributes, callback) {
  92. if (callback == null) {
  93. callback = function(error, project) {}
  94. }
  95. const rootFolder = new Folder({ name: 'rootFolder' })
  96. attributes.lastUpdatedBy = attributes.owner_ref = new ObjectId(owner_id)
  97. attributes.name = projectName
  98. const project = new Project(attributes)
  99. Object.assign(project, attributes)
  100. if (Settings.apis.project_history.displayHistoryForNewProjects) {
  101. project.overleaf.history.display = true
  102. }
  103. if (Settings.currentImageName != null) {
  104. // avoid clobbering any imageName already set in attributes (e.g. importedImageName)
  105. if (project.imageName == null) {
  106. project.imageName = Settings.currentImageName
  107. }
  108. }
  109. project.rootFolder[0] = rootFolder
  110. return User.findById(owner_id, 'ace.spellCheckLanguage', function(
  111. err,
  112. user
  113. ) {
  114. project.spellCheckLanguage = user.ace.spellCheckLanguage
  115. return project.save(function(err) {
  116. if (err != null) {
  117. return callback(err)
  118. }
  119. return callback(err, project)
  120. })
  121. })
  122. },
  123. createProjectFromSnippet(owner_id, projectName, docLines, callback) {
  124. if (callback == null) {
  125. callback = function(error, project) {}
  126. }
  127. return ProjectCreationHandler.createBlankProject(
  128. owner_id,
  129. projectName,
  130. function(error, project) {
  131. if (error != null) {
  132. return callback(error)
  133. }
  134. return ProjectCreationHandler._createRootDoc(
  135. project,
  136. owner_id,
  137. docLines,
  138. callback
  139. )
  140. }
  141. )
  142. },
  143. createBasicProject(owner_id, projectName, callback) {
  144. if (callback == null) {
  145. callback = function(error, project) {}
  146. }
  147. return ProjectCreationHandler.createBlankProject(
  148. owner_id,
  149. projectName,
  150. function(error, project) {
  151. if (error != null) {
  152. return callback(error)
  153. }
  154. return ProjectCreationHandler._buildTemplate(
  155. 'mainbasic.tex',
  156. owner_id,
  157. projectName,
  158. function(error, docLines) {
  159. if (error != null) {
  160. return callback(error)
  161. }
  162. return ProjectCreationHandler._createRootDoc(
  163. project,
  164. owner_id,
  165. docLines,
  166. callback
  167. )
  168. }
  169. )
  170. }
  171. )
  172. },
  173. createExampleProject(owner_id, projectName, callback) {
  174. if (callback == null) {
  175. callback = function(error, project) {}
  176. }
  177. return ProjectCreationHandler.createBlankProject(
  178. owner_id,
  179. projectName,
  180. function(error, project) {
  181. if (error != null) {
  182. return callback(error)
  183. }
  184. return async.series(
  185. [
  186. callback =>
  187. ProjectCreationHandler._buildTemplate(
  188. 'main.tex',
  189. owner_id,
  190. projectName,
  191. function(error, docLines) {
  192. if (error != null) {
  193. return callback(error)
  194. }
  195. return ProjectCreationHandler._createRootDoc(
  196. project,
  197. owner_id,
  198. docLines,
  199. callback
  200. )
  201. }
  202. ),
  203. callback =>
  204. ProjectCreationHandler._buildTemplate(
  205. 'references.bib',
  206. owner_id,
  207. projectName,
  208. function(error, docLines) {
  209. if (error != null) {
  210. return callback(error)
  211. }
  212. return ProjectEntityUpdateHandler.addDoc(
  213. project._id,
  214. project.rootFolder[0]._id,
  215. 'references.bib',
  216. docLines,
  217. owner_id,
  218. (error, doc) => callback(error)
  219. )
  220. }
  221. ),
  222. function(callback) {
  223. const universePath = Path.resolve(
  224. __dirname + '/../../../templates/project_files/universe.jpg'
  225. )
  226. return ProjectEntityUpdateHandler.addFile(
  227. project._id,
  228. project.rootFolder[0]._id,
  229. 'universe.jpg',
  230. universePath,
  231. null,
  232. owner_id,
  233. callback
  234. )
  235. }
  236. ],
  237. error => callback(error, project)
  238. )
  239. }
  240. )
  241. },
  242. _createRootDoc(project, owner_id, docLines, callback) {
  243. if (callback == null) {
  244. callback = function(error, project) {}
  245. }
  246. return ProjectEntityUpdateHandler.addDoc(
  247. project._id,
  248. project.rootFolder[0]._id,
  249. 'main.tex',
  250. docLines,
  251. owner_id,
  252. function(error, doc) {
  253. if (error != null) {
  254. OError.tag(error, 'error adding root doc when creating project')
  255. return callback(error)
  256. }
  257. return ProjectEntityUpdateHandler.setRootDoc(
  258. project._id,
  259. doc._id,
  260. error => callback(error, project)
  261. )
  262. }
  263. )
  264. },
  265. _buildTemplate(template_name, user_id, project_name, callback) {
  266. if (callback == null) {
  267. callback = function(error, output) {}
  268. }
  269. return User.findById(user_id, 'first_name last_name', function(
  270. error,
  271. user
  272. ) {
  273. if (error != null) {
  274. return callback(error)
  275. }
  276. const monthNames = [
  277. 'January',
  278. 'February',
  279. 'March',
  280. 'April',
  281. 'May',
  282. 'June',
  283. 'July',
  284. 'August',
  285. 'September',
  286. 'October',
  287. 'November',
  288. 'December'
  289. ]
  290. const templatePath = Path.resolve(
  291. __dirname + `/../../../templates/project_files/${template_name}`
  292. )
  293. return fs.readFile(templatePath, function(error, template) {
  294. if (error != null) {
  295. return callback(error)
  296. }
  297. const data = {
  298. project_name,
  299. user,
  300. year: new Date().getUTCFullYear(),
  301. month: monthNames[new Date().getUTCMonth()]
  302. }
  303. const output = _.template(template.toString(), data)
  304. return callback(null, output.split('\n'))
  305. })
  306. })
  307. }
  308. }
  309. metrics.timeAsyncMethod(
  310. ProjectCreationHandler,
  311. 'createBlankProject',
  312. 'mongo.ProjectCreationHandler',
  313. logger
  314. )
  315. const promises = {
  316. createBlankProject: promisify(ProjectCreationHandler.createBlankProject)
  317. }
  318. ProjectCreationHandler.promises = promises
  319. module.exports = ProjectCreationHandler