mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-23 11:22:33 +01:00
Merge branch 'Man-in-Black-patch-1' into development
This commit is contained in:
commit
76417efd6f
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Activity\Notifications\MessageParts;
|
||||||
|
|
||||||
|
use BookStack\Entities\Models\Entity;
|
||||||
|
use Illuminate\Contracts\Support\Htmlable;
|
||||||
|
use Stringable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A link to a specific entity in the system, with the text showing its name.
|
||||||
|
*/
|
||||||
|
class EntityLinkMessageLine implements Htmlable, Stringable
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
protected Entity $entity,
|
||||||
|
protected int $nameLength = 120,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toHtml(): string
|
||||||
|
{
|
||||||
|
return '<a href="' . e($this->entity->getUrl()) . '">' . e($this->entity->getShortName($this->nameLength)) . '</a>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return "{$this->entity->getShortName($this->nameLength)} ({$this->entity->getUrl()})";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Activity\Notifications\MessageParts;
|
||||||
|
|
||||||
|
use BookStack\Entities\Models\Entity;
|
||||||
|
use Illuminate\Contracts\Support\Htmlable;
|
||||||
|
use Stringable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A link to a specific entity in the system, with the text showing its name.
|
||||||
|
*/
|
||||||
|
class EntityPathMessageLine implements Htmlable, Stringable
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var EntityLinkMessageLine[]
|
||||||
|
*/
|
||||||
|
protected array $entityLinks;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
protected array $entities
|
||||||
|
) {
|
||||||
|
$this->entityLinks = array_map(fn (Entity $entity) => new EntityLinkMessageLine($entity, 24), $this->entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toHtml(): string
|
||||||
|
{
|
||||||
|
$entityHtmls = array_map(fn (EntityLinkMessageLine $line) => $line->toHtml(), $this->entityLinks);
|
||||||
|
return implode(' > ', $entityHtmls);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return implode(' > ', $this->entityLinks);
|
||||||
|
}
|
||||||
|
}
|
@ -3,8 +3,12 @@
|
|||||||
namespace BookStack\Activity\Notifications\Messages;
|
namespace BookStack\Activity\Notifications\Messages;
|
||||||
|
|
||||||
use BookStack\Activity\Models\Loggable;
|
use BookStack\Activity\Models\Loggable;
|
||||||
|
use BookStack\Activity\Notifications\MessageParts\EntityPathMessageLine;
|
||||||
use BookStack\Activity\Notifications\MessageParts\LinkedMailMessageLine;
|
use BookStack\Activity\Notifications\MessageParts\LinkedMailMessageLine;
|
||||||
use BookStack\App\MailNotification;
|
use BookStack\App\MailNotification;
|
||||||
|
use BookStack\Entities\Models\Entity;
|
||||||
|
use BookStack\Entities\Models\Page;
|
||||||
|
use BookStack\Permissions\PermissionApplicator;
|
||||||
use BookStack\Translation\LocaleDefinition;
|
use BookStack\Translation\LocaleDefinition;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
@ -44,4 +48,20 @@ abstract class BaseActivityNotification extends MailNotification
|
|||||||
$locale->trans('notifications.footer_reason_link'),
|
$locale->trans('notifications.footer_reason_link'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a line which provides the book > chapter path to a page.
|
||||||
|
* Takes into account visibility of these parent items.
|
||||||
|
* Returns null if no path items can be used.
|
||||||
|
*/
|
||||||
|
protected function buildPagePathLine(Page $page, User $notifiable): ?EntityPathMessageLine
|
||||||
|
{
|
||||||
|
$permissions = new PermissionApplicator($notifiable);
|
||||||
|
|
||||||
|
$path = array_filter([$page->book, $page->chapter], function (?Entity $entity) use ($permissions) {
|
||||||
|
return !is_null($entity) && $permissions->checkOwnableUserAccess($entity, 'view');
|
||||||
|
});
|
||||||
|
|
||||||
|
return empty($path) ? null : new EntityPathMessageLine($path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace BookStack\Activity\Notifications\Messages;
|
namespace BookStack\Activity\Notifications\Messages;
|
||||||
|
|
||||||
use BookStack\Activity\Models\Comment;
|
use BookStack\Activity\Models\Comment;
|
||||||
|
use BookStack\Activity\Notifications\MessageParts\EntityLinkMessageLine;
|
||||||
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
|
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
@ -19,14 +20,17 @@ class CommentCreationNotification extends BaseActivityNotification
|
|||||||
|
|
||||||
$locale = $notifiable->getLocale();
|
$locale = $notifiable->getLocale();
|
||||||
|
|
||||||
|
$listLines = array_filter([
|
||||||
|
$locale->trans('notifications.detail_page_name') => new EntityLinkMessageLine($page),
|
||||||
|
$locale->trans('notifications.detail_page_path') => $this->buildPagePathLine($page, $notifiable),
|
||||||
|
$locale->trans('notifications.detail_commenter') => $this->user->name,
|
||||||
|
$locale->trans('notifications.detail_comment') => strip_tags($comment->html),
|
||||||
|
]);
|
||||||
|
|
||||||
return $this->newMailMessage($locale)
|
return $this->newMailMessage($locale)
|
||||||
->subject($locale->trans('notifications.new_comment_subject', ['pageName' => $page->getShortName()]))
|
->subject($locale->trans('notifications.new_comment_subject', ['pageName' => $page->getShortName()]))
|
||||||
->line($locale->trans('notifications.new_comment_intro', ['appName' => setting('app-name')]))
|
->line($locale->trans('notifications.new_comment_intro', ['appName' => setting('app-name')]))
|
||||||
->line(new ListMessageLine([
|
->line(new ListMessageLine($listLines))
|
||||||
$locale->trans('notifications.detail_page_name') => $page->name,
|
|
||||||
$locale->trans('notifications.detail_commenter') => $this->user->name,
|
|
||||||
$locale->trans('notifications.detail_comment') => strip_tags($comment->html),
|
|
||||||
]))
|
|
||||||
->action($locale->trans('notifications.action_view_comment'), $page->getUrl('#comment' . $comment->local_id))
|
->action($locale->trans('notifications.action_view_comment'), $page->getUrl('#comment' . $comment->local_id))
|
||||||
->line($this->buildReasonFooterLine($locale));
|
->line($this->buildReasonFooterLine($locale));
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Activity\Notifications\Messages;
|
namespace BookStack\Activity\Notifications\Messages;
|
||||||
|
|
||||||
|
use BookStack\Activity\Notifications\MessageParts\EntityLinkMessageLine;
|
||||||
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
|
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
@ -16,13 +17,16 @@ class PageCreationNotification extends BaseActivityNotification
|
|||||||
|
|
||||||
$locale = $notifiable->getLocale();
|
$locale = $notifiable->getLocale();
|
||||||
|
|
||||||
|
$listLines = array_filter([
|
||||||
|
$locale->trans('notifications.detail_page_name') => new EntityLinkMessageLine($page),
|
||||||
|
$locale->trans('notifications.detail_page_path') => $this->buildPagePathLine($page, $notifiable),
|
||||||
|
$locale->trans('notifications.detail_created_by') => $this->user->name,
|
||||||
|
]);
|
||||||
|
|
||||||
return $this->newMailMessage($locale)
|
return $this->newMailMessage($locale)
|
||||||
->subject($locale->trans('notifications.new_page_subject', ['pageName' => $page->getShortName()]))
|
->subject($locale->trans('notifications.new_page_subject', ['pageName' => $page->getShortName()]))
|
||||||
->line($locale->trans('notifications.new_page_intro', ['appName' => setting('app-name')], $locale))
|
->line($locale->trans('notifications.new_page_intro', ['appName' => setting('app-name')]))
|
||||||
->line(new ListMessageLine([
|
->line(new ListMessageLine($listLines))
|
||||||
$locale->trans('notifications.detail_page_name') => $page->name,
|
|
||||||
$locale->trans('notifications.detail_created_by') => $this->user->name,
|
|
||||||
]))
|
|
||||||
->action($locale->trans('notifications.action_view_page'), $page->getUrl())
|
->action($locale->trans('notifications.action_view_page'), $page->getUrl())
|
||||||
->line($this->buildReasonFooterLine($locale));
|
->line($this->buildReasonFooterLine($locale));
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace BookStack\Activity\Notifications\Messages;
|
namespace BookStack\Activity\Notifications\Messages;
|
||||||
|
|
||||||
|
use BookStack\Activity\Notifications\MessageParts\EntityLinkMessageLine;
|
||||||
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
|
use BookStack\Activity\Notifications\MessageParts\ListMessageLine;
|
||||||
use BookStack\Entities\Models\Page;
|
use BookStack\Entities\Models\Page;
|
||||||
use BookStack\Users\Models\User;
|
use BookStack\Users\Models\User;
|
||||||
@ -16,13 +17,16 @@ class PageUpdateNotification extends BaseActivityNotification
|
|||||||
|
|
||||||
$locale = $notifiable->getLocale();
|
$locale = $notifiable->getLocale();
|
||||||
|
|
||||||
|
$listLines = array_filter([
|
||||||
|
$locale->trans('notifications.detail_page_name') => new EntityLinkMessageLine($page),
|
||||||
|
$locale->trans('notifications.detail_page_path') => $this->buildPagePathLine($page, $notifiable),
|
||||||
|
$locale->trans('notifications.detail_updated_by') => $this->user->name,
|
||||||
|
]);
|
||||||
|
|
||||||
return $this->newMailMessage($locale)
|
return $this->newMailMessage($locale)
|
||||||
->subject($locale->trans('notifications.updated_page_subject', ['pageName' => $page->getShortName()]))
|
->subject($locale->trans('notifications.updated_page_subject', ['pageName' => $page->getShortName()]))
|
||||||
->line($locale->trans('notifications.updated_page_intro', ['appName' => setting('app-name')]))
|
->line($locale->trans('notifications.updated_page_intro', ['appName' => setting('app-name')]))
|
||||||
->line(new ListMessageLine([
|
->line(new ListMessageLine($listLines))
|
||||||
$locale->trans('notifications.detail_page_name') => $page->name,
|
|
||||||
$locale->trans('notifications.detail_updated_by') => $this->user->name,
|
|
||||||
]))
|
|
||||||
->line($locale->trans('notifications.updated_page_debounce'))
|
->line($locale->trans('notifications.updated_page_debounce'))
|
||||||
->action($locale->trans('notifications.action_view_page'), $page->getUrl())
|
->action($locale->trans('notifications.action_view_page'), $page->getUrl())
|
||||||
->line($this->buildReasonFooterLine($locale));
|
->line($this->buildReasonFooterLine($locale));
|
||||||
|
@ -13,6 +13,7 @@ return [
|
|||||||
'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.',
|
'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.',
|
||||||
|
|
||||||
'detail_page_name' => 'Page Name:',
|
'detail_page_name' => 'Page Name:',
|
||||||
|
'detail_page_path' => 'Page Path:',
|
||||||
'detail_commenter' => 'Commenter:',
|
'detail_commenter' => 'Commenter:',
|
||||||
'detail_comment' => 'Comment:',
|
'detail_comment' => 'Comment:',
|
||||||
'detail_created_by' => 'Created By:',
|
'detail_created_by' => 'Created By:',
|
||||||
|
@ -12,7 +12,6 @@ use BookStack\Activity\Tools\ActivityLogger;
|
|||||||
use BookStack\Activity\Tools\UserEntityWatchOptions;
|
use BookStack\Activity\Tools\UserEntityWatchOptions;
|
||||||
use BookStack\Activity\WatchLevels;
|
use BookStack\Activity\WatchLevels;
|
||||||
use BookStack\Entities\Models\Entity;
|
use BookStack\Entities\Models\Entity;
|
||||||
use BookStack\Entities\Tools\TrashCan;
|
|
||||||
use BookStack\Settings\UserNotificationPreferences;
|
use BookStack\Settings\UserNotificationPreferences;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
@ -268,6 +267,7 @@ class WatchTest extends TestCase
|
|||||||
return $mail->subject === 'New comment on page: ' . $entities['page']->getShortName()
|
return $mail->subject === 'New comment on page: ' . $entities['page']->getShortName()
|
||||||
&& str_contains($mailContent, 'View Comment')
|
&& str_contains($mailContent, 'View Comment')
|
||||||
&& str_contains($mailContent, 'Page Name: ' . $entities['page']->name)
|
&& 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, 'Commenter: ' . $admin->name)
|
||||||
&& str_contains($mailContent, 'Comment: My new comment response');
|
&& str_contains($mailContent, 'Comment: My new comment response');
|
||||||
});
|
});
|
||||||
@ -285,12 +285,13 @@ class WatchTest extends TestCase
|
|||||||
$this->actingAs($admin);
|
$this->actingAs($admin);
|
||||||
$this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']);
|
$this->entities->updatePage($entities['page'], ['name' => 'Updated page', 'html' => 'new page content']);
|
||||||
|
|
||||||
$notifications->assertSentTo($editor, function (PageUpdateNotification $notification) use ($editor, $admin) {
|
$notifications->assertSentTo($editor, function (PageUpdateNotification $notification) use ($editor, $admin, $entities) {
|
||||||
$mail = $notification->toMail($editor);
|
$mail = $notification->toMail($editor);
|
||||||
$mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
|
$mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
|
||||||
return $mail->subject === 'Updated page: Updated page'
|
return $mail->subject === 'Updated page: Updated page'
|
||||||
&& str_contains($mailContent, 'View Page')
|
&& str_contains($mailContent, 'View Page')
|
||||||
&& str_contains($mailContent, 'Page Name: Updated 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, 'Updated By: ' . $admin->name)
|
||||||
&& str_contains($mailContent, 'you won\'t be sent notifications for further edits to this page by the same editor');
|
&& str_contains($mailContent, 'you won\'t be sent notifications for further edits to this page by the same editor');
|
||||||
});
|
});
|
||||||
@ -314,12 +315,13 @@ class WatchTest extends TestCase
|
|||||||
$page = $entities['chapter']->pages()->where('draft', '=', true)->first();
|
$page = $entities['chapter']->pages()->where('draft', '=', true)->first();
|
||||||
$this->post($page->getUrl(), ['name' => 'My new page', 'html' => 'My new page content']);
|
$this->post($page->getUrl(), ['name' => 'My new page', 'html' => 'My new page content']);
|
||||||
|
|
||||||
$notifications->assertSentTo($editor, function (PageCreationNotification $notification) use ($editor, $admin) {
|
$notifications->assertSentTo($editor, function (PageCreationNotification $notification) use ($editor, $admin, $entities) {
|
||||||
$mail = $notification->toMail($editor);
|
$mail = $notification->toMail($editor);
|
||||||
$mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
|
$mailContent = html_entity_decode(strip_tags($mail->render()), ENT_QUOTES);
|
||||||
return $mail->subject === 'New page: My new page'
|
return $mail->subject === 'New page: My new page'
|
||||||
&& str_contains($mailContent, 'View Page')
|
&& str_contains($mailContent, 'View Page')
|
||||||
&& str_contains($mailContent, 'Page Name: My new 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);
|
&& str_contains($mailContent, 'Created By: ' . $admin->name);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -408,4 +410,32 @@ class WatchTest extends TestCase
|
|||||||
|
|
||||||
$this->assertDatabaseMissing('watches', ['watchable_type' => 'page', 'watchable_id' => $page->id]);
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user