+ public function test_notify_own_page_changes()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $prefs = new UserNotificationPreferences($editor);
+ $prefs->updateFromSettingsArray(['own-page-changes' => 'true']);
+
+ $notifications = Notification::fake();
+
+ $this->asAdmin();
+ $this->entities->updatePage($entities['page'], ['name' => 'My updated page', 'html' => 'Hello']);
+ $notifications->assertSentTo($editor, PageUpdateNotification::class);
+ }
+
+ public function test_notify_own_page_comments()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $prefs = new UserNotificationPreferences($editor);
+ $prefs->updateFromSettingsArray(['own-page-comments' => 'true']);
+
+ $notifications = Notification::fake();
+
+ $this->asAdmin()->post("/comment/{$entities['page']->id}", [
+ 'html' => '<p>My new comment</p>'
+ ]);
+ $notifications->assertSentTo($editor, CommentCreationNotification::class);
+ }
+
+ public function test_notify_comment_replies()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $prefs = new UserNotificationPreferences($editor);
+ $prefs->updateFromSettingsArray(['comment-replies' => 'true']);
+
+ // Create some existing comments to pad IDs to help potentially error
+ // on mis-identification of parent via ids used.
+ Comment::factory()->count(5)
+ ->for($entities['page'], 'entity')
+ ->create(['created_by' => $this->users->admin()->id]);
+
+ $notifications = Notification::fake();
+
+ $this->actingAs($editor)->post("/comment/{$entities['page']->id}", [
+ 'html' => '<p>My new comment</p>'
+ ]);
+ $comment = $entities['page']->comments()->orderBy('id', 'desc')->first();
+
+ $this->asAdmin()->post("/comment/{$entities['page']->id}", [
+ 'html' => '<p>My new comment response</p>',
+ 'parent_id' => $comment->local_id,
+ ]);
+ $notifications->assertSentTo($editor, CommentCreationNotification::class);
+ }
+
+ public function test_notify_watch_parent_book_ignore()
+ {
+ $editor = $this->users->editor();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $prefs = new UserNotificationPreferences($editor);
+ $watches->updateLevelByValue(WatchLevels::IGNORE);
+ $prefs->updateFromSettingsArray(['own-page-changes' => 'true', 'own-page-comments' => true]);
+
+ $notifications = Notification::fake();
+
+ $this->asAdmin()->post("/comment/{$entities['page']->id}", [
+ 'text' => 'My new comment response',
+ ]);
+ $this->entities->updatePage($entities['page'], ['name' => 'My updated page', 'html' => 'Hello']);
+ $notifications->assertNothingSent();
+ }
+
+ public function test_notify_watch_parent_book_comments()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+
+ // Comment post
+ $this->actingAs($admin)->post("/comment/{$entities['page']->id}", [
+ 'html' => '<p>My new comment response</p>',
+ ]);
+
+ $notifications->assertSentTo($editor, function (CommentCreationNotification $notification) use ($editor, $admin, $entities) {
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ return $mail->subject === 'New comment on page: ' . $entities['page']->getShortName()
+ && str_contains($mailContent, 'View Comment')
+ && str_contains($mailContent, 'Page Name: ' . $entities['page']->name)
+ && str_contains($mailContent, 'Page Path: ' . $entities['book']->getShortName(24) . ' > ' . $entities['chapter']->getShortName(24))
+ && str_contains($mailContent, 'Commenter: ' . $admin->name)
+ && str_contains($mailContent, 'Comment: My new comment response');
+ });
+ }
+
+ public function test_notify_watch_parent_book_updates()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::UPDATES);
+
+ $this->actingAs($admin);
+ $this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']);
+
+ $notifications->assertSentTo($editor, function (PageUpdateNotification $notification) use ($editor, $admin, $entities) {
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ return $mail->subject === 'Updated page: Updated page'
+ && str_contains($mailContent, 'View Page')
+ && str_contains($mailContent, 'Page Name: Updated page')
+ && str_contains($mailContent, 'Page Path: ' . $entities['book']->getShortName(24) . ' > ' . $entities['chapter']->getShortName(24))
+ && str_contains($mailContent, 'Updated By: ' . $admin->name)
+ && str_contains($mailContent, 'you won\'t be sent notifications for further edits to this page by the same editor');
+ });
+
+ // Test debounce
+ $notifications = Notification::fake();
+ $this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']);
+ $notifications->assertNothingSentTo($editor);
+ }
+
+ public function test_notify_watch_parent_book_new()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::NEW);
+
+ $this->actingAs($admin)->get($entities['chapter']->getUrl('/create-page'));
+ $page = $entities['chapter']->pages()->where('draft', '=', true)->first();
+ $this->post($page->getUrl(), ['name' => 'My new page', 'html' => 'My new page content']);
+
+ $notifications->assertSentTo($editor, function (PageCreationNotification $notification) use ($editor, $admin, $entities) {
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ return $mail->subject === 'New page: My new page'
+ && str_contains($mailContent, 'View Page')
+ && str_contains($mailContent, 'Page Name: My new page')
+ && str_contains($mailContent, 'Page Path: ' . $entities['book']->getShortName(24) . ' > ' . $entities['chapter']->getShortName(24))
+ && str_contains($mailContent, 'Created By: ' . $admin->name);
+ });
+ }
+
+ public function test_notifications_sent_in_right_language()
+ {
+ $editor = $this->users->editor();
+ $admin = $this->users->admin();
+ setting()->putUser($editor, 'language', 'de');
+ $entities = $this->entities->createChainBelongingToUser($editor);
+ $watches = new UserEntityWatchOptions($editor, $entities['book']);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+
+ $activities = [
+ ActivityType::PAGE_CREATE => $entities['page'],
+ ActivityType::PAGE_UPDATE => $entities['page'],
+ ActivityType::COMMENT_CREATE => Comment::factory()->make([
+ 'entity_id' => $entities['page']->id,
+ 'entity_type' => $entities['page']->getMorphClass(),
+ ]),
+ ];
+
+ $notifications = Notification::fake();
+ $logger = app()->make(ActivityLogger::class);
+ $this->actingAs($admin);
+
+ foreach ($activities as $activityType => $detail) {
+ $logger->add($activityType, $detail);
+ }
+
+ $sent = $notifications->sentNotifications()[get_class($editor)][$editor->id];
+ $this->assertCount(3, $sent);
+
+ foreach ($sent as $notificationInfo) {
+ $notification = $notificationInfo[0]['notification'];
+ $this->assertInstanceOf(BaseActivityNotification::class, $notification);
+ $mail = $notification->toMail($editor);
+ $mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
+ $this->assertStringContainsString('Name der Seite:', $mailContent);
+ $this->assertStringContainsString('Diese Benachrichtigung wurde', $mailContent);
+ $this->assertStringContainsString('Sollte es beim Anklicken der Schaltfläche', $mailContent);
+ }
+ }
+
+ public function test_notifications_not_sent_if_lacking_view_permission_for_related_item()
+ {
+ $notifications = Notification::fake();
+ $editor = $this->users->editor();
+ $page = $this->entities->page();
+
+ $watches = new UserEntityWatchOptions($editor, $page);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->permissions->disableEntityInheritedPermissions($page);
+
+ $this->asAdmin()->post("/comment/{$page->id}", [
+ 'html' => '<p>My new comment response</p>',
+ ])->assertOk();
+
+ $notifications->assertNothingSentTo($editor);
+ }
+
+ public function test_watches_deleted_on_user_delete()
+ {
+ $editor = $this->users->editor();
+ $page = $this->entities->page();
+
+ $watches = new UserEntityWatchOptions($editor, $page);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->assertDatabaseHas('watches', ['user_id' => $editor->id]);
+
+ $this->asAdmin()->delete($editor->getEditUrl());
+
+ $this->assertDatabaseMissing('watches', ['user_id' => $editor->id]);
+ }
+
+ public function test_watches_deleted_on_item_delete()
+ {
+ $editor = $this->users->editor();
+ $page = $this->entities->page();
+
+ $watches = new UserEntityWatchOptions($editor, $page);
+ $watches->updateLevelByValue(WatchLevels::COMMENTS);
+ $this->assertDatabaseHas('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
+
+ $this->entities->destroy($page);
+
+ $this->assertDatabaseMissing('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
+ }
+
+ public function test_page_path_in_notifications_limited_by_permissions()
+ {
+ $chapter = $this->entities->chapterHasPages();
+ $page = $chapter->pages()->first();
+ $book = $chapter->book;
+ $notification = new PageCreationNotification($page, $this->users->editor());
+
+ $viewer = $this->users->viewer();
+ $viewerRole = $viewer->roles()->first();
+
+ $content = html_entity_decode(strip_tags($notification->toMail($viewer)->render()), ENT_QUOTES);
+ $this->assertStringContainsString('Page Path: ' . $book->getShortName(24) . ' > ' . $chapter->getShortName(24), $content);
+
+ $this->permissions->setEntityPermissions($page, ['view'], [$viewerRole]);
+ $this->permissions->setEntityPermissions($chapter, [], [$viewerRole]);
+
+ $content = html_entity_decode(strip_tags($notification->toMail($viewer)->render()), ENT_QUOTES);
+ $this->assertStringContainsString('Page Path: ' . $book->getShortName(24), $content);
+ $this->assertStringNotContainsString(' > ' . $chapter->getShortName(24), $content);
+
+ $this->permissions->setEntityPermissions($book, [], [$viewerRole]);
+
+ $content = html_entity_decode(strip_tags($notification->toMail($viewer)->render()), ENT_QUOTES);
+ $this->assertStringNotContainsString('Page Path:', $content);
+ $this->assertStringNotContainsString($book->getShortName(24), $content);
+ $this->assertStringNotContainsString($chapter->getShortName(24), $content);
+ }