Skip to content

Commit 5c94ef4

Browse files
Add policy for managing comment authorization (#23)
* Move existing logic to policy * Remove allow edits and deletes config options * Handle guest users in canEdit and canDelete * Clean up README * Fix styles after merge * Remove canEdit and canDelete in favor of policy check * Hide save form when create comment permission is not granted * Throw authorization exception when attempting to create comment without permission * Use resolveAuthenticatedUser * Check any permissions before rendering comment actions area * Use comment model in relationships * Added default value --------- Co-authored-by: Luís Dalmolin <luis.nh@gmail.com>
1 parent d07562f commit 5c94ef4

19 files changed

Lines changed: 375 additions & 220 deletions

README.md

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,14 @@ class Project extends Model implements Commentable
4949

5050
### Usage with Filament
5151

52-
You can register the plugin in your Panel(s) and configure editing and deleting permissions:
52+
You can register the plugin in your Panel(s) like so:
5353

5454
```php
5555
use Kirschbaum\Commentions\CommentionsPlugin;
5656

5757
return $panel
5858
->plugins([
59-
CommentionsPlugin::make()
60-
->disallowEdits() // Prevent users from editing their comments
61-
->disallowDeletes() // Prevent users from deleting their comments
59+
CommentionsPlugin::make(),
6260
])
6361
```
6462

@@ -110,36 +108,63 @@ If your `User` model lives in a different namespace than `App\Models\User`, you
110108
],
111109
```
112110

113-
### Disabling comment editing and deletion
111+
### Configuring the Comment model
114112

115-
By default, users can edit and delete their own comments. You can disable this functionality in two ways:
113+
If you need to customize the Comment model, you can extend the `\Kirschbaum\Commentions\Comment` class and then update the `comment.model` option in your `config/commentions.php` file:
116114

117-
#### 1. Using the plugin configuration
115+
```php
116+
'comment' => [
117+
'model' => \App\Models\Comment::class,
118+
// ...
119+
],
120+
```
121+
122+
### Configuring Comment permissions
123+
124+
By default, users can create comments, as well as edit and delete their own comments. You can adjust these permissions by implementing your own policy:
125+
126+
#### 1) Create a custom policy
118127

119128
```php
120-
use Kirschbaum\Commentions\CommentionsPlugin;
129+
<?php
121130

