diff --git a/backend/src/middleware/handleHtmlContent/handleContentData.js b/backend/src/middleware/handleHtmlContent/handleContentData.js new file mode 100644 index 000000000..53a6b65fb --- /dev/null +++ b/backend/src/middleware/handleHtmlContent/handleContentData.js @@ -0,0 +1,77 @@ +import extractMentionedUsers from './notifications/extractMentionedUsers' +import extractHashtags from './hashtags/extractHashtags' + +const notify = async (resolve, root, args, context, resolveInfo) => { + // extract user ids before xss-middleware removes link classes + const ids = extractMentionedUsers(args.content) + + console.log('ids: ', ids) + + const post = await resolve(root, args, context, resolveInfo) + + const session = context.driver.session() + const { + id: postId + } = post + const createdAt = new Date().toISOString() + const cypher = ` + match(u:User) where u.id in $ids + match(p:Post) where p.id = $postId + create(n:Notification{id: apoc.create.uuid(), read: false, createdAt: $createdAt}) + merge (n)-[:NOTIFIED]->(u) + merge (p)-[:NOTIFIED]->(n) + ` + await session.run(cypher, { + ids, + createdAt, + postId + }) + session.close() + + return post +} + +const updateHashtagsOfPost = async (postId, resolve, root, args, context, resolveInfo) => { + // extract tag (hashtag) ids before xss-middleware removes link classes + const hashtags = extractHashtags(args.content) + + console.log('hashtags: ', hashtags) + + // const post = await resolve(root, args, context, resolveInfo) + + const session = context.driver.session() + // const { + // id: postId + // } = post + // const createdAt = new Date().toISOString() + const cypher = ` + MATCH (p:Post { id: $postId })-[oldRelations: TAGGED]->(oldTags: Tag) + DELETE oldRelations + WITH p + UNWIND $hashtags AS tagName + MERGE (t: Tag { id: tagName, name: tagName }) + MERGE (p)-[:TAGGED]->(t) + RETURN t + ` + await session.run(cypher, { + postId, + hashtags + }) + session.close() +} + +const handleContentData = async (resolve, root, args, context, resolveInfo) => { + // extract user ids before xss-middleware removes link classes + + const post = await notify(resolve, root, args, context, resolveInfo) + await updateHashtagsOfPost(post.id, resolve, root, args, context, resolveInfo) + + return post +} + +export default { + Mutation: { + CreatePost: handleContentData, + UpdatePost: handleContentData, + }, +} \ No newline at end of file diff --git a/backend/src/middleware/notifications/spec.js b/backend/src/middleware/handleHtmlContent/handleContentData.spec.js similarity index 100% rename from backend/src/middleware/notifications/spec.js rename to backend/src/middleware/handleHtmlContent/handleContentData.spec.js diff --git a/backend/src/middleware/handleHtmlContent/hashtags/extractHashtags.js b/backend/src/middleware/handleHtmlContent/hashtags/extractHashtags.js new file mode 100644 index 000000000..194179b16 --- /dev/null +++ b/backend/src/middleware/handleHtmlContent/hashtags/extractHashtags.js @@ -0,0 +1,21 @@ +import cheerio from 'cheerio' +const ID_REGEX = /\/search\/hashtag\/([\w\-.!~*'"(),]+)/g + +export default function (content) { + if (!content) return [] + const $ = cheerio.load(content) + const urls = $('.hashtag') + .map((_, el) => { + return $(el).attr('href') + }) + .get() + const hashtags = [] + urls.forEach(url => { + console.log('url: ', url) + let match + while ((match = ID_REGEX.exec(url)) != null) { + hashtags.push(match[1]) + } + }) + return hashtags +} \ No newline at end of file diff --git a/backend/src/middleware/notifications/extractIds/index.js b/backend/src/middleware/handleHtmlContent/notifications/extractMentionedUsers.js similarity index 100% rename from backend/src/middleware/notifications/extractIds/index.js rename to backend/src/middleware/handleHtmlContent/notifications/extractMentionedUsers.js diff --git a/backend/src/middleware/notifications/extractIds/spec.js b/backend/src/middleware/handleHtmlContent/notifications/extractMentionedUsers.spec.js similarity index 98% rename from backend/src/middleware/notifications/extractIds/spec.js rename to backend/src/middleware/handleHtmlContent/notifications/extractMentionedUsers.spec.js index 341c39cec..03de4f1bb 100644 --- a/backend/src/middleware/notifications/extractIds/spec.js +++ b/backend/src/middleware/handleHtmlContent/notifications/extractMentionedUsers.spec.js @@ -1,4 +1,4 @@ -import extractIds from '.' +import extractIds from './extractMentionedUsers' describe('extractIds', () => { describe('content undefined', () => { @@ -56,4 +56,4 @@ describe('extractIds', () => { }) }) }) -}) +}) \ No newline at end of file diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js index 9b85bd340..46470115f 100644 --- a/backend/src/middleware/index.js +++ b/backend/src/middleware/index.js @@ -11,7 +11,7 @@ import user from './userMiddleware' import includedFields from './includedFieldsMiddleware' import orderBy from './orderByMiddleware' import validation from './validation' -import notifications from './notifications' +import handleContentData from './handleHtmlContent/handleContentData' export default schema => { const middlewares = { @@ -22,7 +22,7 @@ export default schema => { validation: validation, sluggify: sluggify, excerpt: excerpt, - notifications: notifications, + handleContentData: handleContentData, xss: xss, softDelete: softDelete, user: user, @@ -38,7 +38,7 @@ export default schema => { 'validation', 'sluggify', 'excerpt', - 'notifications', + 'handleContentData', 'xss', 'softDelete', 'user', @@ -57,4 +57,4 @@ export default schema => { } return order.map(key => middlewares[key]) -} +} \ No newline at end of file diff --git a/backend/src/middleware/notifications/index.js b/backend/src/middleware/notifications/index.js deleted file mode 100644 index ca460a512..000000000 --- a/backend/src/middleware/notifications/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import extractIds from './extractIds' - -const notify = async (resolve, root, args, context, resolveInfo) => { - // extract user ids before xss-middleware removes link classes - const ids = extractIds(args.content) - - const post = await resolve(root, args, context, resolveInfo) - - const session = context.driver.session() - const { id: postId } = post - const createdAt = new Date().toISOString() - const cypher = ` - match(u:User) where u.id in $ids - match(p:Post) where p.id = $postId - create(n:Notification{id: apoc.create.uuid(), read: false, createdAt: $createdAt}) - merge (n)-[:NOTIFIED]->(u) - merge (p)-[:NOTIFIED]->(n) - ` - await session.run(cypher, { ids, createdAt, postId }) - session.close() - - return post -} - -export default { - Mutation: { - CreatePost: notify, - UpdatePost: notify, - }, -} diff --git a/webapp/components/Editor/index.vue b/webapp/components/Editor/index.vue index b7fa0263a..dbb30a42e 100644 --- a/webapp/components/Editor/index.vue +++ b/webapp/components/Editor/index.vue @@ -454,7 +454,7 @@ export default { }, hashtag: { // TODO: Fill up with input hashtag in search field - url: `/search/hashtag:${item.name}`, + url: `/search/hashtag/${item.name}`, label: item.name, }, }