Comments: Added wysiwyg link selector, updated tests, removed command
- Updated existing tests with recent back-end changes, mainly to use HTML data. - Removed old comment regen command that's no longer required.
This commit is contained in:
parent
adf0baebb9
commit
e9a19d5878
7 changed files with 58 additions and 120 deletions
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace BookStack\Console\Commands;
|
||||
|
||||
use BookStack\Activity\CommentRepo;
|
||||
use BookStack\Activity\Models\Comment;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class RegenerateCommentContentCommand extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'bookstack:regenerate-comment-content
|
||||
{--database= : The database connection to use}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Regenerate the stored HTML of all comments';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(CommentRepo $commentRepo): int
|
||||
{
|
||||
$connection = DB::getDefaultConnection();
|
||||
if ($this->option('database') !== null) {
|
||||
DB::setDefaultConnection($this->option('database'));
|
||||
}
|
||||
|
||||
Comment::query()->chunk(100, function ($comments) use ($commentRepo) {
|
||||
foreach ($comments as $comment) {
|
||||
$comment->html = $commentRepo->commentToHtml($comment->text);
|
||||
$comment->save();
|
||||
}
|
||||
});
|
||||
|
||||
DB::setDefaultConnection($connection);
|
||||
$this->comment('Comment HTML content has been regenerated');
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ class CommentFactory extends Factory
|
|||
'html' => $html,
|
||||
'text' => $text,
|
||||
'parent_id' => null,
|
||||
'local_id' => 1,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@
|
|||
<script src="{{ versioned_asset('libs/tinymce/tinymce.min.js') }}" nonce="{{ $cspNonce }}"></script>
|
||||
@include('form.editor-translations')
|
||||
@endpush
|
||||
@push('post-app-html')
|
||||
@include('entities.selector-popup')
|
||||
@endpush
|
||||
@endif
|
||||
|
||||
</section>
|
|
@ -68,7 +68,7 @@
|
|||
</div>
|
||||
|
||||
@yield('bottom')
|
||||
|
||||
@stack('post-app-html')
|
||||
|
||||
@if($cspNonce ?? false)
|
||||
<script src="{{ versioned_asset('dist/app.js') }}" nonce="{{ $cspNonce }}"></script>
|
||||
|
|
|
@ -196,7 +196,7 @@ class WatchTest extends TestCase
|
|||
$notifications = Notification::fake();
|
||||
|
||||
$this->asAdmin()->post("/comment/{$entities['page']->id}", [
|
||||
'text' => 'My new comment'
|
||||
'html' => '<p>My new comment</p>'
|
||||
]);
|
||||
$notifications->assertSentTo($editor, CommentCreationNotification::class);
|
||||
}
|
||||
|
@ -217,12 +217,12 @@ class WatchTest extends TestCase
|
|||
$notifications = Notification::fake();
|
||||
|
||||
$this->actingAs($editor)->post("/comment/{$entities['page']->id}", [
|
||||
'text' => 'My new comment'
|
||||
'html' => '<p>My new comment</p>'
|
||||
]);
|
||||
$comment = $entities['page']->comments()->orderBy('id', 'desc')->first();
|
||||
|
||||
$this->asAdmin()->post("/comment/{$entities['page']->id}", [
|
||||
'text' => 'My new comment response',
|
||||
'html' => '<p>My new comment response</p>',
|
||||
'parent_id' => $comment->local_id,
|
||||
]);
|
||||
$notifications->assertSentTo($editor, CommentCreationNotification::class);
|
||||
|
@ -257,7 +257,7 @@ class WatchTest extends TestCase
|
|||
|
||||
// Comment post
|
||||
$this->actingAs($admin)->post("/comment/{$entities['page']->id}", [
|
||||
'text' => 'My new comment response',
|
||||
'html' => '<p>My new comment response</p>',
|
||||
]);
|
||||
|
||||
$notifications->assertSentTo($editor, function (CommentCreationNotification $notification) use ($editor, $admin, $entities) {
|
||||
|
@ -376,7 +376,7 @@ class WatchTest extends TestCase
|
|||
$this->permissions->disableEntityInheritedPermissions($page);
|
||||
|
||||
$this->asAdmin()->post("/comment/{$page->id}", [
|
||||
'text' => 'My new comment response',
|
||||
'html' => '<p>My new comment response</p>',
|
||||
])->assertOk();
|
||||
|
||||
$notifications->assertNothingSentTo($editor);
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Commands;
|
||||
|
||||
use BookStack\Activity\Models\Comment;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RegenerateCommentContentCommandTest extends TestCase
|
||||
{
|
||||
public function test_regenerate_comment_content_command()
|
||||
{
|
||||
Comment::query()->forceCreate([
|
||||
'html' => 'some_old_content',
|
||||
'text' => 'some_fresh_content',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('comments', [
|
||||
'html' => 'some_old_content',
|
||||
]);
|
||||
|
||||
$exitCode = \Artisan::call('bookstack:regenerate-comment-content');
|
||||
$this->assertTrue($exitCode === 0, 'Command executed successfully');
|
||||
|
||||
$this->assertDatabaseMissing('comments', [
|
||||
'html' => 'some_old_content',
|
||||
]);
|
||||
$this->assertDatabaseHas('comments', [
|
||||
'html' => "<p>some_fresh_content</p>\n",
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ class CommentTest extends TestCase
|
|||
'local_id' => 1,
|
||||
'entity_id' => $page->id,
|
||||
'entity_type' => Page::newModelInstance()->getMorphClass(),
|
||||
'text' => $comment->text,
|
||||
'text' => null,
|
||||
'parent_id' => 2,
|
||||
]);
|
||||
|
||||
|
@ -43,17 +43,17 @@ class CommentTest extends TestCase
|
|||
$this->postJson("/comment/$page->id", $comment->getAttributes());
|
||||
|
||||
$comment = $page->comments()->first();
|
||||
$newText = 'updated text content';
|
||||
$newHtml = '<p>updated text content</p>';
|
||||
$resp = $this->putJson("/comment/$comment->id", [
|
||||
'text' => $newText,
|
||||
'html' => $newHtml,
|
||||
]);
|
||||
|
||||
$resp->assertStatus(200);
|
||||
$resp->assertSee($newText);
|
||||
$resp->assertDontSee($comment->text);
|
||||
$resp->assertSee($newHtml, false);
|
||||
$resp->assertDontSee($comment->html, false);
|
||||
|
||||
$this->assertDatabaseHas('comments', [
|
||||
'text' => $newText,
|
||||
'html' => $newHtml,
|
||||
'entity_id' => $page->id,
|
||||
]);
|
||||
|
||||
|
@ -80,46 +80,28 @@ class CommentTest extends TestCase
|
|||
$this->assertActivityExists(ActivityType::COMMENT_DELETE);
|
||||
}
|
||||
|
||||
public function test_comments_converts_markdown_input_to_html()
|
||||
{
|
||||
$page = $this->entities->page();
|
||||
$this->asAdmin()->postJson("/comment/$page->id", [
|
||||
'text' => '# My Title',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseHas('comments', [
|
||||
'entity_id' => $page->id,
|
||||
'entity_type' => $page->getMorphClass(),
|
||||
'text' => '# My Title',
|
||||
'html' => "<h1>My Title</h1>\n",
|
||||
]);
|
||||
|
||||
$pageView = $this->get($page->getUrl());
|
||||
$pageView->assertSee('<h1>My Title</h1>', false);
|
||||
}
|
||||
|
||||
public function test_html_cannot_be_injected_via_comment_content()
|
||||
public function test_scripts_cannot_be_injected_via_comment_html()
|
||||
{
|
||||
$this->asAdmin();
|
||||
$page = $this->entities->page();
|
||||
|
||||
$script = '<script>const a = "script";</script>\n\n# sometextinthecomment';
|
||||
$script = '<script>const a = "script";</script><p onclick="1">My lovely comment</p>';
|
||||
$this->postJson("/comment/$page->id", [
|
||||
'text' => $script,
|
||||
'html' => $script,
|
||||
]);
|
||||
|
||||
$pageView = $this->get($page->getUrl());
|
||||
$pageView->assertDontSee($script, false);
|
||||
$pageView->assertSee('sometextinthecomment');
|
||||
$pageView->assertSee('<p>My lovely comment</p>', false);
|
||||
|
||||
$comment = $page->comments()->first();
|
||||
$this->putJson("/comment/$comment->id", [
|
||||
'text' => $script . 'updated',
|
||||
'html' => $script . '<p>updated</p>',
|
||||
]);
|
||||
|
||||
$pageView = $this->get($page->getUrl());
|
||||
$pageView->assertDontSee($script, false);
|
||||
$pageView->assertSee('sometextinthecommentupdated');
|
||||
$pageView->assertSee('<p>My lovely comment</p><p>updated</p>');
|
||||
}
|
||||
|
||||
public function test_reply_comments_are_nested()
|
||||
|
@ -127,15 +109,17 @@ class CommentTest extends TestCase
|
|||
$this->asAdmin();
|
||||
$page = $this->entities->page();
|
||||
|
||||
$this->postJson("/comment/$page->id", ['text' => 'My new comment']);
|
||||
$this->postJson("/comment/$page->id", ['text' => 'My new comment']);
|
||||
$this->postJson("/comment/$page->id", ['html' => '<p>My new comment</p>']);
|
||||
$this->postJson("/comment/$page->id", ['html' => '<p>My new comment</p>']);
|
||||
|
||||
$respHtml = $this->withHtml($this->get($page->getUrl()));
|
||||
$respHtml->assertElementCount('.comment-branch', 3);
|
||||
$respHtml->assertElementNotExists('.comment-branch .comment-branch');
|
||||
|
||||
$comment = $page->comments()->first();
|
||||
$resp = $this->postJson("/comment/$page->id", ['text' => 'My nested comment', 'parent_id' => $comment->local_id]);
|
||||
$resp = $this->postJson("/comment/$page->id", [
|
||||
'html' => '<p>My nested comment</p>', 'parent_id' => $comment->local_id
|
||||
]);
|
||||
$resp->assertStatus(200);
|
||||
|
||||
$respHtml = $this->withHtml($this->get($page->getUrl()));
|
||||
|
@ -147,7 +131,7 @@ class CommentTest extends TestCase
|
|||
{
|
||||
$page = $this->entities->page();
|
||||
|
||||
$this->asAdmin()->postJson("/comment/$page->id", ['text' => 'My great comment to see in the editor']);
|
||||
$this->asAdmin()->postJson("/comment/$page->id", ['html' => '<p>My great comment to see in the editor</p>']);
|
||||
|
||||
$respHtml = $this->withHtml($this->get($page->getUrl('/edit')));
|
||||
$respHtml->assertElementContains('.comment-box .content', 'My great comment to see in the editor');
|
||||
|
@ -164,4 +148,34 @@ class CommentTest extends TestCase
|
|||
$pageResp = $this->asAdmin()->get($page->getUrl());
|
||||
$pageResp->assertSee('Wolfeschlegels…');
|
||||
}
|
||||
|
||||
public function test_comment_editor_js_loaded_with_create_or_edit_permissions()
|
||||
{
|
||||
$editor = $this->users->editor();
|
||||
$page = $this->entities->page();
|
||||
|
||||
$resp = $this->actingAs($editor)->get($page->getUrl());
|
||||
$resp->assertSee('tinymce.min.js?', false);
|
||||
$resp->assertSee('window.editor_translations', false);
|
||||
$resp->assertSee('component="entity-selector"', false);
|
||||
|
||||
$this->permissions->removeUserRolePermissions($editor, ['comment-create-all']);
|
||||
$this->permissions->grantUserRolePermissions($editor, ['comment-update-own']);
|
||||
|
||||
$resp = $this->actingAs($editor)->get($page->getUrl());
|
||||
$resp->assertDontSee('tinymce.min.js?', false);
|
||||
$resp->assertDontSee('window.editor_translations', false);
|
||||
$resp->assertDontSee('component="entity-selector"', false);
|
||||
|
||||
Comment::factory()->create([
|
||||
'created_by' => $editor->id,
|
||||
'entity_type' => 'page',
|
||||
'entity_id' => $page->id,
|
||||
]);
|
||||
|
||||
$resp = $this->actingAs($editor)->get($page->getUrl());
|
||||
$resp->assertSee('tinymce.min.js?', false);
|
||||
$resp->assertSee('window.editor_translations', false);
|
||||
$resp->assertSee('component="entity-selector"', false);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue