comment.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. module.exports = function(crowi, app) {
  2. const logger = require('@alias/logger')('growi:routes:comment');
  3. const Comment = crowi.model('Comment');
  4. const User = crowi.model('User');
  5. const Page = crowi.model('Page');
  6. const ApiResponse = require('../util/apiResponse');
  7. const globalNotificationService = crowi.getGlobalNotificationService();
  8. const { body } = require('express-validator/check');
  9. const mongoose = require('mongoose');
  10. const ObjectId = mongoose.Types.ObjectId;
  11. const actions = {};
  12. const api = {};
  13. actions.api = api;
  14. api.validators = {};
  15. /**
  16. * @api {get} /comments.get Get comments of the page of the revision
  17. * @apiName GetComments
  18. * @apiGroup Comment
  19. *
  20. * @apiParam {String} page_id Page Id.
  21. * @apiParam {String} revision_id Revision Id.
  22. */
  23. api.get = async function(req, res) {
  24. const pageId = req.query.page_id;
  25. const revisionId = req.query.revision_id;
  26. // check whether accessible
  27. const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
  28. if (!isAccessible) {
  29. return res.json(ApiResponse.error('Current user is not accessible to this page.'));
  30. }
  31. let fetcher = null;
  32. try {
  33. if (revisionId) {
  34. fetcher = Comment.getCommentsByRevisionId(revisionId);
  35. }
  36. else {
  37. fetcher = Comment.getCommentsByPageId(pageId);
  38. }
  39. }
  40. catch (err) {
  41. return res.json(ApiResponse.error(err));
  42. }
  43. const comments = await fetcher.populate(
  44. { path: 'creator', select: User.USER_PUBLIC_FIELDS, populate: User.IMAGE_POPULATION },
  45. );
  46. res.json(ApiResponse.success({ comments }));
  47. };
  48. api.validators.add = function() {
  49. const validator = [
  50. body('commentForm.page_id').exists(),
  51. body('commentForm.revision_id').exists(),
  52. body('commentForm.comment').exists(),
  53. body('commentForm.comment_position').isInt(),
  54. body('commentForm.is_markdown').isBoolean(),
  55. body('commentForm.replyTo').exists().custom((value) => {
  56. if (value === '') {
  57. return undefined;
  58. }
  59. return ObjectId(value);
  60. }),
  61. body('slackNotificationForm.isSlackEnabled').isBoolean().exists(),
  62. ];
  63. return validator;
  64. };
  65. /**
  66. * @api {post} /comments.add Post comment for the page
  67. * @apiName PostComment
  68. * @apiGroup Comment
  69. *
  70. * @apiParam {String} page_id Page Id.
  71. * @apiParam {String} revision_id Revision Id.
  72. * @apiParam {String} comment Comment body
  73. * @apiParam {Number} comment_position=-1 Line number of the comment
  74. */
  75. api.add = async function(req, res) {
  76. const { commentForm, slackNotificationForm } = req.body;
  77. const { validationResult } = require('express-validator/check');
  78. const errors = validationResult(req.body);
  79. if (!errors.isEmpty()) {
  80. // return res.json(ApiResponse.error('Invalid comment.'));
  81. // return res.status(422).json({ errors: errors.array() });
  82. return res.json(ApiResponse.error('コメントを入力してください。'));
  83. }
  84. const pageId = commentForm.page_id;
  85. const revisionId = commentForm.revision_id;
  86. const comment = commentForm.comment;
  87. const position = commentForm.comment_position || -1;
  88. const isMarkdown = commentForm.is_markdown;
  89. const replyTo = commentForm.replyTo;
  90. // check whether accessible
  91. const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
  92. if (!isAccessible) {
  93. return res.json(ApiResponse.error('Current user is not accessible to this page.'));
  94. }
  95. const createdComment = await Comment.create(pageId, req.user._id, revisionId, comment, position, isMarkdown, replyTo)
  96. .catch((err) => {
  97. return res.json(ApiResponse.error(err));
  98. });
  99. // update page
  100. const page = await Page.findOneAndUpdate({ _id: pageId }, {
  101. lastUpdateUser: req.user,
  102. updatedAt: new Date(),
  103. });
  104. res.json(ApiResponse.success({ comment: createdComment }));
  105. const path = page.path;
  106. // global notification
  107. globalNotificationService.notifyComment(createdComment, path);
  108. // slack notification
  109. if (slackNotificationForm.isSlackEnabled) {
  110. const user = await User.findUserByUsername(req.user.username);
  111. const channels = slackNotificationForm.slackChannels;
  112. if (channels) {
  113. page.updateSlackChannel(channels).catch((err) => {
  114. logger.error('Error occured in updating slack channels: ', err);
  115. });
  116. const promises = channels.split(',').map((chan) => {
  117. return crowi.slack.postComment(createdComment, user, chan, path);
  118. });
  119. Promise.all(promises)
  120. .catch((err) => {
  121. logger.error('Error occured in sending slack notification: ', err);
  122. });
  123. }
  124. }
  125. };
  126. /**
  127. * @api {post} /comments.update Update comment dody
  128. * @apiName UpdateComment
  129. * @apiGroup Comment
  130. *
  131. */
  132. api.update = async function(req, res) {
  133. const { commentForm } = req.body;
  134. const pageId = commentForm.page_id;
  135. const revisionId = commentForm.revision_id;
  136. const comment = commentForm.comment;
  137. const isMarkdown = commentForm.is_markdown;
  138. const commentId = commentForm.comment_id;
  139. const creatorId = commentForm.creator_id;
  140. if (comment === '') {
  141. return res.json(ApiResponse.error('Comment text is required'));
  142. }
  143. if (commentId == null) {
  144. return res.json(ApiResponse.error('\'comment_id\' is undefined'));
  145. }
  146. // check whether accessible
  147. const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user._id, revisionId, comment, isMarkdown, req.user);
  148. if (!isAccessible) {
  149. return res.json(ApiResponse.error('Current user is not accessible to this page.'));
  150. }
  151. try {
  152. const updatedComment = await Comment.updateCommentsByPageId(comment, isMarkdown, commentId);
  153. const page = await Page.findOneAndUpdate({ _id: pageId }, {
  154. lastUpdateUser: req.user,
  155. updatedAt: new Date(),
  156. });
  157. res.json(ApiResponse.success({ comment: updatedComment }));
  158. const path = page.path;
  159. // global notification
  160. globalNotificationService.notifyComment(updatedComment, path);
  161. }
  162. catch (err) {
  163. return res.json(ApiResponse.error(err));
  164. }
  165. };
  166. /**
  167. * @api {post} /comments.remove Remove specified comment
  168. * @apiName RemoveComment
  169. * @apiGroup Comment
  170. *
  171. * @apiParam {String} comment_id Comment Id.
  172. */
  173. api.remove = async function(req, res) {
  174. const commentId = req.body.comment_id;
  175. if (!commentId) {
  176. return Promise.resolve(res.json(ApiResponse.error('\'comment_id\' is undefined')));
  177. }
  178. try {
  179. const comment = await Comment.findById(commentId).exec();
  180. if (comment == null) {
  181. throw new Error('This comment does not exist.');
  182. }
  183. // check whether accessible
  184. const pageId = comment.page;
  185. const isAccessible = await Page.isAccessiblePageByViewer(pageId, req.user);
  186. if (!isAccessible) {
  187. throw new Error('Current user is not accessible to this page.');
  188. }
  189. await comment.removeWithReplies();
  190. await Page.updateCommentCount(comment.page);
  191. }
  192. catch (err) {
  193. return res.json(ApiResponse.error(err));
  194. }
  195. return res.json(ApiResponse.success({}));
  196. };
  197. return actions;
  198. };