From 34923d9b8d4f3f9b03058f462337ec5753b1b3c6 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 26 Mar 2024 07:09:41 -0600 Subject: [PATCH] feat: Enable unfurling comments in Slack (#6716) --- plugins/slack/server/api/hooks.ts | 44 +++++++++++++++++----- server/models/Comment.ts | 8 ++++ shared/i18n/locales/en_US/translation.json | 1 + 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/plugins/slack/server/api/hooks.ts b/plugins/slack/server/api/hooks.ts index bdafc20eb..977372685 100644 --- a/plugins/slack/server/api/hooks.ts +++ b/plugins/slack/server/api/hooks.ts @@ -1,8 +1,10 @@ import { t } from "i18next"; import Router from "koa-router"; import escapeRegExp from "lodash/escapeRegExp"; +import queryString from "query-string"; import { z } from "zod"; import { IntegrationService, IntegrationType } from "@shared/types"; +import parseDocumentSlug from "@shared/utils/parseDocumentSlug"; import { AuthenticationError, InvalidRequestError, @@ -19,8 +21,10 @@ import { Integration, IntegrationAuthentication, AuthenticationProvider, + Comment, } from "@server/models"; import SearchHelper from "@server/models/helpers/SearchHelper"; +import { can } from "@server/policies"; import { APIContext } from "@server/types"; import { safeEqual } from "@server/utils/crypto"; import { opts } from "@server/utils/i18n"; @@ -78,16 +82,38 @@ router.post( const unfurls = {}; for (const link of event.links) { - const id = link.url.slice(link.url.lastIndexOf("/") + 1); - const doc = await Document.findByPk(id); - if (!doc || doc.teamId !== user.teamId) { - continue; + const documentId = parseDocumentSlug(link.url); + if (documentId) { + const doc = await Document.findByPk(documentId, { userId: user.id }); + + if (doc && can(user, "read", doc)) { + const commentId = queryString.parse( + link.url.split("?")[1] + )?.commentId; + + if (commentId) { + const comment = await Comment.findByPk(commentId as string); + if (!comment) { + continue; + } + + unfurls[link.url] = { + title: t(`Comment by {{ author }} on "{{ title }}"`, { + author: comment.createdBy.name, + title: doc.title, + ...opts(user), + }), + text: comment.toPlainText(), + }; + } else { + unfurls[link.url] = { + title: doc.title, + text: doc.getSummary(), + color: doc.collection?.color, + }; + } + } } - unfurls[link.url] = { - title: doc.title, - text: doc.getSummary(), - color: doc.collection?.color, - }; } await Slack.post("chat.unfurl", { diff --git a/server/models/Comment.ts b/server/models/Comment.ts index 710df1aeb..4dd5e85c9 100644 --- a/server/models/Comment.ts +++ b/server/models/Comment.ts @@ -1,3 +1,4 @@ +import { Node } from "prosemirror-model"; import { InferAttributes, InferCreationAttributes } from "sequelize"; import { DataType, @@ -9,7 +10,9 @@ import { DefaultScope, } from "sequelize-typescript"; import type { ProsemirrorData } from "@shared/types"; +import ProsemirrorHelper from "@shared/utils/ProsemirrorHelper"; import { CommentValidation } from "@shared/validations"; +import { schema } from "@server/editor"; import Document from "./Document"; import User from "./User"; import ParanoidModel from "./base/ParanoidModel"; @@ -71,6 +74,11 @@ class Comment extends ParanoidModel< @ForeignKey(() => Comment) @Column(DataType.UUID) parentCommentId: string; + + public toPlainText() { + const node = Node.fromJSON(schema, this.data); + return ProsemirrorHelper.toPlainText(node, schema); + } } export default Comment; diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json index e84140e5c..9ef1b0ff8 100644 --- a/shared/i18n/locales/en_US/translation.json +++ b/shared/i18n/locales/en_US/translation.json @@ -970,6 +970,7 @@ "Get rich previews of {{ appName }} links shared in Slack and use the {{ command }} slash command to search for documents without leaving your chat.": "Get rich previews of {{ appName }} links shared in Slack and use the {{ command }} slash command to search for documents without leaving your chat.", "This will remove the Outline slash command from your Slack workspace. Are you sure?": "This will remove the Outline slash command from your Slack workspace. Are you sure?", "Connect {{appName}} collections to Slack channels. Messages will be automatically posted to Slack when documents are published or updated.": "Connect {{appName}} collections to Slack channels. Messages will be automatically posted to Slack when documents are published or updated.", + "Comment by {{ author }} on \"{{ title }}\"": "Comment by {{ author }} on \"{{ title }}\"", "How to use {{ command }}": "How to use {{ command }}", "To search your workspace use {{ command }}. \nType {{ command2 }} help to display this help text.": "To search your workspace use {{ command }}. \nType {{ command2 }} help to display this help text.", "Post to Channel": "Post to Channel",