diff --git a/CHANGELOG.md b/CHANGELOG.md
index 65822cd14..80dc6b6f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,6 +50,10 @@
* The `helper.aNewPad()` function (accessible to client-side tests) now
accepts hook functions to inject when opening a pad. This can be used to
test any new client-side hooks your plugin provides.
+ * Chat improvements:
+ * 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.
# 1.8.14
diff --git a/doc/api/hooks_client-side.md b/doc/api/hooks_client-side.md
index 36c5d67ce..031d490e1 100755
--- a/doc/api/hooks_client-side.md
+++ b/doc/api/hooks_client-side.md
@@ -296,7 +296,12 @@ Context properties:
* `authorName`: The display name of the user that wrote the message.
* `author`: The author ID of the user that wrote the message.
* `text`: Sanitized message HTML, with URLs wrapped like `url`.
+ href="url">url`. (Note that `message.text` is not sanitized or processed
+ in any way.)
+* `message`: The raw message object as received from the server, except with
+ 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.
* `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),
diff --git a/src/static/js/chat.js b/src/static/js/chat.js
index 7ec3abdf0..c9e8046ba 100755
--- a/src/static/js/chat.js
+++ b/src/static/js/chat.js
@@ -131,6 +131,7 @@ exports.chat = (() => {
authorName: msg.userName != null ? msg.userName : html10n.get('pad.userlist.unnamed'),
author: msg.userId,
text: padutils.escapeHtmlWithClickableLinks(msg.text, '_blank'),
+ message: msg,
sticky: false,
timestamp: msg.time,
timeStr: (() => {
diff --git a/src/tests/frontend/specs/chat_hooks.js b/src/tests/frontend/specs/chat_hooks.js
index 2a0307a80..314ddfc6b 100644
--- a/src/tests/frontend/specs/chat_hooks.js
+++ b/src/tests/frontend/specs/chat_hooks.js
@@ -60,5 +60,24 @@ describe('chat hooks', function () {
]);
});
}
+
+ it('message is an object', async function () {
+ await Promise.all([
+ checkHook('chatNewMessage', ({message}) => {
+ expect(message).to.be.an('object');
+ }),
+ helper.sendChatMessage(`${this.test.title}{enter}`),
+ ]);
+ });
+
+ it('message.text is not processed', async function () {
+ const msg = ' https://etherpad.org';
+ await Promise.all([
+ checkHook('chatNewMessage', ({message: {text}}) => {
+ expect(text).to.equal(`${msg}\n`);
+ }),
+ helper.sendChatMessage(`${msg}{enter}`),
+ ]);
+ });
});
});