From 722abffa0692a08d5373939b205778380e4355fd Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Mon, 12 Feb 2024 16:22:27 -0500 Subject: [PATCH 1/4] Add safe link. --- .../js/TipTap/Extentions/SafeLink.js | 22 ++++++++++++++++ .../resources/js/userToUserChat.js | 25 ++++++++++++++----- .../views/filament/pages/user-chat.blade.php | 25 +++++++++++++++++++ 3 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 app-modules/in-app-communication/resources/js/TipTap/Extentions/SafeLink.js diff --git a/app-modules/in-app-communication/resources/js/TipTap/Extentions/SafeLink.js b/app-modules/in-app-communication/resources/js/TipTap/Extentions/SafeLink.js new file mode 100644 index 0000000000..eeca063526 --- /dev/null +++ b/app-modules/in-app-communication/resources/js/TipTap/Extentions/SafeLink.js @@ -0,0 +1,22 @@ +import Link from '@tiptap/extension-link'; +import { mergeAttributes } from '@tiptap/core'; + +export const SafeLink = Link.extend({ + renderHTML({ HTMLAttributes }) { + // This is directly pulled from the Link extension - leave as is. + // eslint-disable-next-line no-script-url + if (HTMLAttributes.href?.startsWith('javascript:')) { + return [ + 'button', + mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, 'data-safe-link': 'true', href: '' }), + 0, + ]; + } + + return [ + 'button', + mergeAttributes(this.options.HTMLAttributes, { ...HTMLAttributes, 'data-safe-link': 'true' }), + 0, + ]; + }, +}); diff --git a/app-modules/in-app-communication/resources/js/userToUserChat.js b/app-modules/in-app-communication/resources/js/userToUserChat.js index 65ccff5d9e..dd10a5052b 100644 --- a/app-modules/in-app-communication/resources/js/userToUserChat.js +++ b/app-modules/in-app-communication/resources/js/userToUserChat.js @@ -36,7 +36,7 @@ document.addEventListener('alpine:init', () => { const { generateHTML } = require('@tiptap/html'); const { Color } = require('@tiptap/extension-color'); const { Editor } = require('@tiptap/core'); - const { Link } = require('@tiptap/extension-link'); + const { SafeLink } = require('./TipTap/Extentions/SafeLink'); const { Mention } = require('./TipTap/Extentions/Mention'); const { Placeholder } = require('@tiptap/extension-placeholder'); const { StarterKit } = require('@tiptap/starter-kit'); @@ -172,6 +172,7 @@ document.addEventListener('alpine:init', () => { } if (selectedConversation) { + console.log('selectedConversation'); this.loadingMessage = 'Loading conversation…'; this.conversation = await conversationsClient @@ -240,6 +241,18 @@ document.addEventListener('alpine:init', () => { window.addEventListener('chatTyping', () => { this.conversation?.typing(); }); + + window.addEventListener('click', (event) => { + const target = event.target; + console.log('click', target); + if (target.matches('[data-safe-link]')) { + this.openConfirmationModal(target.getAttribute('href')); + } + }); + }, + openConfirmationModal(href) { + console.log('openConfirmationModal', href); + this.$dispatch('open-modal', { id: 'confirmSafeLink', href: href }); }, async getMessages() { this.loadingMessage = 'Loading messages…'; @@ -249,7 +262,10 @@ document.addEventListener('alpine:init', () => { .then((messages) => { this.messagePaginator = messages; + console.log('getMessages', messages.items); + messages.items.forEach(async (message) => { + console.log('message', message); this.messages.push({ avatar: await this.getAvatarUrl(message.author), author: await this.getAuthorName(message.author), @@ -314,10 +330,9 @@ document.addEventListener('alpine:init', () => { generateHTML: (content) => { return generateHTML(content, [ Color, - Link.configure({ + SafeLink.configure({ openOnClick: false, HTMLAttributes: { - target: '_blank', rel: 'noopener noreferrer nofollow', class: 'underline font-medium text-primary-600 dark:text-primary-500', }, @@ -351,11 +366,9 @@ document.addEventListener('alpine:init', () => { element: this.$refs.element, extensions: [ Color, - Link.configure({ + SafeLink.configure({ openOnClick: false, HTMLAttributes: { - target: '_blank', - rel: 'noopener noreferrer nofollow', class: 'underline font-medium text-primary-600 dark:text-primary-500', }, }), diff --git a/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php b/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php index 041fdbe9b6..2e55972950 100644 --- a/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php +++ b/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php @@ -612,4 +612,29 @@ class="h-2 w-2 animate-bounce rounded-full bg-primary-500 text-primary-500 anima } + + + Link confirmation + + + +
+ You are about to open another browser tab and visit: + +
+ +
+ + +
+ + + + +
+
+
From efecde5b5b7e0e3929b7c73bf25c959d15a66856 Mon Sep 17 00:00:00 2001 From: Derek Goetz Date: Mon, 12 Feb 2024 19:06:33 -0500 Subject: [PATCH 2/4] Remove console logs. --- .../resources/js/userToUserChat.js | 8 +- .../views/filament/pages/user-chat.blade.php | 766 +++++++----------- 2 files changed, 297 insertions(+), 477 deletions(-) diff --git a/app-modules/in-app-communication/resources/js/userToUserChat.js b/app-modules/in-app-communication/resources/js/userToUserChat.js index dd10a5052b..53e5dc2a85 100644 --- a/app-modules/in-app-communication/resources/js/userToUserChat.js +++ b/app-modules/in-app-communication/resources/js/userToUserChat.js @@ -172,7 +172,6 @@ document.addEventListener('alpine:init', () => { } if (selectedConversation) { - console.log('selectedConversation'); this.loadingMessage = 'Loading conversation…'; this.conversation = await conversationsClient @@ -244,14 +243,13 @@ document.addEventListener('alpine:init', () => { window.addEventListener('click', (event) => { const target = event.target; - console.log('click', target); + if (target.matches('[data-safe-link]')) { this.openConfirmationModal(target.getAttribute('href')); } }); }, openConfirmationModal(href) { - console.log('openConfirmationModal', href); this.$dispatch('open-modal', { id: 'confirmSafeLink', href: href }); }, async getMessages() { @@ -262,10 +260,7 @@ document.addEventListener('alpine:init', () => { .then((messages) => { this.messagePaginator = messages; - console.log('getMessages', messages.items); - messages.items.forEach(async (message) => { - console.log('message', message); this.messages.push({ avatar: await this.getAvatarUrl(message.author), author: await this.getAuthorName(message.author), @@ -333,7 +328,6 @@ document.addEventListener('alpine:init', () => { SafeLink.configure({ openOnClick: false, HTMLAttributes: { - rel: 'noopener noreferrer nofollow', class: 'underline font-medium text-primary-600 dark:text-primary-500', }, }), diff --git a/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php b/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php index 2e55972950..80aca7f638 100644 --- a/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php +++ b/app-modules/in-app-communication/resources/views/filament/pages/user-chat.blade.php @@ -56,66 +56,57 @@ function (array $carry, TwilioConversation $conversation): array {
@foreach ($conversationGroups as $conversations) -
-
- - {{ $loop->first ? 'Channels' : 'Direct messages' }} - - - @if (count($conversations)) - @if ($loop->first) -
- {{ (clone $this->joinChannelsAction)->iconButton()->size('sm')->tooltip('Join channels')->icon('heroicon-m-list-bullet') }} - {{ (clone $this->newChannelAction)->iconButton()->size('sm')->tooltip('Create a channel')->icon('heroicon-m-plus') }} -
- @else - {{ (clone $this->newUserToUserChatAction)->iconButton()->size('sm')->tooltip('Start a chat')->icon('heroicon-m-plus') }} - @endif - @endif -
+
+
+ + {{ $loop->first ? 'Channels' : 'Direct messages' }} + @if (count($conversations)) -
    - @foreach ($conversations as $conversationItem) - @php - /** @var TwilioConversation $conversationItem */ - @endphp -
  • - $conversation?->getKey() === $conversationItem->getKey(), + @if ($loop->first) +
    + {{ (clone $this->joinChannelsAction)->iconButton()->size('sm')->tooltip('Join channels')->icon('heroicon-m-list-bullet') }} + {{ (clone $this->newChannelAction)->iconButton()->size('sm')->tooltip('Create a channel')->icon('heroicon-m-plus') }} +
    + @else + {{ (clone $this->newUserToUserChatAction)->iconButton()->size('sm')->tooltip('Start a chat')->icon('heroicon-m-plus') }} + @endif + @endif +
+ + @if (count($conversations)) +
    + @foreach ($conversations as $conversationItem) + @php + /** @var TwilioConversation $conversationItem */ + @endphp +
  • + $conversation?->getKey() === $conversationItem->getKey(), + ])> + - @php - /** @var TwilioConversationUser $participant */ - $participant = $conversationItem->participant; - @endphp - @if ($participant->is_pinned) - {{ ($this->togglePinChannelAction)(['id' => $conversationItem->getKey()])->icon('heroicon-s-star')->tooltip('Unpin') }} - @else - {{ ($this->togglePinChannelAction)(['id' => $conversationItem->getKey()])->icon('heroicon-o-star')->tooltip('Pin') }} - @endif -
  • - @endforeach -
- @elseif ($loop->first) -
- You do not belong to any channels yet. - You can - {{ (clone $this->joinChannelsAction)->link()->label('browse a list')->tooltip(null)->icon(null) }} - or - {{ (clone $this->newChannelAction)->link()->label('create a new one')->tooltip(null)->icon(null) }} - . -
- @else -
- You do not have any direct messages yet. You can - {{ (clone $this->newUserToUserChatAction)->link()->label('start one')->tooltip(null)->icon(null) }} - . -
- @endif +
+ + @php + /** @var TwilioConversationUser $participant */ + $participant = $conversationItem->participant; + @endphp + @if ($participant->is_pinned) + {{ ($this->togglePinChannelAction)(['id' => $conversationItem->getKey()])->icon('heroicon-s-star')->tooltip('Unpin') }} + @else + {{ ($this->togglePinChannelAction)(['id' => $conversationItem->getKey()])->icon('heroicon-o-star')->tooltip('Pin') }} + @endif + + @endforeach + + @elseif ($loop->first) +
+ You do not belong to any channels yet. + You can + {{ (clone $this->joinChannelsAction)->link()->label('browse a list')->tooltip(null)->icon(null) }} + or + {{ (clone $this->newChannelAction)->link()->label('create a new one')->tooltip(null)->icon(null) }} + .
+ @else +
+ You do not have any direct messages yet. You can + {{ (clone $this->newUserToUserChatAction)->link()->label('start one')->tooltip(null)->icon(null) }} + . +
+ @endif +
@endforeach
@php - /** @var TwilioConversation $conversation */ + /** @var TwilioConversation $conversation */ @endphp @if ($conversation) -
-
- -

+
+
+ +

+
+