chat: Allow `chatNewMessage` hook to control rendering

This commit is contained in:
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:
* `message`: Provides access to the raw message object so that plugins can
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

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
server. It can be used to create different notifications for chat messages. Hook
functions can modify the `author`, `authorName`, `duration`, `sticky`, `text`,
and `timeStr` context properties to change how the message is processed. The
`text` and `timeStr` properties may contain HTML and come pre-sanitized; plugins
should be careful to sanitize any added user input to avoid introducing an XSS
vulnerability.
functions can modify the `author`, `authorName`, `duration`, `rendered`,
`sticky`, `text`, and `timeStr` context properties to change how the message is
processed. The `text` and `timeStr` properties may contain HTML and come
pre-sanitized; plugins should be careful to sanitize any added user input to
avoid introducing an XSS vulnerability.
Context properties:
@ -302,6 +302,11 @@ Context properties:
time correction and a default `userId` property if missing. Plugins must not
modify this object. Warning: Unlike `text`, `message.text` is not
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
own or just sit there until manually closed.
* `timestamp`: When the chat message was sent (milliseconds since epoch),

View File

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

View File

@ -79,5 +79,18 @@ describe('chat hooks', function () {
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);
});
});
});