122-
return $panel
123-
->plugins([
124-
CommentionsPlugin::make()
125-
->disallowEdits() // Prevent users from editing their comments
126-
->disallowDeletes() // Prevent users from deleting their comments
127-
])
131+
namespace App\Policies;
132+
133+
use Kirschbaum\Commentions\Comment;
134+
use Kirschbaum\Commentions\Contracts\Commenter;
135+
use Kirschbaum\Commentions\Policies\CommentPolicy;
136+
137+
class CommentPolicy extends CommentPolicy
138+
{
139+
public function create(Commenter $user): bool
140+
{
141+
// TODO: Implement custom permission logic.
142+
}
143+
144+
public function update($user, Comment $comment): bool
145+
{
146+
// TODO: Implement custom permission logic.
147+
}
148+
149+
public function delete($user, Comment $comment): bool
150+
{
151+
// TODO: Implement custom permission logic.
152+
}
153+
}
128154
```
129155

130-
#### 2. Using the configuration file
156+
#### 2) Register your policy in the configuration file
131157

132-
Set the `allow_edits` and `allow_deletes` options in your `config/commentions.php` file:
158+
Update the `comment.policy` option in your `config/commentions.php` file:
133159

134160
```php
135-
/**
136-
* Comment editing/deleting options.
137-
*/
138-
'allow_edits' => false,
139-
'allow_deletes' => false,
161+
'comment' => [
162+
// ...
163+
'policy' => \App\Policies\CommentPolicy::class,
164+
],
140165
```
141166

142-
> **Note:** The plugin configuration takes precedence over the config file settings.
167+
### Configuring the Commenter name
143168

144169
By default, the `name` property will be used to render the mention names. You can customize it either by implementing the Filament `HasName` interface OR by implementing the optional `getCommenterName` method.
145170

config/commentions.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@
2222

2323
/*
2424
|--------------------------------------------------------------------------
25-
| Comment Moderation
25+
| Comment model configuration
2626
|--------------------------------------------------------------------------
2727
*/
28-
'allow_edits' => true,
29-
'allow_deletes' => true,
28+
'comment' => [
29+
'model' => \Kirschbaum\Commentions\Comment::class,
30+
'policy' => \Kirschbaum\Commentions\Policies\CommentPolicy::class,
31+
],
3032

3133
/*
3234
|--------------------------------------------------------------------------

resources/views/comment.blade.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@use('\Kirschbaum\Commentions\Config')
2+
13
<div class="flex items-start gap-x-4 border p-4 rounded-lg shadow-sm mb-2" id="filament-comment-{{ $comment->getId() }}">
24
@if ($avatar = $comment->getAuthorAvatar())
35
<img
@@ -32,16 +34,18 @@ class="text-xs text-gray-300 ml-1"
3234
@endif
3335
</div>
3436

35-
@if ($comment->isComment() && $comment->canEdit())
37+
@if ($comment->isComment() && Config::resolveAuthenticatedUser()?->canAny(['update', 'delete'], $comment))
3638
<div class="flex gap-x-1">
37-
<x-filament::icon-button
38-
icon="heroicon-s-pencil-square"
39-
wire:click="edit"
40-
size="xs"
41-
color="gray"
42-
/>
39+
@if (Config::resolveAuthenticatedUser()?->can('update', $comment))
40+
<x-filament::icon-button
41+
icon="heroicon-s-pencil-square"
42+
wire:click="edit"
43+
size="xs"
44+
color="gray"
45+
/>
46+
@endif
4347

44-
@if ($comment->canDelete())
48+
@if (Config::resolveAuthenticatedUser()?->can('delete', $comment))
4549
<x-filament::icon-button
4650
icon="heroicon-s-trash"
4751
wire:click="$dispatch('open-modal', { id: 'delete-comment-modal-{{ $comment->getId() }}' })"
@@ -90,7 +94,7 @@ class="text-xs text-gray-300 ml-1"
9094
@endif
9195
</div>
9296

93-
@if ($comment->isComment() && $comment->canDelete())
97+
@if ($comment->isComment() && \Kirschbaum\Commentions\Config::resolveAuthenticatedUser()?->can('delete', $comment))
9498
<x-filament::modal
9599
id="delete-comment-modal-{{ $comment->getId() }}"
96100
wire:model="showDeleteModal"

resources/views/comments.blade.php

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
1+
@use('\Kirschbaum\Commentions\Config')
2+
13
<div class="space-y-2" x-data="{ wasFocused: false }">
2-
<form wire:submit.prevent="save" x-cloak>
3-
{{-- tiptap editor --}}
4-
<div class="relative tip-tap-container mb-2" x-on:click="wasFocused = true" wire:ignore>
5-
<div
6-
x-data="editor(@js($commentBody), @js($this->mentions), 'comments')"
7-
>
8-
<div x-ref="element"></div>
4+
@if (Config::resolveAuthenticatedUser()?->can('create', Config::getCommentModel()))
5+
<form wire:submit.prevent="save" x-cloak>
6+
{{-- tiptap editor --}}
7+
<div class="relative tip-tap-container mb-2" x-on:click="wasFocused = true" wire:ignore>
8+
<div
9+
x-data="editor(@js($commentBody), @js($this->mentions), 'comments')"
10+
>
11+
<div x-ref="element"></div>
12+
</div>
913
</div>
10-
</div>
1114

12-
<template x-if="wasFocused">
13-
<div>
14-
<x-filament::button
15-
wire:click="save"
16-
size="sm"
17-
>Save</x-filament::button>
15+
<template x-if="wasFocused">
16+
<div>
17+
<x-filament::button
18+
wire:click="save"
19+
size="sm"
20+
>Save</x-filament::button>
1821

19-
<x-filament::button
20-
x-on:click="wasFocused = false"
21-
wire:click="clear"
22-
size="sm"
23-
color="gray"
24-
>Cancel</x-filament::button>
25-
</div>
26-
</template>
27-
</form>
22+
<x-filament::button
23+
x-on:click="wasFocused = false"
24+
wire:click="clear"
25+
size="sm"
26+
color="gray"
27+
>Cancel</x-filament::button>
28+
</div>
29+
</template>
30+
</form>
31+
@endif
2832

2933
<livewire:commentions::comment-list
3034
:record="$record"

src/Actions/SaveComment.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,24 @@
22

33
namespace Kirschbaum\Commentions\Actions;
44

5+
use Illuminate\Auth\Access\AuthorizationException;
56
use Illuminate\Database\Eloquent\Model;
67
use Kirschbaum\Commentions\Comment;
8+
use Kirschbaum\Commentions\Config;
79
use Kirschbaum\Commentions\Contracts\Commenter;
810
use Kirschbaum\Commentions\Events\UserWasMentionedEvent;
911

1012
class SaveComment
1113
{
14+
/**
15+
* @throws AuthorizationException
16+
*/
1217
public function __invoke(Model $commentable, Commenter $author, string $body): Comment
1318
{
19+
if ($author->cannot('create', Config::getCommentModel())) {
20+
throw new AuthorizationException('Cannot create comment');
21+
}
22+
1423
$comment = $commentable->comments()->create([
1524
'body' => $body,
1625
'author_id' => $author->getKey(),

src/Comment.php

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,16 +163,6 @@ public function reactions(): HasMany
163163
return $this->hasMany(CommentReaction::class);
164164
}
165165

166-
public function canEdit(): bool
167-
{
168-
return Config::allowEdits() && $this->isAuthor(Config::resolveAuthenticatedUser());
169-
}
170-
171-
public function canDelete(): bool
172-
{
173-
return Config::allowDeletes() && $this->isAuthor(Config::resolveAuthenticatedUser());
174-
}
175-
176166
public function toggleReaction(string $reaction): void
177167
{
178168
ToggleCommentReaction::run($this, $reaction, Config::resolveAuthenticatedUser());

src/CommentReaction.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class CommentReaction extends Model
2323
/** @return BelongsTo<Comment> */
2424
public function comment(): BelongsTo
2525
{
26-
return $this->belongsTo(Comment::class);
26+
return $this->belongsTo(Config::getCommentModel());
2727
}
2828

2929
/** @return MorphTo<Commenter> */

src/CommentionsPlugin.php

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,6 @@ public function register(Panel $panel): void {}
1616

1717
public function boot(Panel $panel): void {}
1818

19-
public function disallowEdits(): static
20-
{
21-
Config::allowEdits(false);
22-
23-
return $this;
24-
}
25-
26-
public function disallowDeletes(): static
27-
{
28-
Config::allowDeletes(false);
29-
30-
return $this;
31-
}
32-
3319
public static function make(): static
3420
{
3521
return app(static::class);

src/CommentionsServiceProvider.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
use Filament\Support\Assets\Css;
66
use Filament\Support\Assets\Js;
77
use Filament\Support\Facades\FilamentAsset;
8+
use Illuminate\Support\Facades\Gate;
9+
use Kirschbaum\Commentions\Comment as CommentModel;
810
use Kirschbaum\Commentions\Livewire\Comment;
911
use Kirschbaum\Commentions\Livewire\CommentList;
1012
use Kirschbaum\Commentions\Livewire\Comments;
@@ -54,5 +56,7 @@ public function packageBooted(): void
5456
],
5557
'kirschbaum-development/' . static::$name
5658
);
59+
60+
Gate::policy(CommentModel::class, config('commentions.comment.policy'));
5761
}
5862
}

src/Config.php

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ class Config
1212

1313
protected static ?Closure $resolveAuthenticatedUser = null;
1414

15-
protected static ?bool $allowEdits = null;
16-
17-
protected static ?bool $allowDeletes = null;
18-
1915
public static function resolveAuthenticatedUserUsing(Closure $callback): void
2016
{
2117
static::$resolveAuthenticatedUser = $callback;
@@ -33,31 +29,14 @@ public static function resolveAuthenticatedUser(): ?Commenter
3329
return $user;
3430
}
3531

36-
public static function getCommenterModel(): string
32+
public static function getCommentModel(): string
3733
{
38-
return config('commentions.commenter.model');
34+
return config('commentions.comment.model', Comment::class);
3935
}
4036

41-
public static function allowEdits(?bool $allow = null): ?bool
42-
{
43-
if (is_bool($allow)) {
44-
static::$allowEdits = $allow;
45-
46-
return null;
47-
}
48-
49-
return static::$allowEdits ?? config('commentions.allow_edits', true);
50-
}
51-
52-
public static function allowDeletes(?bool $allow = null): ?bool
37+
public static function getCommenterModel(): string
5338
{
54-
if (is_bool($allow)) {
55-
static::$allowDeletes = $allow;
56-
57-
return null;
58-
}
59-
60-
return static::$allowDeletes ?? config('commentions.allow_deletes', true);
39+
return config('commentions.commenter.model');
6140
}
6241

6342
public static function getAllowedReactions(): array

0 commit comments

Comments
 (0)