Callback mode 是 Comments 插件的默认模式。在 callback 模式下,需要使用回调函数将用户评论保存到服务器上。Comments 函数(创建、回复、编辑、删除评论、删除所有对话、解决、查找和获取对话)的配置会因服务器端存储配置而异。
Callback 模式为与自定义后端系统集成提供了最大的灵活性。所有评论操作都通过自定义回调函数处理,这些函数与您的服务器进行通信。
Comments 插件在 callback 模式下的工作原理
所有回调函数都接受三个参数:请求对象(或 tinycomments_fetch 的数组)、done 回调和 fail 回调。函数的返回类型并不重要,但所有函数必须精确调用这两个回调中的一个:fail 或 done。
fail回调接受一个特定于每个函数的参数。done回调接受一个特定于每个函数的参数,用于指示成功完成。
大多数函数(创建、回复和编辑)需要一个 id 来标识当前作者。这可以在回调中直接提供,或通过 tinycomments_fetch_author_info 选项动态提供。
当前作者:默认情况下,Comments 插件不知道当前用户的姓名。为了在创建新对话时显示正确的作者信息,集成人员可以使用
tinycomments_fetch_author_info选项通过回调动态提供作者详细信息。作者显示名称:
authorName字段在评论对象中是可选的。如果未提供,插件将默认使用author字段的值。
初始对话加载
在编辑器初始加载期间,Comments 使用 tinycomments_fetch 回调来检索文档中的现有对话。如果未配置,Comments 将回退到 tinycomments_lookup。
评论交互流程
当用户添加评论或回复时,Comments 插件使用 tinycomments_lookup 回调来检索所选对话。这确保了用户始终看到最新的对话数据。
交互式示例
HTML
<textarea id="comments-callback">
<h2>Welcome to Tiny Comments!</h2>
<p>Please try out this demo of our Tiny Comments premium plugin.</p>
<ol>
<li>Highlight the content you want to comment on.</li>
<li>Click the <em>Add Comment</em> icon in the toolbar.</li>
<li>Type your comment into the text field at the bottom of the Comment sidebar.</li>
<li>Click <strong>Comment</strong>.</li>
</ol>
<p>Your comment is <span class="mce-annotation tox-comment" data-mce-annotation-uid="mce-conversation_19679600221621399703915" data-mce-annotation="tinycomments">then</span> attached to the text, <span class="mce-annotation tox-comment" data-mce-annotation-uid="mce-conversation_420304606321716900864126" data-mce-annotation="tinycomments">exactly like this!</span></p>
<p>If you want to take Tiny Comments for a test drive in your own environment, Tiny Comments is one of the premium plugins you can try for free for 14 days by signing up for a Tiny account. Make sure to check out our documentation as well.</p>
<h2>A simple table to play with</h2>
<table style="border-collapse: collapse; width: 100%;" border="1">
<thead>
<tr>
<th>Product</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://www.tiny.cloud/">Tiny Cloud</a></td>
<td>The easiest and most reliable way to integrate powerful rich text editing into your application.</td>
</tr>
<tr>
<td><a href="https://www.tiny.cloud/drive/">Tiny Drive</a></td>
<td>Image and file management for TinyMCE in the cloud.</td>
</tr>
</tbody>
</table>
<p>Thanks for supporting TinyMCE! We hope it helps your users create great content.</p>
</textarea>JS
const API_URL = 'https://demouserdirectory.tiny.cloud/v1/users';
const user_id = 'james-wilson';
const collaborator_id = 'mia-andersson';
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();
const fakeDelay = 200;
const randomString = () => {
return crypto.getRandomValues(new Uint32Array(1))[0].toString(36).substring(2, 14);
};
const conversationDb = {
'mce-conversation_19679600221621399703915': {
uid: 'mce-conversation_19679600221621399703915',
comments: [{
uid: 'mce-conversation_19679600221621399703915',
author: user_id,
authorName: 'James Wilson',
authorAvatar: 'https://sneak-preview.tiny.cloud/demouserdirectory/images/employee_james-wilson_128_52f19412.jpg',
content: `What do you think about this?`,
createdAt: yesterday,
modifiedAt: yesterday
}, {
uid: 'mce-conversation_19679600221621399703917',
author: collaborator_id,
authorName: 'Mia Andersson',
authorAvatar: 'https://sneak-preview.tiny.cloud/demouserdirectory/images/employee_mia-andersson_128_e6f9424b.jpg',
content: `I think this is a great idea!`,
createdAt: anhourago,
modifiedAt: anhourago,
}]
},
'mce-conversation_420304606321716900864126': {
uid: 'mce-conversation_420304606321716900864126',
comments: [{
uid: 'mce-conversation_420304606321716900864126',
author: collaborator_id,
authorName: 'Mia Andersson',
authorAvatar: 'https://sneak-preview.tiny.cloud/demouserdirectory/images/employee_mia-andersson_128_e6f9424b.jpg',
content: `Please revise this sentence, exclamation points are unprofessional!`,
createdAt: yesterday,
modifiedAt: anhourago
}]
}
};
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,
author: user_id,
authorName: 'James Wilson',
authorAvatar: 'https://sneak-preview.tiny.cloud/demouserdirectory/images/employee_james-wilson_128_52f19412.jpg',
content: req.content,
createdAt: req.createdAt,
modifiedAt: req.createdAt
}]
};
setTimeout(() => done({ conversationUid: uid }), fakeDelay);
}
};
const tinycomments_reply = (req, done) => {
const commentUid = 'annotation-' + randomString();
conversationDb[req.conversationUid].comments.push({
uid: commentUid,
author: user_id,
authorName: 'James Wilson',
authorAvatar: 'https://sneak-preview.tiny.cloud/demouserdirectory/images/employee_james-wilson_128_52f19412.jpg',
content: req.content,
createdAt: req.createdAt,
modifiedAt: req.createdAt
});
setTimeout(() => done({ commentUid: commentUid }), fakeDelay);
};
const tinycomments_delete = (req, done) => {
if (user_id === collaborator_id) { // 替换为您自己的逻辑,例如检查用户是否创建了对话
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 (user_id === conversation.comments[0].author) { // 替换为您自己的逻辑,例如检查用户是否有管理员权限
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) { // 找到要删除的评论
if (user_id === comment.author) { // 替换为您自己的逻辑,例如检查用户是否有管理员权限
return false; // 删除评论
} else {
reason = 'Not authorised to delete this comment'; // 更新原因
}
}
return true; // 保留评论
});
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) { // 找到要编辑的评论
if (user_id === comment.author) { // 替换为您自己的逻辑,例如检查用户是否有管理员权限
canEdit = true; // 用户可以编辑评论
return { ...comment, content: req.content, modifiedAt: new Date().toISOString() }; // 更新评论
} else {
reason = 'Not authorised to edit this comment'; // 更新原因
}
}
return 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 (user_id === conversation.comments[0].author) { // 替换为您自己的逻辑,例如检查用户是否有管理员权限
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);
};
const tinycomments_fetch = (conversationUids, done) => {
const fetchedConversations = {};
conversationUids.forEach((uid) => {
const conversation = conversationDb[uid];
if (conversation) {
fetchedConversations[uid] = {...conversation};
}
});
setTimeout(() => done({ conversations: fetchedConversations }), fakeDelay);
};
tinymce.init({
selector: 'textarea#comments-callback',
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',
tinycomments_mode: 'callback',
tinycomments_mentions_enabled: true,
sidebar_show: 'showcomments',
tinycomments_create,
tinycomments_reply,
tinycomments_delete,
tinycomments_resolve,
tinycomments_delete_all,
tinycomments_lookup,
tinycomments_delete_comment,
tinycomments_edit_comment,
tinycomments_fetch,
user_id,
fetch_users: (userIds) => Promise.all(userIds
.map((userId) =>
fetch(`${API_URL}/${userId}`)
.then((response) => response.json())
.catch(() => ({ id: userId }))))
});选项
必需的回调函数
当使用callback模式时,Comments插件需要以下操作的回调函数:
tinycomments_create- 创建新的评论对话tinycomments_reply- 向现有对话添加回复tinycomments_edit_comment- 编辑现有评论tinycomments_delete_comment- 删除单个评论tinycomments_delete- 删除整个对话线程tinycomments_delete_all- 删除所有对话tinycomments_lookup- 检索特定对话
tinycomments_create
Comments插件使用tinycomments_create函数创建评论。
tinycomments_create函数将评论保存为新的对话,并通过done回调返回唯一的对话ID。如果发生不可恢复的错误,应通过fail回调进行指示。
tinycomments_create函数的第一个参数是一个请求(req)对象,具有以下字段:
content: 要创建的评论内容。createdAt: 评论创建的日期。
done回调应接受以下对象:
{
conversationUid: string, // 新对话的uid
// 可选的错误回调,当新对话无法创建时将运行
onError: (err) => { ... },
// 可选的成功回调,当新对话成功创建时将运行
onSuccess: (uid) => { ... }
}例如:const create_comment = (ref, done, fail) => {
const { content, createdAt } = ref;
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((ref2) => {
let conversationUid = ref2.conversationUid;
done({ conversationUid: conversationUid });
})
.catch((e) => {
fail(e);
});
}
tinymce.init({
selector: 'textarea', // 根据您的HTML更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment, // 将回调添加到TinyMCE
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 可选回调
});tinycomments_reply
Comments插件使用tinycomments_reply函数回复评论。
tinycomments_reply函数将评论保存为对现有对话的回复,并在成功后通过done回调返回。不可恢复的错误通过调用fail回调而不是done来告知TinyMCE。
tinycomments_reply函数的第一个参数是一个请求(req)对象,具有以下字段:
conversationUid: 回复目标对话的uid。content: 要创建的评论内容。createdAt: 评论创建的日期(ISO 8601日期字符串)格式。
done回调应接受以下对象:
{
commentUid: string, // 新的评论 uid
author: string, // 当前作者的 id
authorName: string // 当前作者的姓名
}例如:const reply_comment = (ref, done, fail) => {
const { conversationUid, content, createdAt } = ref;
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((ref2) => {
const commentUid = ref2.commentUid;
done({
commentUid: commentUid,
author: currentUser.id,
authorName: currentUser.fullName
});
})
.catch((e) => {
fail(e);
});
}
tinymce.init({
selector: 'textarea', // 根据 HTML 更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment, // 将回调添加到 TinyMCE
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 可选回调
});tinycomments_edit_comment
Comments 插件使用 tinycomments_edit_comment 函数来编辑评论。
tinycomments_edit_comment 函数允许更新或更改现有评论,并在成功时通过 done 回调返回。不可恢复的错误则通过调用 fail 回调通知 TinyMCE。
tinycomments_edit_comment 函数的第一个参数是一个请求 (req) 对象,具有以下字段:
conversationUid:回复的目标对话的 uid。commentUid:要编辑的评论的 uid(如果是编辑对话中的第一条评论,则可以与conversationUid相同)。content:要创建的评论的内容。modifiedAt:评论被修改的日期。
done 回调应接受以下对象:
{
canEdit: boolean, // 编辑是否成功
reason: string? // 解释为何不允许编辑的可选字符串(如果 canEdit 为 false)
}例如:const edit_comment = (ref, done, fail) => {
const { conversationUid, commentUid, content, modifiedAt } = ref;
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((ref2) => {
let canEdit = ref2.canEdit;
done({ canEdit: canEdit });
})
.catch((e) => {
fail(e);
});
}
tinymce.init({
selector: 'textarea', // 根据 HTML 更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment, // 将回调添加到 TinyMCE
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 可选回调
});tinycomments_delete_comment
tinycomments_delete_comment 函数应使用 done 回调异步返回一个标志,指示是否已删除评论或评论线程。不可恢复的错误则通过调用 fail 回调通知 TinyMCE。
tinycomments_delete_comment 函数的第一个参数是一个请求 (req) 对象,具有以下字段:
conversationUid:回复的目标对话的 uid。commentUid:要删除的评论的 uid(不能与conversationUid相同)。
done 回调应接受以下对象:
{
canDelete: boolean, // 单个评论是否可以删除
reason: string? // 解释为何不允许删除的可选原因(如果 canDelete 为 false)
}由于权限或业务规则导致删除失败时,会通过“false”指示,而意外错误应使用“fail”回调指示。例如:
const delete_comment = (ref, done, fail) => {
const { conversationUid, commentUid } = ref;
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...'));
}
});
}
tinymce.init({
selector: 'textarea', // 根据你的HTML更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread, // 将回调添加到TinyMCE
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 可选回调
});tinycomments_delete
tinycomments_delete函数应异步返回一个标志,指示是否使用done回调删除了评论线程。无法恢复的错误应通过调用fail回调传达给TinyMCE。
tinycomments_delete函数的第一个参数是一个(req)对象,该对象包含以下键值对:
conversationUid:回复的目标对话的uid。
done回调应接受以下对象:
{
canDelete: boolean // 对话是否可以删除
reason: string? // 可选字符串,解释为什么不允许删除(如果canDelete为false)
}由于权限或业务规则导致删除失败时,会通过“false”指示,而意外错误应使用“fail”回调指示。
例如:
const delete_comment_thread = (ref, done, fail) => {
const conversationUid = ref.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...'));
}
});
}
tinymce.init({
selector: '#editor',
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread, // 将回调添加到TinyMCE
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 可选回调
});tinycomments_delete_all
tinycomments_delete_all函数应异步返回一个标志,指示是否使用done回调删除了所有评论线程。无法恢复的错误应通过调用fail回调传达给TinyMCE。
tinycomments_delete_all函数的第一个参数是一个没有字段的请求(req)对象。
done回调应接受以下对象:
{
canDelete: boolean, // 所有对话是否可以删除
reason: string? // 可选字符串,解释为什么不允许删除所有(如果canDelete为false)
}由于权限或业务规则导致删除失败时,应通过例如:canDelete: false指示,而意外错误应使用fail回调指示。
const delete_all_comment_threads = (_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...'));
}
});
}
tinymce.init({
selector: 'textarea', // 根据你的HTML更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads, // 将回调添加到TinyMCE
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 可选回调
});tinycomments_lookup
Comments插件使用tinycomments_lookup函数通过对话的唯一ID检索一个现有的对话。
必须为tinycomments_lookup函数考虑显示名称配置:
Comments插件使用一个简单的字符串作为显示名称。对于lookup函数,Comments期望每个评论都包含作者的显示名称,而不是用户ID,因为Comments不知道用户的身份。lookup函数应考虑这一点,并将用户标识符解析为适当的显示名称。
应通过 done 回调返回的传统对话对象结构如下:
tinycomments_lookup 函数的第一个参数是(req)对象,其中包含以下键值对:
conversationUid回复目标对话的 uid。
done 回调应接受以下对象:
{
conversation: {
uid: string, // 对话的 uid,
comments: [
{
author: string, // 第一条评论的作者
authorName: string, // 可选 - 用于代替作者的显示名称。如果未指定,默认使用 `author`
authorAvatar: string, // 可选 - 作者头像的 URL。如果未提供,将自动生成头像
createdAt: date, // 第一条评论的创建时间
content: string, // 第一条评论的内容
modifiedAt: date, // 第一条评论的创建时间/最后更新时间
uid: string // 对话中第一条评论的 uid
},
{
author: string, // 第二条评论的作者
authorName: string, // 可选 - 用于代替作者的显示名称。如果未指定,默认使用 `author`
authorAvatar: string, // 可选 - 作者头像的 URL。如果未提供,将自动生成头像
createdAt: date, // 第二条评论的创建时间
content: string, // 第二条评论的内容
modifiedAt: date, // 第二条评论的创建时间/最后更新时间
uid: string // 对话中第二条评论的 uid
}
]
}
}例如:日期应使用 ISO 8601 格式。可以在 JavaScript 中使用
new Date().toISOString()生成此格式。作者头像功能仅在 TinyMCE 6.1 或更高版本中可用,如果提供:
将缩放为直径 36px 的圆形;并且
可以是任何能够包装在
<img>元素中的文件类型。
const lookup_comment = ({ conversationUid }, done, fail) => {
const lookup = async () => {
const convResp = await fetch(
`https://api.example/conversations${conversationUid}`
);
if (!convResp.ok) {
throw new Error('Failed to get conversation');
}
const comments = await convResp.json();
const usersResp = await fetch('https://api.example/users/');
if (!usersResp.ok) {
throw new Error('Failed to get users');
}
const { users } = await usersResp.json();
const getUser = (userId) => users.find((u) => u.id === userId);
return {
conversation: {
uid: conversationUid,
comments: comments.map((comment) => {
const user = getUser(comment.author);
return {
...comment,
content: comment.content,
authorName: user?.displayName,
authorAvatar: user?.avatarUrl
};
})
}
};
};
lookup()
.then((data) => {
console.log(`Lookup success ${conversationUid}`, data);
done(data);
})
.catch((err) => {
console.error(`Lookup failure ${conversationUid}`, err);
fail(err);
});
};
tinymce.init({
selector: '#editor',
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment, // Add the callback to TinyMCE
tinycomments_fetch: fetch_comments // Optional callback
});可选回调函数
tinycomments_resolve
此选项在对话的第一条评论的下拉菜单中添加一个“Resolve Conversation”项。
tinycomments_resolve 函数应异步返回一个标志,指示是否使用 done 回调解决了评论线程。无法恢复的错误通过调用 fail 回调传达给 TinyMCE。
tinycomments_resolve 函数的第一个参数是(req)对象,其中包含以下键值对:
conversationUid:目标对话的 uid
done 回调应接受以下对象:
{
canResolve: boolean // 对话是否可以解决
reason?: string // 可选字符串,解释为什么不允许解决(如果 canResolve 为 false)
}由于权限或业务规则无法解析的情况,应通过例如:canResolve: false指示,而意外错误应使用fail回调指示。
const resolve_comment_thread = (ref, done, fail) => {
const conversationUid = ref.conversationUid;
fetch(`https://api.example/conversations/${conversationUid}`, {
method: 'PUT',
}).then((response) => {
if (response.ok) {
done({ canResolve: true });
} else if (response.status === 403) {
done({ canResolve: false });
} else {
fail(new Error('Something has gone wrong...'));
}
});
}
tinymce.init({
selector: 'textarea', // 根据 HTML 更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_resolve: resolve_comment_thread, // 将回调添加到 TinyMCE
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments
});tinycomments_fetch
Comments 插件使用 tinycomments_fetch 回调函数根据其唯一标识符检索多个会话。
应通过 done 回调返回的常规会话对象结构如下:
tinycomments_fetch 函数的第一个参数是一个 (req) 对象,其中包含以下键值对:
conversationUids:一个字符串数组,表示要获取的会话的唯一标识符。
done 回调应接受以下对象:
{
conversations: {
[conversationUid: string]: {
uid: string,
comments: [
{
uid: string,
author: string,
authorName?: string,
authorAvatar?: string,
content: string,
createdAt: string, // ISO 8601 日期字符串
modifiedAt: string // ISO 8601 日期字符串
},
// ... 更多评论
]
},
// ... 更多会话
}
}例如:const tinycomments_fetch = (conversationUids, done, fail) => {
const requests = conversationUids.map((uid) => fetch(`https://api.example/conversations/${uid}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}})
.then((response) => response.json())
.then((data) => ({
[uid]: {
uid: uid,
comments: data
}
}))
);
Promise.all(requests)
.then((data) => {
console.log('data', data);
const conversations = data.reduce((conv, d) => ({
...conv,
...d
})
, {});
console.log(`Fetch success ${conversationUids}`, conversations);
done({ conversations });
})
.catch((err) => {
console.error(`Fetch failure ${conversationUids}`, err);
fail('Fetching conversations failed');
});
tinymce.init({
selector: '#editor',
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments // 将回调添加到 TinyMCE
});user_id
此选项设置编辑器中当前用户的唯一标识符。它用于 UserLookup API。
类型:String
默认值:'Anonymous'
user_id 选项tinymce.init({
selector: 'textarea', // 根据 HTML 更改此值
user_id: 'james-wilson' // 用唯一字符串替换,以标识用户
});fetch_users
一个必需的回调函数,用于获取用户数据。此函数以用户 ID 数组调用,应返回一个 Promise,该 Promise 解析为用户对象数组。UserLookup API 使用此回调。如果返回的数组不包含所有请求的用户 ID,则缺失用户的 Promise 将被拒绝,并显示 "User {id} not found" 错误。
类型:Function
参数:- ids (Array<string>):要获取的用户 ID 数组。
返回值:- Promise<Array<Object>>:解析为用户对象数组的 Promise。
fetch_users 选项const API_URL = 'https://demouserdirectory.tiny.cloud/v1/users';
tinymce.init({
selector: 'textarea',
user_id: 'james-wilson',
fetch_users: (userIds) => Promise.all(userIds
.map((userId) =>
fetch(`${API_URL}/${userId}`)
.then((response) => response.json())
.catch(() => ({ id: userId })))),
});示例:返回带有验证的用户数组const API_URL = 'https://demouserdirectory.tiny.cloud/v1/users';
tinymce.init({
selector: 'textarea',
user_id: 'james-wilson',
fetch_users: async (userIds) => {
const users = await Promise.all(userIds
.map((userId) =>
fetch(`${API_URL}/${userId}`)
.then((response) => response.json())
.catch(() => ({ id: userId }))));
return userIds.map(
(userId) =>
users.find((user) => user.id === userId)
|| ({ id: userId }) // 即使服务器未返回该用户,仍返回一个有效的用户
);
},
});tinycomments_fetch_author_info
此选项在 TinyMCE 8 中已被弃用,可能会在 TinyMCE 未来的主版本中被移除。请使用user_id和fetch_users的组合来代替。 |用户查找 API
Comments 插件使用 tinycomments_fetch_author_info 回调函数来提供在编辑器中显示的评论的作者详细信息。
每当渲染一条评论时,此函数都会被调用,它允许编辑器根据当前用户显示作者的姓名和头像。
tinycomments_fetch_author_info 函数会接收一个 done 回调参数。
该函数必须调用 done 并传入一个包含以下属性的对象:
author(string):用户的唯一标识符。authorName(string):要显示的用户全名。(可选)authorAvatar(string):指向用户头像图片的 URL。(可选)
tinycomments_fetch_author_info 函数示例const tinycomments_fetch_author_info = (done) => {
setTimeout(() => done({
author: currentUser.id, // 映射到评论对象中的 `author` 字段
authorName: currentUser.fullName, // 映射到评论对象中的 `authorName` 字段。(可选)
authorAvatar: currentUser.image, // 映射到评论对象中的 `authorAvatar` 字段。(可选)
}), fakeDelay);
};tinycomments_fetch_author_info 函数与自定义作者的示例const currentUser = {
id: 'johnsmith',
name: 'John Smith',
fullName: 'John Smith',
description: 'Company Founder',
image: '{{imagesdir}}/users/johnsmith.png'
};
const tinycomments_fetch_author_info = (done) => {
fetch('https://example.api.com/currentUser')
.then(res => res.json())
.then(currentUser => {
done({
author: currentUser.id, // 映射到评论对象中的 `author` 字段
authorName: currentUser.fullName, // 映射到评论对象中的 `authorName` 字段。(可选)
authorAvatar: currentUser.image, // 映射到评论对象中的 `authorAvatar` 字段。(可选)
});
});
};
tinymce.init({
selector: '#editor',
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create: create_comment,
tinycomments_reply: reply_comment,
tinycomments_edit_comment: edit_comment,
tinycomments_delete: delete_comment_thread,
tinycomments_delete_all: delete_all_comment_threads,
tinycomments_delete_comment: delete_comment,
tinycomments_lookup: lookup_comment,
tinycomments_fetch: fetch_comments, // 可选回调
tinycomments_fetch_author_info: fetch_comments
});tinycomments_always_show_highlights
可选:Comments 插件提供 tinycomments_always_show_highlights 选项,用于在 Comments 侧边栏关闭时保持对评论内容的高亮显示。默认情况下,TinyMCE 仅在侧边栏打开时高亮显示评论内容,一旦侧边栏关闭,就会移除编辑器内容上的高亮显示。
当此选项设置为 true 时,无论侧边栏是打开还是关闭,TinyMCE 都会高亮显示评论内容,并且选中高亮显示的内容会重新打开已关闭的 Comments 侧边栏。
类型: Boolean
默认值: false
可能的值: true,false
示例:使用 tinycomments_always_show_highlights
const currentAuthor = 'embedded_journalist';
tinymce.init({
selector: 'textarea', // 根据您的 html 更改此值
plugins: 'tinycomments',
toolbar: 'addcomment showcomments',
tinycomments_mode: 'embedded',
user_id: currentAuthor,
tinycomments_always_show_highlights: true
});在编辑器加载时显示侧边栏
可以使用 sidebar_show 选项在编辑器加载时显示 Comments 侧边栏。
tinymce.init({
selector: 'textarea', // 根据您的 html 更改此值
plugins: 'tinycomments',
tinycomments_mode: 'callback',
tinycomments_create,
tinycomments_reply,
tinycomments_edit_comment,
tinycomments_delete,
tinycomments_delete_all,
tinycomments_delete_comment,
tinycomments_lookup,
tinycomments_fetch,
sidebar_show: 'showcomments'
});配置评论文本和块的 CSS 属性
高亮样式现在是整体内容皮肤的一部分,并且通过自定义皮肤来更改。
TinyMCE 开源项目 oxide(默认皮肤)定义了用于更改批注颜色的变量。
请参考 文档以了解如何使用此代码库构建皮肤。
有关配置 TinyMCE 格式的更多信息,请参阅 formats 部分。