Skip to content

Commit 536bf56

Browse files
committed
Comment Mentions: Added core back-end logic
- Added new user notification preference, opt-in by default - Added parser to extract mentions from comment HTML, with tests to cover. - Added notification and notification handling Not yet tested, needs testing coverage.
1 parent e2f91c2 commit 536bf56

File tree

9 files changed

+93
-1
lines changed

9 files changed

+93
-1
lines changed

app/Activity/Notifications/NotificationManager.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use BookStack\Activity\Models\Activity;
77
use BookStack\Activity\Models\Loggable;
88
use BookStack\Activity\Notifications\Handlers\CommentCreationNotificationHandler;
9+
use BookStack\Activity\Notifications\Handlers\CommentMentionNotificationHandler;
910
use BookStack\Activity\Notifications\Handlers\NotificationHandler;
1011
use BookStack\Activity\Notifications\Handlers\PageCreationNotificationHandler;
1112
use BookStack\Activity\Notifications\Handlers\PageUpdateNotificationHandler;
@@ -48,5 +49,7 @@ public function loadDefaultHandlers(): void
4849
$this->registerHandler(ActivityType::PAGE_CREATE, PageCreationNotificationHandler::class);
4950
$this->registerHandler(ActivityType::PAGE_UPDATE, PageUpdateNotificationHandler::class);
5051
$this->registerHandler(ActivityType::COMMENT_CREATE, CommentCreationNotificationHandler::class);
52+
$this->registerHandler(ActivityType::COMMENT_CREATE, CommentMentionNotificationHandler::class);
53+
$this->registerHandler(ActivityType::COMMENT_UPDATE, CommentMentionNotificationHandler::class);
5154
}
5255
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace BookStack\Activity\Tools;
4+
5+
use BookStack\Util\HtmlDocument;
6+
use DOMElement;
7+
8+
class MentionParser
9+
{
10+
public function parseUserIdsFromHtml(string $html): array
11+
{
12+
$doc = new HtmlDocument($html);
13+
14+
$ids = [];
15+
$mentionLinks = $doc->queryXPath('//a[@data-mention-user-id]');
16+
17+
foreach ($mentionLinks as $link) {
18+
if ($link instanceof DOMElement) {
19+
$id = intval($link->getAttribute('data-mention-user-id'));
20+
if ($id > 0) {
21+
$ids[] = $id;
22+
}
23+
}
24+
}
25+
26+
return array_values(array_unique($ids));
27+
}
28+
}

app/App/Providers/AppServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace BookStack\App\Providers;
44

55
use BookStack\Access\SocialDriverManager;
6+
use BookStack\Activity\Models\Comment;
67
use BookStack\Activity\Tools\ActivityLogger;
78
use BookStack\Entities\Models\Book;
89
use BookStack\Entities\Models\Bookshelf;
@@ -73,6 +74,7 @@ public function boot(): void
7374
'book' => Book::class,
7475
'chapter' => Chapter::class,
7576
'page' => Page::class,
77+
'comment' => Comment::class,
7678
]);
7779
}
7880
}

app/Config/setting-defaults.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
'bookshelves_view_type' => env('APP_VIEWS_BOOKSHELVES', 'grid'),
4242
'bookshelf_view_type' => env('APP_VIEWS_BOOKSHELF', 'grid'),
4343
'books_view_type' => env('APP_VIEWS_BOOKS', 'grid'),
44+
'notifications#comment-mentions' => true,
4445
],
4546

4647
];

app/Settings/UserNotificationPreferences.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,14 @@ public function notifyOnCommentReplies(): bool
2626
return $this->getNotificationSetting('comment-replies');
2727
}
2828

