diff --git a/modules/ROOT/examples/live-demos/comments-callback/example.js b/modules/ROOT/examples/live-demos/comments-callback/example.js
deleted file mode 100644
index 411c385ae8..0000000000
--- a/modules/ROOT/examples/live-demos/comments-callback/example.js
+++ /dev/null
@@ -1,214 +0,0 @@
-/********************************
- * Tiny Comments functions *
- * (must call "done" or "fail") *
- ********************************/
-
-function tinycomments_create(req, done, fail) {
- let content = req.content;
- let createdAt = req.createdAt;
-
- fetch('https://api.example/conversations/', {
- method: 'POST',
- body: JSON.stringify({ content: content, createdAt: createdAt }),
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- })
- .then((response) => {
- if (!response.ok) {
- throw new Error('Failed to create comment');
- }
- return response.json();
- })
- .then((req2) => {
- let conversationUid = req2.conversationUid;
- done({ conversationUid: conversationUid });
- })
- .catch((e) => {
- fail(e);
- });
-}
-
-function tinycomments_reply(req, done, fail) {
- let conversationUid = req.conversationUid;
- let content = req.content;
- let createdAt = req.createdAt;
-
- fetch('https://api.example/conversations/' + conversationUid, {
- method: 'POST',
- body: JSON.stringify({ content: content, createdAt: createdAt }),
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- })
- .then((response) => {
- if (!response.ok) {
- throw new Error('Failed to reply to comment');
- }
- return response.json();
- })
- .then((req2) => {
- let commentUid = req2.commentUid;
- done({ commentUid: commentUid });
- })
- .catch((e) => {
- fail(e);
- });
-}
-
-function tinycomments_edit_comment(req, done, fail) {
- let conversationUid = req.conversationUid;
- let commentUid = req.commentUid;
- let content = req.content;
- let modifiedAt = req.modifiedAt;
-
- fetch(
- 'https://api.example/conversations/' + conversationUid + '/' + commentUid,
- {
- method: 'PUT',
- body: JSON.stringify({ content: content, modifiedAt: modifiedAt }),
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- }
- )
- .then((response) => {
- if (!response.ok) {
- throw new Error('Failed to edit comment');
- }
- return response.json();
- })
- .then((req2) => {
- let canEdit = req2.canEdit;
- done({ canEdit: canEdit });
- })
- .catch((e) => {
- fail(e);
- });
-}
-
-function tinycomments_delete(req, done, fail) {
- let conversationUid = req.conversationUid;
- fetch('https://api.example/conversations/' + conversationUid, {
- method: 'DELETE',
- }).then((response) => {
- if (response.ok) {
- done({ canDelete: true });
- } else if (response.status === 403) {
- done({ canDelete: false });
- } else {
- fail(new Error('Something has gone wrong...'));
- }
- });
-}
-
-function tinycomments_delete_all(_req, done, fail) {
- fetch('https://api.example/conversations', {
- method: 'DELETE',
- }).then((response) => {
- if (response.ok) {
- done({ canDelete: true });
- } else if (response.status === 403) {
- done({ canDelete: false });
- } else {
- fail(new Error('Something has gone wrong...'));
- }
- });
-}
-
-function tinycomments_delete_comment(req, done, fail) {
- let conversationUid = req.conversationUid;
- let commentUid = req.commentUid;
-
- fetch(
- 'https://api.example/conversations/' + conversationUid + '/' + commentUid,
- {
- method: 'DELETE',
- }
- ).then((response) => {
- if (response.ok) {
- done({ canDelete: true });
- } else if (response.status === 403) {
- done({ canDelete: false });
- } else {
- fail(new Error('Something has gone wrong...'));
- }
- });
-}
-
-function tinycomments_lookup({ conversationUid }, done, fail) {
- let lookup = async function () {
- let convResp = await fetch(
- 'https://api.example/conversations/' + conversationUid
- );
- if (!convResp.ok) {
- throw new Error('Failed to get conversation');
- }
- let comments = await convResp.json();
- let usersResp = await fetch('https://api.example/users/');
- if (!usersResp.ok) {
- throw new Error('Failed to get users');
- }
- let { users } = await usersResp.json();
- let getUser = function (userId) {
- return users.find((u) => {
- return u.id === userId;
- });
- };
- return {
- conversation: {
- uid: conversationUid,
- comments: comments.map((comment) => {
- return {
- ...comment,
- content: comment.content,
- authorName: getUser(comment.author)?.displayName,
- };
- }),
- },
- };
- };
- lookup()
- .then((data) => {
- console.log('Lookup success ' + conversationUid, data);
- done(data);
- })
- .catch((err) => {
- console.error('Lookup failure ' + conversationUid, err);
- fail(err);
- });
-}
-
-tinymce.init({
- selector: 'textarea#comments-callback',
- height: 800,
- plugins: 'paste code tinycomments help lists',
- toolbar:
- 'undo redo | formatselect | ' +
- 'bold italic backcolor | alignleft aligncenter ' +
- 'alignright alignjustify | bullist numlist outdent indent | ' +
- 'removeformat | addcomment showcomments | help',
- menubar: 'file edit view insert format tc',
- menu: {
- tc: {
- title: 'Comments',
- items: 'addcomment showcomments deleteallconversations',
- },
- },
- tinycomments_create,
- tinycomments_reply,
- tinycomments_edit_comment,
- tinycomments_delete,
- tinycomments_delete_all,
- tinycomments_delete_comment,
- tinycomments_lookup,
- /* The following setup callback opens the comments sidebar when the editor loads */
- setup: function (editor) {
- editor.on('SkinLoaded', () => {
- editor.execCommand('ToggleSidebar', false, 'showcomments');
- });
- },
-});
diff --git a/modules/ROOT/examples/live-demos/comments-callback/index.html b/modules/ROOT/examples/live-demos/comments-callback/index.html
index bf495c1fb6..92c933f6e6 100644
--- a/modules/ROOT/examples/live-demos/comments-callback/index.html
+++ b/modules/ROOT/examples/live-demos/comments-callback/index.html
@@ -1,32 +1,32 @@
diff --git a/modules/ROOT/examples/live-demos/comments-callback/index.js b/modules/ROOT/examples/live-demos/comments-callback/index.js
index 3ae3b58072..27eabeaeb0 100644
--- a/modules/ROOT/examples/live-demos/comments-callback/index.js
+++ b/modules/ROOT/examples/live-demos/comments-callback/index.js
@@ -1,679 +1,225 @@
-tinymce.ScriptLoader.loadScripts(
- [
- '//unpkg.com/@pollyjs/core@5.1.1',
- '//unpkg.com/@pollyjs/adapter-fetch@5.1.1',
- '//unpkg.com/@pollyjs/persister-local-storage@5.1.1',
- ],
- () => {
- /******************************
- * Mock server implementation *
- ******************************/
-
- let { Polly } = window['@pollyjs/core'];
- let FetchAdapter = window['@pollyjs/adapter-fetch'];
- let LocalStoragePersister = window['@pollyjs/persister-local-storage'];
-
- Polly.register(FetchAdapter);
- Polly.register(LocalStoragePersister);
- let polly = new Polly('test', {
- adapters: ['fetch'],
- persister: 'local-storage',
- logging: true,
- });
- let server = polly.server;
-
- /* this would be an admin for the file, they're allowed to do all operations */
- function getOwner() {
- return localStorage.getItem('owner') ?? users[0].id;
- }
-
- /* Server knows the author, probably by cookie or JWT token */
- function getAuthor() {
- return localStorage.getItem('author') ?? users[0].id;
- }
-
- /* this would be an admin for the file, they're allowed to do all operations */
- function setOwner(user) {
- localStorage.setItem('owner', user) ?? users[0].id;
- }
-
- /* Server knows the author, probably by cookie or JWT token */
- function setAuthor(user) {
- localStorage.setItem('author', user) ?? users[0].id;
- }
-
- function randomString() {
- const randomArray = new Uint32Array(20);
- return window.crypto.getRandomValues(randomArray)[0];
- }
-
- /* Our server "database" */
- function getDB() {
- return JSON.parse(localStorage.getItem('fakedb') ?? '{}');
- }
- function setDB(data) {
- localStorage.setItem('fakedb', JSON.stringify(data));
- }
-
- function getConversation(uid) {
- let store = getDB();
- console.log('DB get:', uid, store[uid]);
- return store[uid];
- }
-
- function setConversation(uid, conversation) {
- let store = getDB();
- console.log('DB set:', uid, store[uid], conversation);
- store[uid] = conversation;
- setDB(store);
- }
-
- function deleteConversation(uid) {
- let store = getDB();
- console.log('DB delete:', uid);
- delete store[uid];
- setDB(store);
- }
-
- function deleteAllConversations() {
- console.log('DB delete all');
- let store = {};
- setDB(store);
- }
-
- server.host('https://api.example', () => {
- /* create new conversation */
- server.post('/conversations/').intercept((req, res) => {
- let author = getAuthor();
- let { content, createdAt } = JSON.parse(req.body);
- console.log(req.body);
- try {
- let conversationUid = randomString();
- setConversation(conversationUid, [
- {
- author,
- createdAt,
- modifiedAt: createdAt,
- content,
- uid: conversationUid /* first comment has same uid as conversation */,
- },
- ]);
- res.status(201).json({ conversationUid });
- } catch (e) {
- console.log('Server error:', e);
- res.status(500);
- }
- });
-
- /* add new comment to conversation */
- server.post('/conversations/:conversationUid').intercept((req, res) => {
- let author = getAuthor();
- let { content, createdAt } = JSON.parse(req.body);
- let conversationUid = req.params.conversationUid;
- try {
- let conversation = getConversation(conversationUid);
- let commentUid = randomString();
- setConversation(
- conversationUid,
- conversation.concat([
- {
- author,
- createdAt,
- modifiedAt: createdAt,
- content,
- uid: commentUid,
- },
- ])
- );
- res.status(201).json({ commentUid });
- } catch (e) {
- console.log('Server error:', e);
- res.status(500);
- }
- });
-
- /* edit a comment */
- server
- .put('/conversations/:conversationUid/:commentUid')
- .intercept((req, res) => {
- let author = getAuthor();
- let { content, modifiedAt } = JSON.parse(req.body);
- let conversationUid = req.params.conversationUid;
- let commentUid = req.params.commentUid;
-
- try {
- let conversation = getConversation(conversationUid);
- let commentIndex = conversation.findIndex((comment) => {
- return comment.uid === commentUid;
- });
- let comment = conversation[commentIndex];
- let canEdit = comment.author === author;
- if (canEdit) {
- setConversation(conversationUid, [
- ...conversation.slice(0, commentIndex),
- {
- ...comment,
- content,
- modifiedAt,
- },
- ...conversation.slice(commentIndex + 1),
- ]);
- }
- res.status(201).json({ canEdit });
- } catch (e) {
- console.log('Server error:', e);
- res.status(500);
- }
- });
-
- /* delete a comment */
- server
- .delete('/conversations/:conversationUid/:commentUid')
- .intercept((req, res) => {
- let author = getAuthor();
- let owner = getOwner();
- let conversationUid = req.params.conversationUid;
- let commentUid = req.params.commentUid;
- let conversation = getConversation(conversationUid);
- if (!conversation) {
- res.status(404);
- }
- let commentIndex = conversation.findIndex((comment) => {
- return comment.uid === commentUid;
- });
- if (commentIndex === -1) {
- res.status(404);
- }
- if (
- conversation[commentIndex].author === author ||
- author === owner
- ) {
- setConversation(conversationUid, [
- ...conversation.slice(0, commentIndex),
- ...conversation.slice(commentIndex + 1),
- ]);
- res.status(204);
- } else {
- res.status(403);
- }
- });
-
- /* delete a conversation */
- server.delete('/conversations/:conversationUid').intercept((req, res) => {
- let author = getAuthor();
- let owner = getOwner();
- let conversationUid = req.params.conversationUid;
- let conversation = getConversation(conversationUid);
- if (conversation) {
- if (conversation[0].author === author || author === owner) {
- deleteConversation(conversationUid);
- res.status(204);
- } else {
- res.status(403);
- }
- } else {
- res.status(404);
- }
- });
-
- /* delete all conversations */
- server.delete('/conversations').intercept((req, res) => {
- let author = getAuthor();
- let owner = getOwner();
- if (author === owner) {
- deleteAllConversations();
- res.status(204);
- } else {
- res.status(403);
- }
- });
-
- /* lookup a conversation */
- server.get('/conversations/:conversationUid').intercept((req, res) => {
- let conversation = getConversation(req.params.conversationUid);
- if (conversation) {
- res.status(200).json(conversation);
- } else {
- res.status(404);
- }
- });
-
- /* lookup users */
- server.get('/users/').intercept((req, res) => {
- res.status(200).json({
- users,
- });
- });
- }); /* server.host */
-
- /* Connect using the `connectTo` API */
- polly.connectTo('fetch');
-
- /************************************************
- * Fake Users and associated pickers *
- * Should be based on sessions and backend data *
- ***********************************************/
-
- const users = [
- { id: 'alex', displayName: 'Alex' },
- { id: 'jessie', displayName: 'Jessie' },
- { id: 'sam', displayName: 'Sam' },
- ];
-
- /* Set initial Owner */
- setOwner(users[2].id);
-
- /* Set initial Author */
- setAuthor(users[0].id);
-
- /********************************
- * Tiny Comments functions *
- * (must call "done" or "fail") *
- ********************************/
-
- /**
- * Callback for when the operation was successful.
- * @template T
- * @callback done
- * @param {T} data - the data
- * @returns {void}
- */
-
- /**
- * Callback for when the operation failed.
- * @callback fail
- * @param {string|Error} error - the reason for the failure
- * @returns {void}
- */
-
- /**
- * The data supplied to create a comment.
- * @typedef {Object} TinyCommentsCreateReq
- * @property {string} content - comment content
- * @property {string} createdAt - ISO creation date
- */
-
- /**
- * The response returned when a comment was created on the server.
- * @typedef {Object} TinyCommentsCreateResp
- * @property {string} conversationUid - ID of created comment
- * @property {?fail} onError - error callback to call when the comment can't be put into the document
- * @property {?done} onSuccess - success callback to call when the comment is put into the document
- */
-
- /**
- * Conversation "create" function. Saves the comment as a new conversation,
- * and asynchronously returns a conversation unique ID via the "done"
- * callback.
- *
- * @param {TinyCommentsCreateReq} req - the comment to create
- * @param {done} done - callback to call when the comment is created on the server
- * @param {fail} fail - callback to call when something fails
- */
- function tinycomments_create(req, done, fail) {
- let content = req.content;
- let createdAt = req.createdAt;
-
- fetch('https://api.example/conversations/', {
- method: 'POST',
- body: JSON.stringify({ content: content, createdAt: createdAt }),
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- })
- .then((response) => {
- if (!response.ok) {
- throw new Error('Failed to create comment');
- }
- return response.json();
- })
- .then((req2) => {
- let conversationUid = req2.conversationUid;
- done({ conversationUid: conversationUid });
- })
- .catch((e) => {
- fail(e);
- });
- }
-
- /**
- *
- * @typedef {Object} TinyCommentsReplyReq
- * @property {string} conversationUid
- * @property {string} content
- * @property {string} createdAt
- */
-
- /**
- *
- * @typedef {Object} TinyCommentsReplyResp
- * @property {string} commentUid
- */
-
- /**
- * Conversation "reply" function. Saves the comment as a reply to the
- * an existing conversation, and asynchronously returns via the "done"
- * callback when finished.
- *
- * @param {TinyCommentsReplyReq} req - the comment to append
- * @param {done} done - callback to call when the comment is created on the server
- * @param {fail} fail - callback to call when something fails
- */
- function tinycomments_reply(req, done, fail) {
- let conversationUid = req.conversationUid;
- let content = req.content;
- let createdAt = req.createdAt;
-
- fetch('https://api.example/conversations/' + conversationUid, {
- method: 'POST',
- body: JSON.stringify({ content: content, createdAt: createdAt }),
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- })
- .then((response) => {
- if (!response.ok) {
- throw new Error('Failed to reply to comment');
- }
- return response.json();
- })
- .then((req2) => {
- let commentUid = req2.commentUid;
- done({ commentUid: commentUid });
- })
- .catch((e) => {
- fail(e);
- });
- }
- /**
- *
- * @typedef {Object} TinyCommentsEditReq
- * @property {string} conversationUid
- * @property {string} commentUid
- * @property {string} content
- * @property {string} modifiedAt
- */
-
- /**
- *
- * @typedef {Object} TinyCommentsEditResp
- * @property {boolean} canEdit
- * @property {?string} reason
- */
-
- /**
- *
- * @param {TinyCommentsEditReq} req
- * @param {done} done
- * @param {fail} fail
- */
- function tinycomments_edit_comment(req, done, fail) {
- let conversationUid = req.conversationUid;
- let commentUid = req.commentUid;
- let content = req.content;
- let modifiedAt = req.modifiedAt;
-
- fetch(
- 'https://api.example/conversations/' +
- conversationUid +
- '/' +
- commentUid,
- {
- method: 'PUT',
- body: JSON.stringify({ content: content, modifiedAt: modifiedAt }),
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json',
- },
- }
- )
- .then((response) => {
- if (!response.ok) {
- throw new Error('Failed to edit comment');
- }
- return response.json();
- })
- .then((req2) => {
- let canEdit = req2.canEdit;
- done({ canEdit: canEdit });
- })
- .catch((e) => {
- fail(e);
- });
- }
-
- /**
- *
- * @typedef TinyCommentsDeleteReq
- * @property {string} conversationUid
- */
-
- /**
- *
- * @typedef TinyCommentsDeleteResp
- * @property {boolean} canDelete
- * @property {?string} reason
- */
-
- /**
- * Conversation "delete" function. Deletes an entire conversation.
- * Returns asynchronously whether the conversation was deleted.
- * Failure to delete due to permissions or business rules is indicated
- * by `{canDelete: false}`, while unexpected errors should be indicated using the
- * "fail" callback.
- * @param {TinyCommentsDeleteReq} req
- * @param {done} done
- * @param {fail} fail
- */
- function tinycomments_delete(req, done, fail) {
- let conversationUid = req.conversationUid;
- fetch('https://api.example/conversations/' + conversationUid, {
- method: 'DELETE',
- }).then((response) => {
- if (response.ok) {
- done({ canDelete: true });
- } else if (response.status === 403) {
- done({ canDelete: false });
- } else {
- fail(new Error('Something has gone wrong...'));
- }
- });
- }
-
- /**
- *
- * @typedef TinyCommentsDeleteAllReq
- * @type {object}
- */
-
- /**
- *
- * @typedef TinyCommentsDeleteAllResp
- * @property {boolean} canDelete
- * @property {?string} reason
- */
-
- /**
- * All conversations "delete_all" function. Deletes all conversations.
- * Returns asynchronously whether all conversations were deleted.
- * Failure to delete due to permissions or business rules is indicated
- * by `{canDelete: false}`, while unexpected errors should be indicated using the
- * "fail" callback.
- * @param {TinyCommentsDeleteAllReq} _req - no options
- * @param {done} done
- * @param {fail} fail
- */
- function tinycomments_delete_all(_req, done, fail) {
- fetch('https://api.example/conversations', {
- method: 'DELETE',
- }).then((response) => {
- if (response.ok) {
- done({ canDelete: true });
- } else if (response.status === 403) {
- done({ canDelete: false });
- } else {
- fail(new Error('Something has gone wrong...'));
- }
- });
- }
-
- /**
- *
- * @typedef TinyCommentsDeleteCommentReq
- * @property {string} conversationUid
- * @property {string} commentUid
- */
-
- /**
- *
- * @typedef TinyCommentsDeleteCommentResp
- * @property {boolean} canDelete
- * @property {?string} reason
- */
-
- /**
- *
- * @param {TinyCommentsDeleteCommentReq} req
- * @param {done} done
- * @param {fail} fail
- */
- function tinycomments_delete_comment(req, done, fail) {
- let conversationUid = req.conversationUid;
- let commentUid = req.commentUid;
-
- fetch(
- 'https://api.example/conversations/' +
- conversationUid +
- '/' +
- commentUid,
- {
- method: 'DELETE',
- }
- ).then((response) => {
- if (response.ok) {
- done({ canDelete: true });
- } else if (response.status === 403) {
- done({ canDelete: false });
- } else {
- fail(new Error('Something has gone wrong...'));
- }
- });
- }
-
- /**
- * @typedef TinyCommentsLookupReq
- * @property {string} conversationUid
- */
-
- /**
- *
- * @typedef TinyCommentsLookupRespComment
- * @property {string} author
- * @property {?string} authorName
- * @property {string} createdAt
- * @property {string} modifiedAt
- * @property {string} content
- * @property {string} uid
- */
+/********************************
+ * Demo-specific configuration *
+ ********************************/
+
+const userDb = {
+ 'michaelcook': {
+ id: 'michaelcook',
+ name: 'Michael Cook',
+ fullName: 'Michael Cook',
+ description: 'Product Owner',
+ },
+ 'kalebwilson': {
+ id: 'kalebwilson',
+ name: 'Kaleb Wilson',
+ fullName: 'Kaleb Wilson',
+ description: 'Marketing Director',
+ }
+};
- /**
- *
- * @typedef TinyCommentsLookupRespConversation
- * @property {string} uid
- * @property {TinyCommentsLookupRespComment[]} comments
- */
+const currentUid = 'kalebwilson';
+const adminUid = 'michaelcook';
- /**
- *
- * @typedef TinyCommentsLookupResp
- * @property {TinyCommentsLookupRespConversation} conversation
- */
+const now = new Date();
+const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000).toISOString();
+const anhourago = new Date(now.getTime() - 60 * 60 * 1000).toISOString();
- /**
- * Conversation "lookup" function. Retreives an existing conversation
- * via a conversation unique ID. Asynchronously returns the conversation
- * via the "done" callback.
- *
- * @param {TinyCommentsLookupReq} req
- * @param {done} done
- * @param {fail} fail
- */
- function tinycomments_lookup({ conversationUid }, done, fail) {
- let lookup = async function () {
- let convResp = await fetch(
- 'https://api.example/conversations/' + conversationUid
- );
- if (!convResp.ok) {
- throw new Error('Failed to get conversation');
- }
- let comments = await convResp.json();
- let usersResp = await fetch('https://api.example/users/');
- if (!usersResp.ok) {
- throw new Error('Failed to get users');
- }
- let { users } = await usersResp.json();
- let getUser = function (userId) {
- return users.find((u) => {
- return u.id === userId;
- });
- };
- return {
- conversation: {
- uid: conversationUid,
- comments: comments.map((comment) => {
- return {
- ...comment,
- content: comment.content,
- authorName: getUser(comment.author)?.displayName,
- };
- }),
- },
- };
- };
- lookup()
- .then((data) => {
- console.log('Lookup success ' + conversationUid, data);
- done(data);
- })
- .catch((err) => {
- console.error('Lookup failure ' + conversationUid, err);
- fail(err);
- });
- }
+const fillAuthorInfo = (id, fullName) => ({
+ author: id,
+ authorName: fullName,
+});
- tinymce.init({
- selector: 'textarea#comments-callback',
- height: 800,
- plugins: 'paste code tinycomments help lists',
- toolbar:
- 'undo redo | formatselect | ' +
- 'bold italic backcolor | alignleft aligncenter ' +
- 'alignright alignjustify | bullist numlist outdent indent | ' +
- 'removeformat | addcomment showcomments | help',
- menubar: 'file edit view insert format tc',
- menu: {
- tc: {
- title: 'Comments',
- items: 'addcomment showcomments deleteallconversations',
- },
- },
- tinycomments_create,
- tinycomments_reply,
- tinycomments_edit_comment,
- tinycomments_delete,
- tinycomments_delete_all,
- tinycomments_delete_comment,
- tinycomments_lookup,
- /* The following setup callback opens the comments sidebar when the editor loads */
- setup: function (editor) {
- editor.on('SkinLoaded', () => {
- editor.execCommand('ToggleSidebar', false, 'showcomments', {
- skip_focus: true,
- });
- });
- },
- });
+const getAuthorInfo = (uid) => {
+ const user = userDb[uid];
+ if (user) {
+ return fillAuthorInfo(user.id, user.fullName);
+ }
+ return {
+ author: uid,
+ authorName: uid,
+ };
+}
+
+const conversationDb = {
+ 'mce-conversation_19679600221621399703915': {
+ uid: 'mce-conversation_19679600221621399703915',
+ comments: [{
+ uid: 'mce-conversation_19679600221621399703915',
+ ...getAuthorInfo(currentUid),
+ content: `What do you think about this?`,
+ createdAt: yesterday,
+ modifiedAt: yesterday
+ }, {
+ uid: 'mce-conversation_19679600221621399703917',
+ ...getAuthorInfo(adminUid),
+ content: `I think this is a great idea!`,
+ createdAt: anhourago,
+ modifiedAt: anhourago,
+ }]
+ },
+ 'mce-conversation_420304606321716900864126': {
+ uid: 'mce-conversation_420304606321716900864126',
+ comments: [{
+ uid: 'mce-conversation_420304606321716900864126',
+ ...getAuthorInfo(adminUid),
+ content: `Please revise this sentence, exclamation points are unprofessional!`,
+ createdAt: yesterday,
+ modifiedAt: anhourago
+ }]
+ }
+};
+
+const fakeDelay = 200;
+const randomString = () => crypto.getRandomValues(new Uint32Array(1))[0].toString(36).substring(2, 14);
+
+const resolvedConversationDb = {};
+
+/********************************
+ * Tiny Comments functions *
+ * (must call "done" or "fail") *
+ ********************************/
+
+const tinycomments_create = (req, done, fail) => {
+ if (req.content === 'fail') {
+ fail(new Error('Something has gone wrong...'));
+ } else {
+ const uid = 'annotation-' + randomString();
+ conversationDb[uid] = {
+ uid,
+ comments: [{
+ uid,
+ ...getAuthorInfo(currentUid),
+ content: req.content,
+ createdAt: req.createdAt,
+ modifiedAt: req.createdAt
+ }]
+ };
+ setTimeout(() => done({ conversationUid: uid }), fakeDelay);
+ }
+};
+
+const tinycomments_reply = (req, done) => {
+ const replyUid = 'annotation-' + randomString();
+ conversationDb[req.conversationUid].comments.push({
+ uid: replyUid,
+ ...getAuthorInfo(currentUid),
+ content: req.content,
+ createdAt: req.createdAt,
+ modifiedAt: req.createdAt
+ });
+ setTimeout(() => done({ commentUid: replyUid }), fakeDelay);
+};
+
+const tinycomments_delete = (req, done) => {
+ if (currentUid === adminUid) { // Replace wth your own logic, e.g. check if user created the conversation
+ delete conversationDb[req.conversationUid];
+ setTimeout(() => done({ canDelete: true }), fakeDelay);
+ } else {
+ setTimeout(() => done({ canDelete: false, reason: 'Must be admin user' }), fakeDelay);
}
-);
+};
+
+const tinycomments_resolve = (req, done) => {
+ const conversation = conversationDb[req.conversationUid];
+ if (currentUid === conversation.comments[0].author) { // Replace wth your own logic, e.g. check if user has admin priveleges
+ delete conversationDb[req.conversationUid];
+ setTimeout(() => done({ canResolve: true }), fakeDelay);
+ } else {
+ setTimeout(() => done({ canResolve: false, reason: 'Must be conversation author' }), fakeDelay);
+ }
+};
+
+const tinycomments_delete_comment = (req, done) => {
+ const oldcomments = conversationDb[req.conversationUid].comments;
+ let reason = 'Comment not found';
+
+ const newcomments = oldcomments.filter((comment) => {
+ if (comment.uid === req.commentUid) { // Found the comment to delete
+ if (currentUid === comment.author) { // Replace with your own logic, e.g. check if user has admin privileges
+ return false; // Remove the comment
+ } else {
+ reason = 'Not authorised to delete this comment'; // Update reason
+ }
+ }
+ return true; // Keep the comment
+ });
+ if (newcomments.length === oldcomments.length) {
+ setTimeout(() => done({ canDelete: false, reason }), fakeDelay);
+ } else {
+ conversationDb[req.conversationUid].comments = newcomments;
+ setTimeout(() => done({ canDelete: true }), fakeDelay);
+ }
+};
+
+const tinycomments_edit_comment = (req, done) => {
+ const oldcomments = conversationDb[req.conversationUid].comments;
+ let reason = 'Comment not found';
+ let canEdit = false;
+
+ const newcomments = oldcomments.map((comment) => {
+ if (comment.uid === req.commentUid) { // Found the comment to delete
+ if (currentUid === comment.author) { // Replace with your own logic, e.g. check if user has admin privileges
+ canEdit = true; // User can edit the comment
+ return { ...comment, content: req.content, modifiedAt: new Date().toISOString() }; // Update the comment
+ } else {
+ reason = 'Not authorised to edit this comment'; // Update reason
+ }
+ }
+ return comment; // Keep the comment
+ });
+
+ if (canEdit) {
+ conversationDb[req.conversationUid].comments = newcomments;
+ setTimeout(() => done({ canEdit }), fakeDelay);
+ } else {
+ setTimeout(() => done({ canEdit, reason }), fakeDelay);
+ }
+};
+
+const tinycomments_delete_all = (req, done) => {
+ const conversation = conversationDb[req.conversationUid];
+ if (currentUid === conversation.comments[0].author) { // Replace wth your own logic, e.g. check if user has admin priveleges
+ delete conversationDb[req.conversationUid];
+ setTimeout(() => done({ canDelete: true }), fakeDelay);
+ } else {
+ setTimeout(() => done({ canDelete: false, reason: 'Must be conversation author' }), fakeDelay);
+ }
+};
+
+const tinycomments_lookup = (req, done) => {
+ setTimeout(() => {
+ done({
+ conversation: {
+ uid: conversationDb[req.conversationUid].uid,
+ comments: [...conversationDb[req.conversationUid].comments]
+ }
+ });
+ }, fakeDelay);
+};
+
+
+tinymce.init({
+ selector: 'textarea#comments-callback',
+ height: 800,
+ toolbar: 'addcomment showcomments code | bold italic underline',
+ menubar: 'file edit view insert format tools tc help',
+ menu: {
+ tc: {
+ title: 'TinyComments',
+ items: 'addcomment showcomments deleteallconversations'
+ }
+ },
+ plugins: ['tinycomments', 'help', 'code', 'quickbars', 'link', 'lists', 'image'],
+ quickbars_selection_toolbar: 'alignleft aligncenter alignright | addcomment showcomments',
+ quickbars_image_toolbar: 'alignleft aligncenter alignright | rotateleft rotateright | imageoptions',
+ sidebar_show: 'showcomments',
+ tinycomments_mode: 'callback',
+ tinycomments_create,
+ tinycomments_reply,
+ tinycomments_delete,
+ tinycomments_resolve,
+ tinycomments_delete_all,
+ tinycomments_lookup,
+ tinycomments_delete_comment,
+ tinycomments_edit_comment,
+});
\ No newline at end of file
diff --git a/modules/ROOT/pages/comments-callback-mode.adoc b/modules/ROOT/pages/comments-callback-mode.adoc
index a88142dc5c..1b1986a3ac 100644
--- a/modules/ROOT/pages/comments-callback-mode.adoc
+++ b/modules/ROOT/pages/comments-callback-mode.adoc
@@ -10,8 +10,6 @@
== Interactive example
-The following example uses a simulated server (provided by https://netflix.github.io/pollyjs/[Polly.js]) which has been hidden from the example javascript to keep the example code concise. The interactions between TinyMCE and Polly.js are visible in the browser console.
-
liveDemo::comments-callback[]
=== How the comments plugin works in callback mode