diff --git a/CHANGELOG.md b/CHANGELOG.md index a773dca9a..6815d681d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,9 @@ `authorId` context property instead. Also, the hook now runs asynchronously. * Chat API deprecations and removals (no replacements planned): * Server-side: + * The `Pad.appendChatMessage()` method is deprecated. + * The `Pad.getChatMessage()` method is deprecated. + * The `Pad.getChatMessages()` method is deprecated. * The `sendChatMessageToPadClients()` function in `src/node/handler/PadMessageHandler.js` is deprecated. * Client-side: diff --git a/src/node/chat.js b/src/node/chat.js index bd78a93f2..3420df644 100644 --- a/src/node/chat.js +++ b/src/node/chat.js @@ -4,17 +4,53 @@ const ChatMessage = require('../static/js/ChatMessage'); const api = require('./db/API'); const authorManager = require('./db/AuthorManager'); const hooks = require('../static/js/pluginfw/hooks.js'); +const pad = require('./db/Pad'); const padManager = require('./db/PadManager'); const padMessageHandler = require('./handler/PadMessageHandler'); let socketio; +const appendChatMessage = async (pad, msg) => { + pad.chatHead++; + await Promise.all([ + // Don't save the display name in the database because the user can change it at any time. The + // `displayName` property will be populated with the current value when the message is read from + // the database. + pad.db.set(`pad:${pad.id}:chat:${pad.chatHead}`, {...msg, displayName: undefined}), + pad.saveToDatabase(), + ]); +}; + +const getChatMessage = async (pad, entryNum) => { + const entry = await pad.db.get(`pad:${pad.id}:chat:${entryNum}`); + if (entry == null) return null; + const message = ChatMessage.fromObject(entry); + message.displayName = await authorManager.getAuthorName(message.authorId); + return message; +}; + +const getChatMessages = async (pad, start, end) => { + const entries = await Promise.all( + [...Array(end + 1 - start).keys()].map((i) => getChatMessage(pad, start + i))); + + // sort out broken chat entries + // it looks like in happened in the past that the chat head was + // incremented, but the chat message wasn't added + return entries.filter((entry) => { + const pass = (entry != null); + if (!pass) { + console.warn(`WARNING: Found broken chat entry in pad ${pad.id}`); + } + return pass; + }); +}; + const sendChatMessageToPadClients = async (message, padId) => { const pad = await padManager.getPad(padId, null, message.authorId); await hooks.aCallAll('chatNewMessage', {message, pad, padId}); - // pad.appendChatMessage() ignores the displayName property so we don't need to wait for + // appendChatMessage() ignores the displayName property so we don't need to wait for // authorManager.getAuthorName() to resolve before saving the message to the database. - const promise = pad.appendChatMessage(message); + const promise = appendChatMessage(pad, message); message.displayName = await authorManager.getAuthorName(message.authorId); socketio.sockets.in(padId).json.send({ type: 'COLLABROOM', @@ -91,7 +127,7 @@ exports.handleMessage = async (hookName, {message, sessionInfo, socket}) => { type: 'COLLABROOM', data: { type: 'CHAT_MESSAGES', - messages: await pad.getChatMessages(start, end), + messages: await getChatMessages(pad, start, end), }, }); break; @@ -107,9 +143,16 @@ exports.socketio = (hookName, {io}) => { }; api.registerChatHandlers({ + getChatMessages, sendChatMessageToPadClients, }); +pad.registerLegacyChatMethodHandlers({ + appendChatMessage, + getChatMessage, + getChatMessages, +}); + padMessageHandler.registerLegacyChatHandlers({ sendChatMessageToPadClients, }); diff --git a/src/node/db/API.js b/src/node/db/API.js index 65b23e752..c2c51547f 100644 --- a/src/node/db/API.js +++ b/src/node/db/API.js @@ -338,7 +338,7 @@ exports.getChatHistory = async (padID, start, end) => { } // the the whole message-log and return it to the client - const messages = await pad.getChatMessages(start, end); + const messages = await chat.getChatMessages(pad, start, end); return {messages}; }; diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index b692962f1..4a18ddcff 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -22,6 +22,10 @@ const hooks = require('../../static/js/pluginfw/hooks'); const {padutils: {warnDeprecated}} = require('../../static/js/pad_utils'); const promises = require('../utils/promises'); +let chat = null; + +exports.registerLegacyChatMethodHandlers = (handlers) => chat = handlers; + /** * Copied from the Etherpad source code. It converts Windows line breaks to Unix * line breaks and convert Tabs to spaces @@ -287,6 +291,7 @@ class Pad { /** * Adds a chat message to the pad, including saving it to the database. * + * @deprecated * @param {(ChatMessage|string)} msgOrText - Either a chat message object (recommended) or a * string containing the raw text of the user's chat message (deprecated). * @param {?string} [authorId] - The user's author ID. Deprecated; use `msgOrText.authorId` @@ -295,31 +300,24 @@ class Pad { * `msgOrText.time` instead. */ async appendChatMessage(msgOrText, authorId = null, time = null) { + warnDeprecated('Pad.appendChatMessage() is deprecated'); const msg = msgOrText instanceof ChatMessage ? msgOrText : new ChatMessage(msgOrText, authorId, time); - this.chatHead++; - await Promise.all([ - // Don't save the display name in the database because the user can change it at any time. The - // `displayName` property will be populated with the current value when the message is read - // from the database. - this.db.set(`pad:${this.id}:chat:${this.chatHead}`, {...msg, displayName: undefined}), - this.saveToDatabase(), - ]); + await chat.appendChatMessage(this, msg); } /** + * @deprecated * @param {number} entryNum - ID of the desired chat message. * @returns {?ChatMessage} */ async getChatMessage(entryNum) { - const entry = await this.db.get(`pad:${this.id}:chat:${entryNum}`); - if (entry == null) return null; - const message = ChatMessage.fromObject(entry); - message.displayName = await authorManager.getAuthorName(message.authorId); - return message; + warnDeprecated('Pad.getChatMessage() is deprecated'); + return await chat.getChatMessage(this, entryNum); } /** + * @deprecated * @param {number} start - ID of the first desired chat message. * @param {number} end - ID of the last desired chat message. * @returns {ChatMessage[]} Any existing messages with IDs between `start` (inclusive) and `end` @@ -327,19 +325,8 @@ class Pad { * interval as is typical in code. */ async getChatMessages(start, end) { - const entries = - await Promise.all(Stream.range(start, end + 1).map(this.getChatMessage.bind(this))); - - // sort out broken chat entries - // it looks like in happened in the past that the chat head was - // incremented, but the chat message wasn't added - return entries.filter((entry) => { - const pass = (entry != null); - if (!pass) { - console.warn(`WARNING: Found broken chat entry in pad ${this.id}`); - } - return pass; - }); + warnDeprecated('Pad.getChatMessages() is deprecated'); + return await chat.getChatMessages(this, start, end); } async init(text, authorId = '') { @@ -709,7 +696,7 @@ class Pad { const chats = Stream.range(0, this.chatHead + 1) .map(async (c) => { try { - const msg = await this.getChatMessage(c); + const msg = await chat.getChatMessage(this, c); assert(msg != null); assert(msg instanceof ChatMessage); } catch (err) {