29+
public function notifyOnCommentMentions(): bool
30+
{
31+
return $this->getNotificationSetting('comment-mentions');
32+
}
33+
2934
public function updateFromSettingsArray(array $settings)
3035
{
31-
$allowList = ['own-page-changes', 'own-page-comments', 'comment-replies'];
36+
$allowList = ['own-page-changes', 'own-page-comments', 'comment-replies', 'comment-mentions'];
3237
foreach ($settings as $setting => $status) {
3338
if (!in_array($setting, $allowList)) {
3439
continue;

lang/en/notifications.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
'updated_page_subject' => 'Updated page: :pageName',
1212
'updated_page_intro' => 'A page has been updated in :appName:',
1313
'updated_page_debounce' => 'To prevent a mass of notifications, for a while you won\'t be sent notifications for further edits to this page by the same editor.',
14+
'comment_mention_subject' => 'You were mentioned in a comment on :pageName',
15+
'comment_mention_intro' => 'You were mentioned in a comment on :appName:',
1416

1517
'detail_page_name' => 'Page Name:',
1618
'detail_page_path' => 'Page Path:',

lang/en/preferences.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
'notifications_desc' => 'Control the email notifications you receive when certain activity is performed within the system.',
2424
'notifications_opt_own_page_changes' => 'Notify upon changes to pages I own',
2525
'notifications_opt_own_page_comments' => 'Notify upon comments on pages I own',
26+
'notifications_opt_comment_mentions' => 'Notify when I\'m mentioned in a comment',
2627
'notifications_opt_comment_replies' => 'Notify upon replies to my comments',
2728
'notifications_save' => 'Save Preferences',
2829
'notifications_update_success' => 'Notification preferences have been updated!',

resources/views/users/account/notifications.blade.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
'label' => trans('preferences.notifications_opt_comment_replies'),
3434
])
3535
</div>
36+
<div>
37+
@include('form.toggle-switch', [
38+
'name' => 'preferences[comment-mentions]',
39+
'value' => $preferences->notifyOnCommentMentions(),
40+
'label' => trans('preferences.notifications_opt_comment_mentions'),
41+
])
42+
</div>
3643
@endif
3744
</div>
3845

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Tests\Activity;
4+
5+
use BookStack\Activity\Tools\MentionParser;
6+
use Tests\TestCase;
7+
8+
class MentionParserTest extends TestCase
9+
{
10+
public function test_it_extracts_mentions()
11+
{
12+
$parser = new MentionParser();
13+
14+
// Test basic mention extraction
15+
$html = '<p>Hello <a href="/user/5" data-mention-user-id="5">@User</a></p>';
16+
$result = $parser->parseUserIdsFromHtml($html);
17+
$this->assertEquals([5], $result);
18+
19+
// Test multiple mentions
20+
$html = '<p><a data-mention-user-id="1">@Alice</a> and <a data-mention-user-id="2">@Bob</a></p>';
21+
$result = $parser->parseUserIdsFromHtml($html);
22+
$this->assertEquals([1, 2], $result);
23+
24+
// Test filtering out invalid IDs (zero and negative)
25+
$html = '<p><a data-mention-user-id="0">@Invalid</a> <a data-mention-user-id="-5">@Negative</a> <a data-mention-user-id="3">@Valid</a></p>';
26+
$result = $parser->parseUserIdsFromHtml($html);
27+
$this->assertEquals([3], $result);
28+
29+
// Test non-mention links are ignored
30+
$html = '<p><a href="/page/1">Normal Link</a> <a data-mention-user-id="7">@User</a></p>';
31+
$result = $parser->parseUserIdsFromHtml($html);
32+
$this->assertEquals([7], $result);
33+
34+
// Test empty HTML
35+
$result = $parser->parseUserIdsFromHtml('');
36+
$this->assertEquals([], $result);
37+
38+
// Test duplicate user IDs
39+
$html = '<p><a data-mention-user-id="4">@User</a> mentioned <a data-mention-user-id="4">@User</a> again</p>';
40+
$result = $parser->parseUserIdsFromHtml($html);
41+
$this->assertEquals([4], $result);
42+
}
43+
}

0 commit comments

Comments
 (0)