chat: Allow `chatNewMessage` hook to control rendering

pull/5256/head
Richard Hansen 2021-10-25 20:44:35 -04:00
parent 2597b940f4
commit f1f4ed7c58
4 changed files with 27 additions and 6 deletions

View File

@ -54,6 +54,8 @@
* The `chatNewMessage` client-side hook context has new properties: * The `chatNewMessage` client-side hook context has new properties:
* `message`: Provides access to the raw message object so that plugins can * `message`: Provides access to the raw message object so that plugins can
see the original unprocessed message text and any added metadata. see the original unprocessed message text and any added metadata.
* `rendered`: Allows plugins to completely override how the message is
rendered in the UI.
# 1.8.14 # 1.8.14

View File

@ -285,11 +285,11 @@ Called from: `src/static/js/chat.js`
This hook runs on the client side whenever a chat message is received from the This hook runs on the client side whenever a chat message is received from the
server. It can be used to create different notifications for chat messages. Hook server. It can be used to create different notifications for chat messages. Hook
functions can modify the `author`, `authorName`, `duration`, `sticky`, `text`, functions can modify the `author`, `authorName`, `duration`, `rendered`,
and `timeStr` context properties to change how the message is processed. The `sticky`, `text`, and `timeStr` context properties to change how the message is
`text` and `timeStr` properties may contain HTML and come pre-sanitized; plugins processed. The `text` and `timeStr` properties may contain HTML and come
should be careful to sanitize any added user input to avoid introducing an XSS pre-sanitized; plugins should be careful to sanitize any added user input to
vulnerability. avoid introducing an XSS vulnerability.
Context properties: Context properties:
@ -302,6 +302,11 @@ Context properties:
time correction and a default `userId` property if missing. Plugins must not time correction and a default `userId` property if missing. Plugins must not
modify this object. Warning: Unlike `text`, `message.text` is not modify this object. Warning: Unlike `text`, `message.text` is not
pre-sanitized or processed in any way. pre-sanitized or processed in any way.
* `rendered` - Used to override the default message rendering. Initially set to
`null`. If the hook function sets this to a DOM element object or a jQuery
object, then that object will be used as the rendered message UI. Otherwise,
if this is set to `null`, then Etherpad will render a default UI for the
message using the other context properties.
* `sticky` (boolean): Whether the gritter notification should fade out on its * `sticky` (boolean): Whether the gritter notification should fade out on its
own or just sit there until manually closed. own or just sit there until manually closed.
* `timestamp`: When the chat message was sent (milliseconds since epoch), * `timestamp`: When the chat message was sent (milliseconds since epoch),

View File

@ -132,6 +132,7 @@ exports.chat = (() => {
author: msg.userId, author: msg.userId,
text: padutils.escapeHtmlWithClickableLinks(msg.text, '_blank'), text: padutils.escapeHtmlWithClickableLinks(msg.text, '_blank'),
message: msg, message: msg,
rendered: null,
sticky: false, sticky: false,
timestamp: msg.time, timestamp: msg.time,
timeStr: (() => { timeStr: (() => {
@ -164,7 +165,7 @@ exports.chat = (() => {
await hooks.aCallAll('chatNewMessage', ctx); await hooks.aCallAll('chatNewMessage', ctx);
const cls = authorClass(ctx.author); const cls = authorClass(ctx.author);
const chatMsg = $('<p>') const chatMsg = ctx.rendered != null ? $(ctx.rendered) : $('<p>')
.attr('data-authorId', ctx.author) .attr('data-authorId', ctx.author)
.addClass(cls) .addClass(cls)
.append($('<b>').text(`${ctx.authorName}:`)) .append($('<b>').text(`${ctx.authorName}:`))

View File

@ -79,5 +79,18 @@ describe('chat hooks', function () {
helper.sendChatMessage(`${msg}{enter}`), helper.sendChatMessage(`${msg}{enter}`),
]); ]);
}); });
it('`rendered` overrides default rendering', async function () {
let rendered;
await Promise.all([
checkHook('chatNewMessage', (context) => {
expect(context.rendered == null).to.be.ok();
rendered = context.rendered = helper.padChrome$.document.createElement('p');
rendered.append('message rendering overridden');
}),
helper.sendChatMessage(`${this.test.title}{enter}`),
]);
expect(helper.chatTextParagraphs().last()[0]).to.be(rendered);
});
}); });
}); });