class PageController extends Controller
{
- protected PageRepo $pageRepo;
- protected ReferenceFetcher $referenceFetcher;
-
- /**
- * PageController constructor.
- */
- public function __construct(PageRepo $pageRepo, ReferenceFetcher $referenceFetcher)
- {
- $this->pageRepo = $pageRepo;
- $this->referenceFetcher = $referenceFetcher;
+ public function __construct(
+ protected PageRepo $pageRepo,
+ protected ReferenceFetcher $referenceFetcher
+ ) {
}
/**
namespace BookStack\Entities\Tools;
+use BookStack\Activity\Tools\CommentTree;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Repos\PageRepo;
use BookStack\Entities\Tools\Markdown\HtmlToMarkdown;
class PageEditorData
{
- protected Page $page;
- protected PageRepo $pageRepo;
- protected string $requestedEditor;
-
protected array $viewData;
protected array $warnings;
- public function __construct(Page $page, PageRepo $pageRepo, string $requestedEditor)
- {
- $this->page = $page;
- $this->pageRepo = $pageRepo;
- $this->requestedEditor = $requestedEditor;
-
+ public function __construct(
+ protected Page $page,
+ protected PageRepo $pageRepo,
+ protected string $requestedEditor
+ ) {
$this->viewData = $this->build();
}
'draftsEnabled' => $draftsEnabled,
'templates' => $templates,
'editor' => $editorType,
+ 'comments' => new CommentTree($page),
];
}
'comment_updated_success' => 'Comment updated',
'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
'comment_in_reply_to' => 'In reply to :commentId',
+ 'comment_editor_explain' => 'Here are the comments that have been left on this page. Comments can be added & managed when viewing the saved page.',
// Revision
'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
@include lightDark(background-color, #FFF, #222);
.content {
font-size: 0.666em;
+ padding: $-m $-s;
p, ul, ol {
font-size: $fs-m;
margin: .5em 0;
.comment-box .header {
border-bottom: 1px solid #DDD;
+ padding: $-s;
@include lightDark(border-color, #DDD, #000);
button {
font-size: .8rem;
.text-muted {
color: #999;
}
+ .meta a, .meta span {
+ white-space: nowrap;
+ }
.right-meta .text-muted {
opacity: .8;
}
display: block;
}
+.comment-container-compact .comment-box {
+ .meta {
+ font-size: 0.8rem;
+ }
+ .header {
+ padding: $-xs;
+ }
+ .right-meta {
+ display: none;
+ }
+ .content {
+ padding: $-xs $-s;
+ }
+}
+.comment-container-compact .comment-thread-indicator {
+ width: $-m;
+}
+
#tag-manager .drag-card {
max-width: 500px;
}
-<div component="page-comment"
+<div component="{{ $readOnly ? '' : 'page-comment' }}"
option:page-comment:comment-id="{{ $comment->id }}"
option:page-comment:comment-local-id="{{ $comment->local_id }}"
option:page-comment:comment-parent-id="{{ $comment->parent_id }}"
option:page-comment:deleted-text="{{ trans('entities.comment_deleted_success') }}"
id="comment{{$comment->local_id}}"
class="comment-box">
- <div class="header p-s">
- <div class="flex-container-row justify-space-between wrap">
- <div class="meta text-muted flex-container-row items-center">
+ <div class="header">
+ <div class="flex-container-row wrap items-center gap-x-xs">
+ @if ($comment->createdBy)
+ <div>
+ <img width="50" src="{{ $comment->createdBy->getAvatar(50) }}" class="avatar block mx-xs" alt="{{ $comment->createdBy->name }}">
+ </div>
+ @endif
+ <div class="meta text-muted flex-container-row wrap items-center flex">
@if ($comment->createdBy)
- <img width="50" src="{{ $comment->createdBy->getAvatar(50) }}" class="avatar mx-xs" alt="{{ $comment->createdBy->name }}">
-
<a href="{{ $comment->createdBy->getProfileUrl() }}">{{ $comment->createdBy->getShortName(16) }}</a>
@else
{{ trans('common.deleted_user') }}
@endif
</div>
<div class="right-meta flex-container-row justify-flex-end items-center px-s">
+ @if(!$readOnly && (userCan('comment-create-all') || userCan('comment-update', $comment) || userCan('comment-delete', $comment)))
<div class="actions mr-s">
@if(userCan('comment-create-all'))
<button refs="page-comment@reply-button" type="button" class="text-button text-muted hover-underline p-xs">@icon('reply') {{ trans('common.reply') }}</button>
•
</span>
</div>
+ @endif
<div>
<a class="bold text-muted" href="#comment{{$comment->local_id}}">#{{$comment->local_id}}</a>
</div>
</div>
- <div refs="page-comment@content-container" class="content px-m py-s">
+ <div refs="page-comment@content-container" class="content">
@if ($comment->parent_id)
<p class="comment-reply mb-xxs">
<a class="text-muted text-small" href="#comment{{ $comment->parent_id }}">@icon('reply'){{ trans('entities.comment_in_reply_to', ['commentId' => '#' . $comment->parent_id]) }}</a>
{!! $comment->html !!}
</div>
- @if(userCan('comment-update', $comment))
+ @if(!$readOnly && userCan('comment-update', $comment))
<form novalidate refs="page-comment@form" hidden class="content pt-s px-s block">
<div class="form-group description-input">
<textarea refs="page-comment@input" name="markdown" rows="3" placeholder="{{ trans('entities.comment_placeholder') }}">{{ $comment->text }}</textarea>
<div refs="page-comments@commentContainer" class="comment-container">
@foreach($commentTree->get() as $branch)
- @include('comments.comment-branch', ['branch' => $branch])
+ @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => false])
@endforeach
</div>
<button type="button" refs="editor-toolbox@tab-button" data-tab="files" title="{{ trans('entities.attachments') }}">@icon('attach')</button>
@endif
<button type="button" refs="editor-toolbox@tab-button" data-tab="templates" title="{{ trans('entities.templates') }}">@icon('template')</button>
+ @if($comments->enabled())
+ <button type="button" refs="editor-toolbox@tab-button" data-tab="comments" title="{{ trans('entities.comments') }}">@icon('comment')</button>
+ @endif
</div>
<div refs="editor-toolbox@tab-content" data-tab-content="tags" class="toolbox-tab-content">
<div class="px-l">
@include('pages.parts.template-manager', ['page' => $page, 'templates' => $templates])
</div>
-
</div>
+ @if($comments->enabled())
+ @include('pages.parts.toolbox-comments')
+ @endif
+
</div>
--- /dev/null
+<div refs="editor-toolbox@tab-content" data-tab-content="comments" class="toolbox-tab-content">
+ <h4>{{ trans('entities.comments') }}</h4>
+
+ <div class="comment-container-compact px-l">
+ <p class="text-muted small mb-m">
+ {{ trans('entities.comment_editor_explain') }}
+ </p>
+ @foreach($comments->get() as $branch)
+ @include('comments.comment-branch', ['branch' => $branch, 'readOnly' => true])
+ @endforeach
+ @if($comments->empty())
+ <p class="italic text-muted">{{ trans('common.no_items') }}</p>
+ @endif
+ </div>
+</div>
\ No newline at end of file
$respHtml->assertElementCount('.comment-branch', 4);
$respHtml->assertElementContains('.comment-branch .comment-branch', 'My nested comment');
}
+
+ public function test_comments_are_visible_in_the_page_editor()
+ {
+ $page = $this->entities->page();
+
+ $this->asAdmin()->postJson("/comment/$page->id", ['text' => 'My great comment to see in the editor']);
+
+ $respHtml = $this->withHtml($this->get($page->getUrl('/edit')));
+ $respHtml->assertElementContains('.comment-box .content', 'My great comment to see in the editor');
+ }
}