diff --git a/ldap-overleaf-sl/Dockerfile b/ldap-overleaf-sl/Dockerfile index fec6ea2..0d702cd 100644 --- a/ldap-overleaf-sl/Dockerfile +++ b/ldap-overleaf-sl/Dockerfile @@ -36,7 +36,7 @@ COPY sharelatex/AuthenticationManager.js /overleaf/services/web/app/src/Featu COPY sharelatex/AuthenticationController.js /overleaf/services/web/app/src/Features/Authentication/ COPY sharelatex/ContactController.js /overleaf/services/web/app/src/Features/Contacts/ COPY sharelatex/ProjectEditorHandler.js /overleaf/services/web/app/src/Features/Project/ -COPY sharelatex/TrackChangesController.js /overleaf/services/web/app/src/Features/TrackChanges/ +COPY sharelatex_diff/TrackChangesController.js /overleaf/services/web/app/src/Features/TrackChanges/ COPY sharelatex/router.js /overleaf/services/web/app/src/router.js ## Copy some new files from the Docker git repo (because they are not in the current Docker release - to enable Track-Changes diff --git a/ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js b/ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js new file mode 100644 index 0000000..7151cb8 --- /dev/null +++ b/ldap-overleaf-sl/sharelatex_diff/TrackChangesController.js @@ -0,0 +1,308 @@ +const ChatApiHandler = require('../Chat/ChatApiHandler') +const ChatManager = require('../Chat/ChatManager') +const EditorRealTimeController = require('../Editor/EditorRealTimeController') +const SessionManager = require('../Authentication/SessionManager') +const UserInfoManager = require('../User/UserInfoManager') +const DocstoreManager = require('../Docstore/DocstoreManager') +const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler') +const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter') +const { Project } = require('../../models/Project') +const pLimit = require('p-limit') + +async function _updateTCState (projectId, state, callback) { + await Project.updateOne({_id: projectId}, {track_changes: state}).exec() + callback() +} +function _transformId(doc) { + if (doc._id) { + doc.id = doc._id; + delete doc._id; + } + return doc; +} + +const TrackChangesController = { + trackChanges(req, res, next) { + const { project_id } = req.params + let state = req.body.on || req.body.on_for + if ( req.body.on_for_guests && !req.body.on ) state.__guests__ = true + + return _updateTCState(project_id, state, + function (err, message) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'toggle-track-changes', + state + ) + return res.sendStatus(204) + } + ) + }, + acceptChanges(req, res, next) { + const { project_id, doc_id } = req.params + const change_ids = req.body.change_ids + return DocumentUpdaterHandler.acceptChanges( + project_id, + doc_id, + change_ids, + function (err, message) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'accept-changes', + doc_id, + change_ids, + ) + return res.sendStatus(204) + } + ) + }, + async getAllRanges(req, res, next) { + const { project_id } = req.params + // FIXME: ranges are from mongodb, probably already outdated + const ranges = await DocstoreManager.promises.getAllRanges(project_id) +// frontend expects 'id', not '_id' + return res.json(ranges.map(_transformId)) + }, + async getChangesUsers(req, res, next) { + const { project_id } = req.params + const memberIds = await CollaboratorsGetter.promises.getMemberIds(project_id) + // FIXME: Does not work properly if the user is no longer a member of the project + // memberIds from DocstoreManager.getAllRanges(project_id) is not a remedy + // because ranges are not updated in real-time + const limit = pLimit(3) + const users = await Promise.all( + memberIds.map(memberId => + limit(async () => { + const user = await UserInfoManager.promises.getPersonalInfo(memberId) + return user + }) + ) + ) + users.push({_id: null}) // An anonymous user won't cause any harm +// frontend expects 'id', not '_id' + return res.json(users.map(_transformId)) + }, + getThreads(req, res, next) { + const { project_id } = req.params + return ChatApiHandler.getThreads( + project_id, + function (err, messages) { + if (err != null) { + return next(err) + } + return ChatManager.injectUserInfoIntoThreads( + messages, + function (err) { + if (err != null) { + return next(err) + } + return res.json(messages) + } + ) + } + ) + }, + sendComment(req, res, next) { + const { project_id, thread_id } = req.params + const { content } = req.body + const user_id = SessionManager.getLoggedInUserId(req.session) + if (user_id == null) { + const err = new Error('no logged-in user') + return next(err) + } + return ChatApiHandler.sendComment( + project_id, + thread_id, + user_id, + content, + function (err, message) { + if (err != null) { + return next(err) + } + return UserInfoManager.getPersonalInfo( + user_id, + function (err, user) { + if (err != null) { + return next(err) + } + message.user = user + EditorRealTimeController.emitToRoom( + project_id, + 'new-comment', + thread_id, message + ) + return res.sendStatus(204) + } + ) + } + ) + }, + editMessage(req, res, next) { + const { project_id, thread_id, message_id } = req.params + const { content } = req.body + const user_id = SessionManager.getLoggedInUserId(req.session) + if (user_id == null) { + const err = new Error('no logged-in user') + return next(err) + } + return ChatApiHandler.editMessage( + project_id, + thread_id, + message_id, + user_id, + content, + function (err, message) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'edit-message', + thread_id, + message_id, + content + ) + return res.sendStatus(204) + } + ) + }, + deleteMessage(req, res, next) { + const { project_id, thread_id, message_id } = req.params + return ChatApiHandler.deleteMessage( + project_id, + thread_id, + message_id, + function (err, message) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'delete-message', + thread_id, + message_id + ) + return res.sendStatus(204) + } + ) + }, + resolveThread(req, res, next) { + const { project_id, doc_id, thread_id } = req.params + const user_id = SessionManager.getLoggedInUserId(req.session) + if (user_id == null) { + const err = new Error('no logged-in user') + return next(err) + } + DocumentUpdaterHandler.resolveThread( + project_id, + doc_id, + thread_id, + user_id, + function (err, message) { + if (err != null) { + return next(err) + } + } + ) + return ChatApiHandler.resolveThread( + project_id, + thread_id, + user_id, + function (err, message) { + if (err != null) { + return next(err) + } + return UserInfoManager.getPersonalInfo( + user_id, + function (err, user) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'resolve-thread', + thread_id, + user_id + ) + return res.sendStatus(204) + } + ) + } + ) + }, + reopenThread(req, res, next) { + const { project_id, doc_id, thread_id } = req.params + const user_id = SessionManager.getLoggedInUserId(req.session) + if (user_id == null) { + const err = new Error('no logged-in user') + return next(err) + } + DocumentUpdaterHandler.reopenThread( + project_id, + doc_id, + thread_id, + user_id, + function (err, message) { + if (err != null) { + return next(err) + } + } + ) + return ChatApiHandler.reopenThread( + project_id, + thread_id, + function (err, message) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'reopen-thread', + thread_id + ) + return res.sendStatus(204) + } + ) + }, + deleteThread(req, res, next) { + const { project_id, doc_id, thread_id } = req.params + const user_id = SessionManager.getLoggedInUserId(req.session) + if (user_id == null) { + const err = new Error('no logged-in user') + return next(err) + } + return DocumentUpdaterHandler.deleteThread( + project_id, + doc_id, + thread_id, + user_id, + function (err, message) { + if (err != null) { + return next(err) + } + ChatApiHandler.deleteThread( + project_id, + thread_id, + function (err, message) { + if (err != null) { + return next(err) + } + EditorRealTimeController.emitToRoom( + project_id, + 'delete-thread', + thread_id + ) + return res.sendStatus(204) + } + ) + } + ) + }, +} +module.exports = TrackChangesController