- Notifications
You must be signed in to change notification settings - Fork73
A plugin for Strapi Headless CMS that provides end to end comments feature with their moderation panel, bad words filtering, abuse reporting and more.
License
VirtusLab-Open-Source/strapi-plugin-comments
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
A plugin forStrapi Headless CMS that provides end to end comments feature with their moderation panel, bad words filtering, abuse reporting and much more.
- 💎 Versions
- ✨ Features
- ⏳ Installation
- 🖐 Requirements
- 🔧 Configuration
- 🕸️ Public API - REST
- 🕸️ Public API - GraphQL
- 🌿 Model lifecycle hooks
- ⚗️ Custom fields
- 🧩 Examples
- 💬 FAQ
- 🤝 Contributing
- 👨💻 Community support
- Comments Public REST + GraphQL API: Elegant, entirely customizable and a fully extensible admin panel.
- Strapi & generic users: Support for built-in & also generic non-Strapi users that might be the comments authors.
- Strapi Custom Fields support: Improve an experience of your Content Types by usingdedicated set of custom fields for each of them and automate client side processing of Comments.
- Any Content Type relation: Comments can be linked to any of your Content Types by default. Simply, you're controlling it.
- Moderation Panel: Search & Filter through the bucket with your auditory comments. Manage them by blocking single ones or full threads. All in combined list & hierarchical tree view of threads.
- Automated Bad Words filtering: By default end users are not allowed to post abusing comments where bad words have been used.
- Abuse Reporting & Reviewing: Don't allow inferior language, react to reports from your community, send email notifications about issued reports
As a ✅verified plugin by Strapi team we're available on theStrapi Marketplace as well asIn-App Marketplace where you can follow the installation instructions.
(Useyarn to install this plugin within your Strapi project (recommended).Install yarn with these docs.)
yarn add strapi-plugin-comments@latest
After successful installation you've to re-build your Strapi instance. To archive that simply use:
yarn buildyarn develop
TheComments plugin should appear in thePlugins section of Strapi sidebar after you run app again.
As a next step you must configure your the plugin by the way you want to. SeeConfiguration section.
All done. Enjoy 🎉
Complete installation requirements are exact same as for Strapi itself and can be found in the documentation underInstallation Requirements.
Minimum environment requirements
- Node.js
>=18.0.0 <=22.x.x
- NPM
>=6.x.x
In our minimum support we're followingofficial Node.js releases timelines.
Supported Strapi versions:
- Strapi v5.10.3 (recently tested)
- Strapi v5.x
This plugin is designed forStrapi v5. To get support for other Strapi versions, please follow theversions section.
Plugin dependencies
@strapi/plugin-graphql
- required to run GraphQL handled by this plugin
We recommend always using the latest version of Strapi to start your new projects.
To start your journey withComments plugin you must first setup it using the dedicated Settings page or for any version, put your configuration inconfig/plugins.{js|ts}
. Anyway we're recommending the click-through option where your configuration is going to be properly validated.
On the dedicated page, you will be able to set up all crucial properties which drive the plugin and customize each individual collection for whichComments plugin should be enabled.
NoteDefault configuration for your plugin is fetched from
config/plugins.{js|ts}
or directly from the plugin itself. If you would like to customize the default state to which you might revert, please follow the next section.
To setup amend default plugin configuration we recommend to put following snippet as part ofconfig/plugins.{js|ts}
orconfig/<env>/plugins.{js|ts}
file. If the file does not exist yet, you have to create it manually. If you've got already configurations for other plugins stores by this way, use just thecomments
part within exisingplugins
item.
module.exports=({ env})=>({//...comments:{enabled:true,config:{badWords:false,moderatorRoles:["Authenticated"],approvalFlow:["api::page.page"],entryLabel:{"*":["Title","title","Name","name","Subject","subject"],"api::page.page":["MyField"],},blockedAuthorProps:["name","email"],reportReasons:{MY_CUSTOM_REASON:"MY_CUSTOM_REASON",},gql:{// ...},},},//...});
enabledCollections
- list of Collection and Single Types for which plugin should be enabled in format like'api::<collection name>.<content type name>'
. By default it's empty and none comments are not enabled for any of type in Strapi.no-profanity
- Enabled support forprofanity filtering. Can be turned off or altered using theoptions reference. Default value:true
.moderatorRoles
- Optional list of names of roles. Users with those roles will be notified by email when a new abuse report is created. This feature requires a built-inStrapi email plugin configured.approvalFlow
- list of Content Types which are supporting approval flow. Values must be in format like'api::<collection name>.<content type name>'
. For not included, posted comments are going to be immediately visible.entryLabel
- ordered list of property names per Content Type to generate related entity label. Keys must be in format like'api::<collection name>.<content type name>'
. Default formatting set as*
.reportReasons
- set of enums you would like to use for issuing abuse reports. Provided by default'BAD_LANGUAGE'
,'DISCRIMINATION'
and'OTHER'
.gql
- specific configuration for GraphQL. SeeAdditional GQL ConfigurationblockedAuthorProps
- list of author's entity properties removed from a response for client side
All you need to do is to install and enable@strapi/plugin-graphql
for you instance based on theofficial Strapi v4 docs and decide if you would like to call it by anyone (open for world) or only by authenticated users (Strapi users).
Important!If you're using
config/plugins.{js|ts}
to configure your plugins , please putcomments
property beforegraphql
. Otherwise types are not going to be properly added to GraphQL Schema. That's because of dynamic types which base on plugin configuration which are added onboostrap
stage, notregister
. This is not valid if you're usinggraphql
plugin without any custom configuration, so most of cases in real.
{// ..."gql": {"auth":true// Default: false }// ...}
auth
- does GraphQL Queries should be authenticated or not? Default:false
Seeavailable GQL specification section.
If
auth
is set totrue
you must provide relevant authentication headers to all requests like for example:{"Authorization":"Bearer <your token here>"}
Plugin provides granular permissions based on Strapi RBAC functionality.
For any role different thanSuper Admin, to access theComments panel you must set following permissions:
- Plugins ->Content-type-builder ->Read - gives you ability to fetch Content Type schema
- Plugins ->Comments ->Comments: Read - gives you the basic read access toComments Panel
Feature / Capability focused permissions:
- Plugins ->Comments ->Comments: Moderate - allows you to block, unblock, approve & reject comments
- Plugins ->Comments ->Reports: Read - allows you to see the list of issued abuse reports against comments
- Plugins ->Comments ->Reports: Moderate - allows you to review (resolve) issued abuse reports against comments
{"documentId":"njx99iv4p4txuqp307ye8625","content":"My comment content","blocked":null,"blockedThread":true,"blockReason":null,"authorUser":null,"removed":null,"approvalStatus":"APPROVED",// Only in case of enabled approval flow. Default: null"author": {"id":"207ccfdc-94ba-45eb-979c-790f6f49c392",// For Strapi users id reflects to the format used by your Strapi"name":"Joe Doe",// For Strapi users it is equal to 'username' field"email":"jdoe@sample.com","avatar":null },"createdAt":"2020-07-14T20:13:01.649Z","updatedAt":"2020-07-14T20:13:01.670Z","related": {},// Related content type entity"reports": []// Reports issued against this comment}
Note: If you want to use
plguin::user-permissions.user
as a comment's author with an avatar, you need to add anavatar
field to its model. This field have to be of typeMedia
.
Strapi Users vs. Generic authors
Keep in mind that if you're using auth / authz your requests to setup proper user contexts it has got higher priority in order to take author data comparing to
author
property provided as part of your payload.
GraphQL equivalent:Public GraphQL API -> Get Comments
GET <host>/api/comments/api::<collection name>.<content type name>:<entity document id>
Return a hierarchical tree structure of comments for specified instance of Content Type like for examplePage
withdocumentId: njx99iv4p4txuqp307ye8625
.
Example URL:https://localhost:1337/api/comments/api::page.page:njx99iv4p4txuqp307ye8625
Example response body
[ {// -- Comment Model fields ---,"children": [ {// -- Comment Model fields ---,"children": [// ... ] }// ... ] }// ...]
GraphQL equivalent:Public GraphQL API -> Get Comments (flat structure)
GET <host>/api/comments/api::<collection name>.<content type name>:<entity document id>/flat
Return a flat structure of comments for specified instance of Content Type like for examplePage
withdocumentId: njx99iv4p4txuqp307ye8625
Example URL:https://localhost:1337/api/comments/api::page.page:njx99iv4p4txuqp307ye8625/flat
Example response body
{"data": [ {// -- Comment Model fields --- }, {// -- Comment Model fields --- }// ... ],"meta": {"pagination": {// payload based on Strapi REST Pagination specification } }}
Possible response codes
200
- Successful. Response with list of comments (can be empty)400
- Bad Request. Requested list for not valid / not existing Content Type
GraphQL equivalent:Public GraphQL API -> Get Comments (by Author)
GET <host>/api/comments/author/<id>/<?type>
Return a flat structure of comments by specified Author for exampleAuthor
withID: 1
Example URL:https://localhost:1337/api/comments/author/1
- get comments byID: 1
of Strapi UserExample URL:https://localhost:1337/api/comments/author/1/generic
- get comments byID: 1
of Generic User
To skip a field from the response you can use a query param calledomit
. It is a list ofComment
entity fields you want to ignore. For example?omit[]=author&omit[]=related
.
Remember!id
field is required so it will not be skipped.
Example response body
{"data": [ {// -- Comment Model fields --- }, {// -- Comment Model fields --- }// ... ],"meta": {"pagination": {// payload based on Strapi REST Pagination specification } }}
Possible response codes
200
- Successful. Response with list of comments (can be empty)400
- Bad Request. Requested list for not valid / not existing Content Type
GraphQL equivalent:Public GraphQL API -> Post a Comments
POST <host>/api/comments/api::<collection name>.<content type name>:<entity document id>
Posts a Comment related to specified instance of Content Type like for examplePage
withdocumentId: njx99iv4p4txuqp307ye8625
Example URL:https://localhost:1337/api/comments/api::page.page:njx99iv4p4txuqp307ye8625
Example request body
Generic (non Strapi User)
{"author": {"id":"<any ID like value>","name":"Joe Doe","email":"jdoe@sample.com","avatar":"<any image url>" },"content":"My sample response","threadOf":2// id of comment we would like to start / continue the thread (Optional)}
Strapi user
Author is taken directly from the request context
{"content":"My sample response","threadOf":2// id of comment we would like to start / continue the thread (Optional)}
Multi-language entities
When posting comments to entities with multiple locales, you must provide the
locale
field in the payload to match the entity's locale:
{"author": {"id":518,"name":"Author","email":"author@example.com","avatar":"<Link to avatar file>" },"content":"Test","locale":"fr"// Locale must be the same as the provided entity}
Example response body
{// -- Comment Model fields ---}
Possible response codes
200
- Successful. Response with created Comment Model400
- Bad Request. Missing field values or bad words check fails. Error message will provide relevant reason.
GraphQL equivalent:Public GraphQL API -> Update Comments
PUT <host>/api/comments/api::<collection name>.<content type name>:<entity document id>/comment/<commentId>
Updates a specified Comment content based on itcommentId
and related to specified instance of Content Type like for examplePage
withdocumentId: njx99iv4p4txuqp307ye8625
Example URL:https://localhost:1337/api/comments/api::page.page:njx99iv4p4txuqp307ye8625/comment/2
Example request body
Generic (non Strapi User)
{"author": {"id":"<any ID like value>" },"content":"My sample response"}
Strapi user
Author is taken directly from the request context
{"content":"My sample response"}
Example response body
{// -- Comment Model fields ---}
Possible response codes
200
- Successful. Response with updated Comment Model400
- Bad Request. Missing field values or bad words check fails. Error message will provide relevant reason.409
- Conflict. Occurs when trying to update a non existing or not own comment. Possible cause might be thatauthorId
orauthorUser
mismatch with existing comment.
GraphQL equivalent:Public GraphQL API -> Delete Comment
DELETE <host>/api/comments/api::<collection name>.<content type name>:<entity document id>/comment/<commentId>?authorId=<authorId>
Deletes a specified Comment based on itcommentId
and related to specified instance of Content Type like for examplePage
withdocumentId: njx99iv4p4txuqp307ye8625
.
Example URL:https://localhost:1337/api/comments/api::page.page:njx99iv4p4txuqp307ye8625/comment/1?authorId=1
Example response body
{// -- Empty Response ---}
Possible response codes
200
- Successful with blank Response.409
- Conflict. Occurs when trying to delete a non existing comment.
GraphQL equivalent:Public GraphQL API -> Issue Abuse Report against specified Comment
POST <host>/api/comments/api::<collection name>.<content type name>:<entity document id>/comment/<commentId>/report-abuse
Reports abuse in specified Comment content based on itcommentId
and related to specified instance of Content Type like for examplePage
withdocumentId: njx99iv4p4txuqp307ye8625
and requests moderator attention.
Example URL:https://localhost:1337/api/comments/api::page.page:njx99iv4p4txuqp307ye8625/comment/2/report-abuse
Example request body
{"reason":"<reason enum>","content":"This comment is not relevant"}
Available reason enums:BAD_WORDS
,OTHER
,DISCRIMINATION
(want more? Seeconfiguration section.)
Example response body
{// -- Comment Abuse Report fields ---}
Possible response codes
200
- Successful. Response with reported abuse.409
- Conflict. Occurs when trying to report an abuse to a non existing comment.
Strapi Users vs. Generic authors
Keep in mind that if you're using auth / authz your requests to setup proper user contexts it has got higher priority in order to take author data comparing to
author
property provided as part of your payload.
Testing
To test all queries and understand the schemas use GraphQL Playground exposed by
@strapi/plugin-graphql
onhttp://localhost:1337/graphql
REST API equivalent:Public REST API -> Get Comments
Example request
query {findAllInHierarchy(relation:"api::page.page:njx99iv4p4txuqp307ye8625") {idcontentblockedchildren {idcontent }threadOf {id }author {idname } }}
Example response
{"data": {"findAllInHierarchy": [ {"id":1,"content":"Test","blocked":false,"children": [ {"id":6,"content":"Text to search for" }// ... ],"threadOf":null,"author": {"id":"123456","name":"Joe Doe" } }// ... ] }}
REST API equivalent:Public REST API -> Get Comments (flat structure)
Example request
query {findAllFlat(relation:"api::page.page:njx99iv4p4txuqp307ye8625"filters: {content: {contains:"Test" } } ) {idcontentblockedthreadOf {id }author {idname } }}
Example response
{"data": {"findAllFlat": [ {"id":3,"content":"Test","blocked":false,"threadOf":null,"author": {"id":"123456","name":"Joe Doe" } },// ... ] }}
REST API equivalent:Public REST API -> Get Comments (by Author)
Example request
query {findAllPerAuthor(authorId:1,authorType: STRAPI) { //authorTypemightbeoneof [GENERIC,STRAPI]data {idcontentblockedthreadOf {id } } }}
Example response
{"data": {"findAllPerAuthor": {"data": [ {"id":4,"content":"Hackaton test comment","blocked":false,"threadOf": {"id":1 } }// ... ] } }
REST API equivalent:Public REST API -> Post a Comment
Example request
mutationcreateComment {createComment(input: {relation:"api::page.page:njx99iv4p4txuqp307ye8625"content:"Hello World!"threadOf:3author: {id:"12345678",name:"John Wick",email:"test@test.pl" } # Optional if using auth / authz requests } ) {idcontentthreadOf {id }author {idname } }}
Example response
{"data": {"createComment": {"id":34,"content":"Hello World!","threadOf": {"id":3 },"author": {"id":"12345678","name":"John Wick" } } }}
REST API equivalent:Public REST API -> Update Comment
Example request
mutationupdateComment {updateComment(input: {id:34relation:"api::page.page:njx99iv4p4txuqp307ye8625"content:"I've changed it!"author: {id:"12345678" } # Optional if using auth / authz requests } ) {idcontentthreadOf {id }author {idname }createdAtupdatedAt }}
Example response
{"data": {"updateComment": {"id":34,"content":"I've changed it!","threadOf": {"id":3 },"author": {"id":"12345678","name":"John Wick" },"createdAt":"2022-01-26T07:45:35.978Z","updatedAt":"2022-01-26T07:47:44.659Z" } }}
REST API equivalent:Public REST API -> Delete Comment
Example request
mutationremoveComment {removeComment(input: {id:33relation:"api::page.page:njx99iv4p4txuqp307ye8625"author: {id:"12345678" } # Optional if using auth / authz requests } ) {idremoved }}
Example response
{"data": {"removeComment": {"id":33,"removed":true } }}
REST API equivalent:Public REST API -> Issue Abuse Report against specified Comment
Example request body
mutationcreateAbuseReport {createAbuseReport(input: {commentId:34relation:"api::page.page:njx99iv4p4txuqp307ye8625"reason: BAD_LANGUAGEcontent:"Rude language" } ) {idreasoncontentrelated {idauthor {idname } } }}
Available reason enums:BAD_WORDS
,OTHER
,DISCRIMINATION
(want more? Seeconfiguration section.)
Example response
{"data": {"createAbuseReport": {"id":28,"content":"Rude language","reason":"DISCRIMINATION","related": {"id":34,"author": {"id":"12345678","name":"John Wick" } } } }}
Live example of plugin usage can be found in theVirtusLab Strapi Examples repository.
For developers who upgrades their Strapi instance custom field from Comments plugin is available. Custom field can be picked from content types' edit page or added in definition file.
Read more about this feature inStrapi's docs.
Comments plugin allows to register lifecycle hooks forComment
andComment report
content types.
You can read more about lifecycle hookshere. (You can set a listener for all of the hooks).
Lifecycle hooks can be register either inregister()
orbootstrap()
methods of your server. You can register more than one listener for a specified lifecycle hook. For example: you want to do three things on report creation and do not want to handle all of these actions in one big function. You can split logic in as many listeners as you want.
Listeners can by sync andasync
.
Be aware that lifecycle hooks registered in
register()
may be fired by plugin's bootstrapping. If you want listen to events triggered after server's startup usebootstrap()
.
Example:
constcommentsCommonService=strapi.plugin("comments").service("common");commentsCommonService.registerLifecycleHook({callback:async({ action, result})=>{constsaveResult=awaitlogIntoSystem(action,result);console.log(saveResult);},contentTypeName:"comment",hookName:"afterCreate",});commentsCommonService.registerLifecycleHook({callback:async({ action, result})=>{constsaveResult=awaitlogIntoSystem(action,result);console.log(saveResult);},contentTypeName:"report",hookName:"afterCreate",});
Q: I would like to use GraphQL schemas but I'm not getting any response, just 403 or 500 for every query or even proper types etc. What should I do?
A: There is a one trick you might try. Strapi by default is ordering plugins by the way which takesstrapi-plugin-graphql
to initialize earlier than other plugins so types might not be injected. If you don't have it yet, please createconfig/plugins.{js|ts}
file and put there following lines (putgraphql
at the end):
module.exports={'comments':{enabled:true},'graphql':{enabled:true},};
If you already got it, make sure thatcomments
plugin is inserted beforegraphql
. That should do the job.
Feel free to fork and make a Pull Request to this plugin project. All the input is warmly welcome!
For general help using Strapi, please refer tothe official Strapi documentation. For additional help, you can use one of these channels to ask a question:
- Discord We're present on official Strapi Discord workspace. Find us by
[VirtusLab]
prefix and DM. - Slack - VirtusLab Open Source We're present on a public channel #strapi-molecules
- GitHub (Bug reports, Contributions, Questions and Discussions)
- E-mail - we will respond back as soon as possible
MIT License Copyright (c)VirtusLab Sp. z o.o. &Strapi Solutions.
About
A plugin for Strapi Headless CMS that provides end to end comments feature with their moderation panel, bad words filtering, abuse reporting and more.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.