ProjectCreationHandler.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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 async = require('async')
  17. const metrics = require('metrics-sharelatex')
  18. const Settings = require('settings-sharelatex')
  19. const { ObjectId } = require('mongoose').Types
  20. const { Project } = require('../../models/Project')
  21. const { Folder } = require('../../models/Folder')
  22. const ProjectEntityUpdateHandler = require('./ProjectEntityUpdateHandler')
  23. const ProjectDetailsHandler = require('./ProjectDetailsHandler')
  24. const HistoryManager = require('../History/HistoryManager')
  25. const { User } = require('../../models/User')
  26. const fs = require('fs')
  27. const Path = require('path')
  28. const { promisify } = require('util')
  29. const _ = require('underscore')
  30. const AnalyticsManger = require('../Analytics/AnalyticsManager')
  31. const ProjectCreationHandler = {
  32. createBlankProject(owner_id, projectName, attributes, callback) {
  33. if (callback == null) {
  34. callback = function(error, project) {}
  35. }
  36. metrics.inc('project-creation')
  37. if (arguments.length === 3) {
  38. callback = attributes
  39. attributes = {}
  40. }
  41. return ProjectDetailsHandler.validateProjectName(projectName, function(
  42. error
  43. ) {
  44. if (error != null) {
  45. return callback(error)
  46. }
  47. logger.log({ owner_id, projectName }, 'creating blank project')
  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. AnalyticsManger.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. AnalyticsManger.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.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. if (user != null) {
  115. // It's possible the owner_id is a UserStub
  116. project.spellCheckLanguage = user.ace.spellCheckLanguage
  117. }
  118. return project.save(function(err) {
  119. if (err != null) {
  120. return callback(err)
  121. }
  122. return callback(err, project)
  123. })
  124. })
  125. },
  126. createProjectFromSnippet(owner_id, projectName, docLines, callback) {
  127. if (callback == null) {
  128. callback = function(error, project) {}
  129. }
  130. return ProjectCreationHandler.createBlankProject(
  131. owner_id,
  132. projectName,
  133. function(error, project) {
  134. if (error != null) {
  135. return callback(error)
  136. }
  137. return ProjectCreationHandler._createRootDoc(
  138. project,
  139. owner_id,
  140. docLines,
  141. callback
  142. )
  143. }
  144. )
  145. },
  146. createBasicProject(owner_id, projectName, callback) {
  147. if (callback == null) {
  148. callback = function(error, project) {}
  149. }
  150. return ProjectCreationHandler.createBlankProject(
  151. owner_id,
  152. projectName,
  153. function(error, project) {
  154. if (error != null) {
  155. return callback(error)
  156. }
  157. return ProjectCreationHandler._buildTemplate(
  158. 'mainbasic.tex',
  159. owner_id,
  160. projectName,
  161. function(error, docLines) {
  162. if (error != null) {
  163. return callback(error)
  164. }
  165. return ProjectCreationHandler._createRootDoc(
  166. project,
  167. owner_id,
  168. docLines,
  169. callback
  170. )
  171. }
  172. )
  173. }
  174. )
  175. },
  176. createExampleProject(owner_id, projectName, callback) {
  177. if (callback == null) {
  178. callback = function(error, project) {}
  179. }
  180. return ProjectCreationHandler.createBlankProject(
  181. owner_id,
  182. projectName,
  183. function(error, project) {
  184. if (error != null) {
  185. return callback(error)
  186. }
  187. return async.series(
  188. [
  189. callback =>
  190. ProjectCreationHandler._buildTemplate(
  191. 'main.tex',
  192. owner_id,
  193. projectName,
  194. function(error, docLines) {
  195. if (error != null) {
  196. return callback(error)
  197. }
  198. return ProjectCreationHandler._createRootDoc(
  199. project,
  200. owner_id,
  201. docLines,
  202. callback
  203. )
  204. }
  205. ),
  206. callback =>
  207. ProjectCreationHandler._buildTemplate(
  208. 'references.bib',
  209. owner_id,
  210. projectName,
  211. function(error, docLines) {
  212. if (error != null) {
  213. return callback(error)
  214. }
  215. return ProjectEntityUpdateHandler.addDoc(
  216. project._id,
  217. project.rootFolder[0]._id,
  218. 'references.bib',
  219. docLines,
  220. owner_id,
  221. (error, doc) => callback(error)
  222. )
  223. }
  224. ),
  225. function(callback) {
  226. const universePath = Path.resolve(
  227. __dirname + '/../../../templates/project_files/universe.jpg'
  228. )
  229. return ProjectEntityUpdateHandler.addFile(
  230. project._id,
  231. project.rootFolder[0]._id,
  232. 'universe.jpg',
  233. universePath,
  234. null,
  235. owner_id,
  236. callback
  237. )
  238. }
  239. ],
  240. error => callback(error, project)
  241. )
  242. }
  243. )
  244. },
  245. _createRootDoc(project, owner_id, docLines, callback) {
  246. if (callback == null) {
  247. callback = function(error, project) {}
  248. }
  249. return ProjectEntityUpdateHandler.addDoc(
  250. project._id,
  251. project.rootFolder[0]._id,
  252. 'main.tex',
  253. docLines,
  254. owner_id,
  255. function(error, doc) {
  256. if (error != null) {
  257. logger.warn(
  258. { err: error },
  259. 'error adding root doc when creating project'
  260. )
  261. return callback(error)
  262. }
  263. return ProjectEntityUpdateHandler.setRootDoc(
  264. project._id,
  265. doc._id,
  266. error => callback(error, project)
  267. )
  268. }
  269. )
  270. },
  271. _buildTemplate(template_name, user_id, project_name, callback) {
  272. if (callback == null) {
  273. callback = function(error, output) {}
  274. }
  275. return User.findById(user_id, 'first_name last_name', function(
  276. error,
  277. user
  278. ) {
  279. if (error != null) {
  280. return callback(error)
  281. }
  282. const monthNames = [
  283. 'January',
  284. 'February',
  285. 'March',
  286. 'April',
  287. 'May',
  288. 'June',
  289. 'July',
  290. 'August',
  291. 'September',
  292. 'October',
  293. 'November',
  294. 'December'
  295. ]
  296. const templatePath = Path.resolve(
  297. __dirname + `/../../../templates/project_files/${template_name}`
  298. )
  299. return fs.readFile(templatePath, function(error, template) {
  300. if (error != null) {
  301. return callback(error)
  302. }
  303. const data = {
  304. project_name,
  305. user,
  306. year: new Date().getUTCFullYear(),
  307. month: monthNames[new Date().getUTCMonth()]
  308. }
  309. const output = _.template(template.toString(), data)
  310. return callback(null, output.split('\n'))
  311. })
  312. })
  313. }
  314. }
  315. metrics.timeAsyncMethod(
  316. ProjectCreationHandler,
  317. 'createBlankProject',
  318. 'mongo.ProjectCreationHandler',
  319. logger
  320. )
  321. const promises = {
  322. createBlankProject: promisify(ProjectCreationHandler.createBlankProject)
  323. }
  324. ProjectCreationHandler.promises = promises
  325. module.exports = ProjectCreationHandler