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