EditorController.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. /* eslint-disable
  2. camelcase,
  3. node/handle-callback-err,
  4. max-len,
  5. no-dupe-keys,
  6. no-unused-vars,
  7. */
  8. // TODO: This file was created by bulk-decaffeinate.
  9. // Fix any style issues and re-enable lint.
  10. /*
  11. * decaffeinate suggestions:
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS207: Consider shorter variations of null checks
  14. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  15. */
  16. const logger = require('@overleaf/logger')
  17. const OError = require('@overleaf/o-error')
  18. const Metrics = require('@overleaf/metrics')
  19. const ProjectEntityUpdateHandler = require('../Project/ProjectEntityUpdateHandler')
  20. const ProjectOptionsHandler = require('../Project/ProjectOptionsHandler')
  21. const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
  22. const ProjectDeleter = require('../Project/ProjectDeleter')
  23. const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
  24. const EditorRealTimeController = require('./EditorRealTimeController')
  25. const async = require('async')
  26. const PublicAccessLevels = require('../Authorization/PublicAccessLevels')
  27. const _ = require('underscore')
  28. const { promisifyAll } = require('../../util/promises')
  29. const EditorController = {
  30. addDoc(project_id, folder_id, docName, docLines, source, user_id, callback) {
  31. if (callback == null) {
  32. callback = function () {}
  33. }
  34. return EditorController.addDocWithRanges(
  35. project_id,
  36. folder_id,
  37. docName,
  38. docLines,
  39. {},
  40. source,
  41. user_id,
  42. callback
  43. )
  44. },
  45. addDocWithRanges(
  46. project_id,
  47. folder_id,
  48. docName,
  49. docLines,
  50. docRanges,
  51. source,
  52. user_id,
  53. callback
  54. ) {
  55. if (callback == null) {
  56. callback = function () {}
  57. }
  58. docName = docName.trim()
  59. Metrics.inc('editor.add-doc')
  60. return ProjectEntityUpdateHandler.addDocWithRanges(
  61. project_id,
  62. folder_id,
  63. docName,
  64. docLines,
  65. docRanges,
  66. user_id,
  67. (err, doc, folder_id) => {
  68. if (err != null) {
  69. OError.tag(err, 'error adding doc without lock', {
  70. project_id,
  71. docName,
  72. })
  73. return callback(err)
  74. }
  75. EditorRealTimeController.emitToRoom(
  76. project_id,
  77. 'reciveNewDoc',
  78. folder_id,
  79. doc,
  80. source,
  81. user_id
  82. )
  83. return callback(err, doc)
  84. }
  85. )
  86. },
  87. addFile(
  88. project_id,
  89. folder_id,
  90. fileName,
  91. fsPath,
  92. linkedFileData,
  93. source,
  94. user_id,
  95. callback
  96. ) {
  97. if (callback == null) {
  98. callback = function () {}
  99. }
  100. fileName = fileName.trim()
  101. Metrics.inc('editor.add-file')
  102. return ProjectEntityUpdateHandler.addFile(
  103. project_id,
  104. folder_id,
  105. fileName,
  106. fsPath,
  107. linkedFileData,
  108. user_id,
  109. (err, fileRef, folder_id) => {
  110. if (err != null) {
  111. OError.tag(err, 'error adding file without lock', {
  112. project_id,
  113. folder_id,
  114. fileName,
  115. })
  116. return callback(err)
  117. }
  118. EditorRealTimeController.emitToRoom(
  119. project_id,
  120. 'reciveNewFile',
  121. folder_id,
  122. fileRef,
  123. source,
  124. linkedFileData,
  125. user_id
  126. )
  127. return callback(err, fileRef)
  128. }
  129. )
  130. },
  131. upsertDoc(
  132. project_id,
  133. folder_id,
  134. docName,
  135. docLines,
  136. source,
  137. user_id,
  138. callback
  139. ) {
  140. if (callback == null) {
  141. callback = function () {}
  142. }
  143. return ProjectEntityUpdateHandler.upsertDoc(
  144. project_id,
  145. folder_id,
  146. docName,
  147. docLines,
  148. source,
  149. user_id,
  150. function (err, doc, didAddNewDoc) {
  151. if (didAddNewDoc) {
  152. EditorRealTimeController.emitToRoom(
  153. project_id,
  154. 'reciveNewDoc',
  155. folder_id,
  156. doc,
  157. source,
  158. user_id
  159. )
  160. }
  161. return callback(err, doc)
  162. }
  163. )
  164. },
  165. upsertFile(
  166. project_id,
  167. folder_id,
  168. fileName,
  169. fsPath,
  170. linkedFileData,
  171. source,
  172. user_id,
  173. callback
  174. ) {
  175. if (callback == null) {
  176. callback = function () {}
  177. }
  178. return ProjectEntityUpdateHandler.upsertFile(
  179. project_id,
  180. folder_id,
  181. fileName,
  182. fsPath,
  183. linkedFileData,
  184. user_id,
  185. function (err, newFile, didAddFile, existingFile) {
  186. if (err != null) {
  187. return callback(err)
  188. }
  189. if (!didAddFile) {
  190. // replacement, so remove the existing file from the client
  191. EditorRealTimeController.emitToRoom(
  192. project_id,
  193. 'removeEntity',
  194. existingFile._id,
  195. source
  196. )
  197. }
  198. // now add the new file on the client
  199. EditorRealTimeController.emitToRoom(
  200. project_id,
  201. 'reciveNewFile',
  202. folder_id,
  203. newFile,
  204. source,
  205. linkedFileData,
  206. user_id
  207. )
  208. return callback(null, newFile)
  209. }
  210. )
  211. },
  212. upsertDocWithPath(
  213. project_id,
  214. elementPath,
  215. docLines,
  216. source,
  217. user_id,
  218. callback
  219. ) {
  220. return ProjectEntityUpdateHandler.upsertDocWithPath(
  221. project_id,
  222. elementPath,
  223. docLines,
  224. source,
  225. user_id,
  226. function (err, doc, didAddNewDoc, newFolders, lastFolder) {
  227. if (err != null) {
  228. return callback(err)
  229. }
  230. return EditorController._notifyProjectUsersOfNewFolders(
  231. project_id,
  232. newFolders,
  233. function (err) {
  234. if (err != null) {
  235. return callback(err)
  236. }
  237. if (didAddNewDoc) {
  238. EditorRealTimeController.emitToRoom(
  239. project_id,
  240. 'reciveNewDoc',
  241. lastFolder._id,
  242. doc,
  243. source,
  244. user_id
  245. )
  246. }
  247. return callback()
  248. }
  249. )
  250. }
  251. )
  252. },
  253. upsertFileWithPath(
  254. project_id,
  255. elementPath,
  256. fsPath,
  257. linkedFileData,
  258. source,
  259. user_id,
  260. callback
  261. ) {
  262. return ProjectEntityUpdateHandler.upsertFileWithPath(
  263. project_id,
  264. elementPath,
  265. fsPath,
  266. linkedFileData,
  267. user_id,
  268. function (
  269. err,
  270. newFile,
  271. didAddFile,
  272. existingFile,
  273. newFolders,
  274. lastFolder
  275. ) {
  276. if (err != null) {
  277. return callback(err)
  278. }
  279. return EditorController._notifyProjectUsersOfNewFolders(
  280. project_id,
  281. newFolders,
  282. function (err) {
  283. if (err != null) {
  284. return callback(err)
  285. }
  286. if (!didAddFile) {
  287. // replacement, so remove the existing file from the client
  288. EditorRealTimeController.emitToRoom(
  289. project_id,
  290. 'removeEntity',
  291. existingFile._id,
  292. source
  293. )
  294. }
  295. // now add the new file on the client
  296. EditorRealTimeController.emitToRoom(
  297. project_id,
  298. 'reciveNewFile',
  299. lastFolder._id,
  300. newFile,
  301. source,
  302. linkedFileData,
  303. user_id
  304. )
  305. return callback()
  306. }
  307. )
  308. }
  309. )
  310. },
  311. addFolder(project_id, folder_id, folderName, source, userId, callback) {
  312. if (callback == null) {
  313. callback = function () {}
  314. }
  315. folderName = folderName.trim()
  316. Metrics.inc('editor.add-folder')
  317. return ProjectEntityUpdateHandler.addFolder(
  318. project_id,
  319. folder_id,
  320. folderName,
  321. (err, folder, folder_id) => {
  322. if (err != null) {
  323. OError.tag(err, 'could not add folder', {
  324. project_id,
  325. folder_id,
  326. folderName,
  327. source,
  328. })
  329. return callback(err)
  330. }
  331. return EditorController._notifyProjectUsersOfNewFolder(
  332. project_id,
  333. folder_id,
  334. folder,
  335. userId,
  336. function (err) {
  337. if (err != null) {
  338. return callback(err)
  339. }
  340. return callback(null, folder)
  341. }
  342. )
  343. }
  344. )
  345. },
  346. mkdirp(project_id, path, callback) {
  347. if (callback == null) {
  348. callback = function () {}
  349. }
  350. logger.log({ project_id, path }, "making directories if they don't exist")
  351. return ProjectEntityUpdateHandler.mkdirp(
  352. project_id,
  353. path,
  354. (err, newFolders, lastFolder) => {
  355. if (err != null) {
  356. OError.tag(err, 'could not mkdirp', {
  357. project_id,
  358. path,
  359. })
  360. return callback(err)
  361. }
  362. return EditorController._notifyProjectUsersOfNewFolders(
  363. project_id,
  364. newFolders,
  365. function (err) {
  366. if (err != null) {
  367. return callback(err)
  368. }
  369. return callback(null, newFolders, lastFolder)
  370. }
  371. )
  372. }
  373. )
  374. },
  375. deleteEntity(project_id, entity_id, entityType, source, userId, callback) {
  376. if (callback == null) {
  377. callback = function () {}
  378. }
  379. Metrics.inc('editor.delete-entity')
  380. return ProjectEntityUpdateHandler.deleteEntity(
  381. project_id,
  382. entity_id,
  383. entityType,
  384. userId,
  385. function (err) {
  386. if (err != null) {
  387. OError.tag(err, 'could not delete entity', {
  388. project_id,
  389. entity_id,
  390. entityType,
  391. })
  392. return callback(err)
  393. }
  394. logger.log(
  395. { project_id, entity_id, entityType },
  396. 'telling users entity has been deleted'
  397. )
  398. EditorRealTimeController.emitToRoom(
  399. project_id,
  400. 'removeEntity',
  401. entity_id,
  402. source
  403. )
  404. return callback()
  405. }
  406. )
  407. },
  408. deleteEntityWithPath(project_id, path, source, user_id, callback) {
  409. return ProjectEntityUpdateHandler.deleteEntityWithPath(
  410. project_id,
  411. path,
  412. user_id,
  413. function (err, entity_id) {
  414. if (err != null) {
  415. return callback(err)
  416. }
  417. EditorRealTimeController.emitToRoom(
  418. project_id,
  419. 'removeEntity',
  420. entity_id,
  421. source
  422. )
  423. return callback(null, entity_id)
  424. }
  425. )
  426. },
  427. updateProjectDescription(project_id, description, callback) {
  428. if (callback == null) {
  429. callback = function () {}
  430. }
  431. logger.log({ project_id, description }, 'updating project description')
  432. return ProjectDetailsHandler.setProjectDescription(
  433. project_id,
  434. description,
  435. function (err) {
  436. if (err != null) {
  437. OError.tag(
  438. err,
  439. 'something went wrong setting the project description',
  440. {
  441. project_id,
  442. description,
  443. }
  444. )
  445. return callback(err)
  446. }
  447. EditorRealTimeController.emitToRoom(
  448. project_id,
  449. 'projectDescriptionUpdated',
  450. description
  451. )
  452. return callback()
  453. }
  454. )
  455. },
  456. deleteProject(project_id, callback) {
  457. Metrics.inc('editor.delete-project')
  458. return ProjectDeleter.deleteProject(project_id, callback)
  459. },
  460. renameEntity(project_id, entity_id, entityType, newName, userId, callback) {
  461. if (callback == null) {
  462. callback = function () {}
  463. }
  464. Metrics.inc('editor.rename-entity')
  465. return ProjectEntityUpdateHandler.renameEntity(
  466. project_id,
  467. entity_id,
  468. entityType,
  469. newName,
  470. userId,
  471. function (err) {
  472. if (err != null) {
  473. OError.tag(err, 'error renaming entity', {
  474. project_id,
  475. entity_id,
  476. entityType,
  477. newName,
  478. })
  479. return callback(err)
  480. }
  481. if (newName.length > 0) {
  482. EditorRealTimeController.emitToRoom(
  483. project_id,
  484. 'reciveEntityRename',
  485. entity_id,
  486. newName
  487. )
  488. }
  489. return callback()
  490. }
  491. )
  492. },
  493. moveEntity(project_id, entity_id, folder_id, entityType, userId, callback) {
  494. if (callback == null) {
  495. callback = function () {}
  496. }
  497. Metrics.inc('editor.move-entity')
  498. return ProjectEntityUpdateHandler.moveEntity(
  499. project_id,
  500. entity_id,
  501. folder_id,
  502. entityType,
  503. userId,
  504. function (err) {
  505. if (err != null) {
  506. OError.tag(err, 'error moving entity', {
  507. project_id,
  508. entity_id,
  509. folder_id,
  510. })
  511. return callback(err)
  512. }
  513. EditorRealTimeController.emitToRoom(
  514. project_id,
  515. 'reciveEntityMove',
  516. entity_id,
  517. folder_id
  518. )
  519. return callback()
  520. }
  521. )
  522. },
  523. renameProject(project_id, newName, callback) {
  524. if (callback == null) {
  525. callback = function () {}
  526. }
  527. return ProjectDetailsHandler.renameProject(
  528. project_id,
  529. newName,
  530. function (err) {
  531. if (err != null) {
  532. OError.tag(err, 'error renaming project', {
  533. project_id,
  534. newName,
  535. })
  536. return callback(err)
  537. }
  538. EditorRealTimeController.emitToRoom(
  539. project_id,
  540. 'projectNameUpdated',
  541. newName
  542. )
  543. return callback()
  544. }
  545. )
  546. },
  547. setCompiler(project_id, compiler, callback) {
  548. if (callback == null) {
  549. callback = function () {}
  550. }
  551. return ProjectOptionsHandler.setCompiler(
  552. project_id,
  553. compiler,
  554. function (err) {
  555. if (err != null) {
  556. return callback(err)
  557. }
  558. EditorRealTimeController.emitToRoom(
  559. project_id,
  560. 'compilerUpdated',
  561. compiler
  562. )
  563. return callback()
  564. }
  565. )
  566. },
  567. setImageName(project_id, imageName, callback) {
  568. if (callback == null) {
  569. callback = function () {}
  570. }
  571. return ProjectOptionsHandler.setImageName(
  572. project_id,
  573. imageName,
  574. function (err) {
  575. if (err != null) {
  576. return callback(err)
  577. }
  578. EditorRealTimeController.emitToRoom(
  579. project_id,
  580. 'imageNameUpdated',
  581. imageName
  582. )
  583. return callback()
  584. }
  585. )
  586. },
  587. setSpellCheckLanguage(project_id, languageCode, callback) {
  588. if (callback == null) {
  589. callback = function () {}
  590. }
  591. return ProjectOptionsHandler.setSpellCheckLanguage(
  592. project_id,
  593. languageCode,
  594. function (err) {
  595. if (err != null) {
  596. return callback(err)
  597. }
  598. EditorRealTimeController.emitToRoom(
  599. project_id,
  600. 'spellCheckLanguageUpdated',
  601. languageCode
  602. )
  603. return callback()
  604. }
  605. )
  606. },
  607. setPublicAccessLevel(project_id, newAccessLevel, callback) {
  608. if (callback == null) {
  609. callback = function () {}
  610. }
  611. return ProjectDetailsHandler.setPublicAccessLevel(
  612. project_id,
  613. newAccessLevel,
  614. function (err) {
  615. if (err != null) {
  616. return callback(err)
  617. }
  618. EditorRealTimeController.emitToRoom(
  619. project_id,
  620. 'project:publicAccessLevel:changed',
  621. { newAccessLevel }
  622. )
  623. if (newAccessLevel === PublicAccessLevels.TOKEN_BASED) {
  624. return ProjectDetailsHandler.ensureTokensArePresent(
  625. project_id,
  626. function (err, tokens) {
  627. if (err != null) {
  628. return callback(err)
  629. }
  630. EditorRealTimeController.emitToRoom(
  631. project_id,
  632. 'project:tokens:changed',
  633. { tokens }
  634. )
  635. return callback()
  636. }
  637. )
  638. } else {
  639. return callback()
  640. }
  641. }
  642. )
  643. },
  644. setRootDoc(project_id, newRootDocID, callback) {
  645. if (callback == null) {
  646. callback = function () {}
  647. }
  648. return ProjectEntityUpdateHandler.setRootDoc(
  649. project_id,
  650. newRootDocID,
  651. function (err) {
  652. if (err != null) {
  653. return callback(err)
  654. }
  655. EditorRealTimeController.emitToRoom(
  656. project_id,
  657. 'rootDocUpdated',
  658. newRootDocID
  659. )
  660. return callback()
  661. }
  662. )
  663. },
  664. _notifyProjectUsersOfNewFolders(project_id, folders, callback) {
  665. if (callback == null) {
  666. callback = function () {}
  667. }
  668. return async.eachSeries(
  669. folders,
  670. (folder, cb) =>
  671. EditorController._notifyProjectUsersOfNewFolder(
  672. project_id,
  673. folder.parentFolder_id,
  674. folder,
  675. null,
  676. cb
  677. ),
  678. callback
  679. )
  680. },
  681. _notifyProjectUsersOfNewFolder(
  682. project_id,
  683. folder_id,
  684. folder,
  685. userId,
  686. callback
  687. ) {
  688. if (callback == null) {
  689. callback = function () {}
  690. }
  691. EditorRealTimeController.emitToRoom(
  692. project_id,
  693. 'reciveNewFolder',
  694. folder_id,
  695. folder,
  696. userId
  697. )
  698. return callback()
  699. },
  700. }
  701. EditorController.promises = promisifyAll(EditorController)
  702. module.exports = EditorController