Skip to content

Commit 082dbc9

Browse files
committed
API: Started building comments API endpoints
1 parent abe9c1e commit 082dbc9

File tree

6 files changed

+112
-10
lines changed

6 files changed

+112
-10
lines changed

app/Activity/CommentRepo.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
use BookStack\Activity\Models\Comment;
66
use BookStack\Entities\Models\Entity;
77
use BookStack\Exceptions\NotifyException;
8-
use BookStack\Exceptions\PrettyException;
98
use BookStack\Facades\Activity as ActivityService;
109
use BookStack\Util\HtmlDescriptionFilter;
10+
use Illuminate\Database\Eloquent\Builder;
1111

1212
class CommentRepo
1313
{
@@ -19,6 +19,14 @@ public function getById(int $id): Comment
1919
return Comment::query()->findOrFail($id);
2020
}
2121

22+
/**
23+
* Start a query for comments visible to the user.
24+
*/
25+
public function getQueryForVisible(): Builder
26+
{
27+
return Comment::query()->scopes('visible');
28+
}
29+
2230
/**
2331
* Create a new comment on an entity.
2432
*/
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BookStack\Activity\Controllers;
6+
7+
use BookStack\Activity\CommentRepo;
8+
use BookStack\Http\ApiController;
9+
use Illuminate\Http\JsonResponse;
10+
11+
class CommentApiController extends ApiController
12+
{
13+
// TODO - Add tree-style comment listing to page-show responses.
14+
// TODO - list
15+
// TODO - create
16+
// TODO - read
17+
// TODO - update
18+
// TODO - delete
19+
20+
// TODO - Test visibility controls
21+
// TODO - Test permissions of each action
22+
23+
// TODO - Support intro block for API docs so we can explain the
24+
// properties for comments in a shared kind of way?
25+
26+
public function __construct(
27+
protected CommentRepo $commentRepo,
28+
) {
29+
}
30+
31+
32+
/**
33+
* Get a listing of comments visible to the user.
34+
*/
35+
public function list(): JsonResponse
36+
{
37+
$query = $this->commentRepo->getQueryForVisible();
38+
39+
return $this->apiListingResponse($query, [
40+
'id', 'commentable_id', 'commentable_type', 'parent_id', 'local_id', 'content_ref', 'created_by', 'updated_by', 'created_at', 'updated_at'
41+
]);
42+
}
43+
}

app/Activity/Models/Comment.php

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
namespace BookStack\Activity\Models;
44

55
use BookStack\App\Model;
6+
use BookStack\Permissions\Models\JointPermission;
7+
use BookStack\Permissions\PermissionApplicator;
68
use BookStack\Users\Models\HasCreatorAndUpdater;
79
use BookStack\Users\Models\OwnableInterface;
8-
use BookStack\Users\Models\User;
910
use BookStack\Util\HtmlContentFilter;
11+
use Illuminate\Database\Eloquent\Builder;
1012
use Illuminate\Database\Eloquent\Factories\HasFactory;
1113
use Illuminate\Database\Eloquent\Relations\BelongsTo;
14+
use Illuminate\Database\Eloquent\Relations\HasMany;
1215
use Illuminate\Database\Eloquent\Relations\MorphTo;
1316

1417
/**
@@ -17,8 +20,8 @@
1720
* @property string $html
1821
* @property int|null $parent_id - Relates to local_id, not id
1922
* @property int $local_id
20-
* @property string $entity_type
21-
* @property int $entity_id
23+
* @property string $commentable_type
24+
* @property int $commentable_id
2225
* @property string $content_ref
2326
* @property bool $archived
2427
*/
@@ -44,8 +47,8 @@ public function entity(): MorphTo
4447
public function parent(): BelongsTo
4548
{
4649
return $this->belongsTo(Comment::class, 'parent_id', 'local_id', 'parent')
47-
->where('entity_type', '=', $this->entity_type)
48-
->where('entity_id', '=', $this->entity_id);
50+
->where('commentable_type', '=', $this->commentable_type)
51+
->where('commentable_id', '=', $this->commentable_id);
4952
}
5053

5154
/**
@@ -58,11 +61,27 @@ public function isUpdated(): bool
5861

5962
public function logDescriptor(): string
6063
{
61-
return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->entity_type} (ID: {$this->entity_id})";
64+
return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->commentable_type} (ID: {$this->commentable_id})";
6265
}
6366

6467
public function safeHtml(): string
6568
{
6669
return HtmlContentFilter::removeScriptsFromHtmlString($this->html ?? '');
6770
}
71+
72+
public function jointPermissions(): HasMany
73+
{
74+
return $this->hasMany(JointPermission::class, 'entity_id', 'commentable_id')
75+
->whereColumn('joint_permissions.entity_type', '=', 'comments.commentable_type');
76+
}
77+
78+
/**
79+
* Scope the query to just the comments visible to the user based upon the
80+
* user visibility of what has been commented on.
81+
*/
82+
public function scopeVisible(Builder $query): Builder
83+
{
84+
return app()->make(PermissionApplicator::class)
85+
->restrictEntityRelationQuery($query, 'comments', 'commentable_id', 'commentable_type');
86+
}
6887
}

app/Entities/Models/Entity.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ public function tags(): MorphMany
240240
*/
241241
public function comments(bool $orderByCreated = true): MorphMany
242242
{
243-
$query = $this->morphMany(Comment::class, 'entity');
243+
$query = $this->morphMany(Comment::class, 'commentable');
244244

245245
return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
246246
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::table('comments', function (Blueprint $table) {
15+
$table->renameColumn('entity_id', 'commentable_id');
16+
$table->renameColumn('entity_type', 'commentable_type');
17+
});
18+
}
19+
20+
/**
21+
* Reverse the migrations.
22+
*/
23+
public function down(): void
24+
{
25+
Schema::table('comments', function (Blueprint $table) {
26+
$table->renameColumn('commentable_id', 'entity_id');
27+
$table->renameColumn('commentable_type', 'entity_type');
28+
});
29+
}
30+
};

routes/api.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Controllers all end with "ApiController"
77
*/
88

9-
use BookStack\Activity\Controllers\AuditLogApiController;
9+
use BookStack\Activity\Controllers as ActivityControllers;
1010
use BookStack\Api\ApiDocsController;
1111
use BookStack\App\SystemApiController;
1212
use BookStack\Entities\Controllers as EntityControllers;
@@ -70,6 +70,8 @@
7070

7171
Route::get('search', [SearchApiController::class, 'all']);
7272

73+
Route::get('comments', [ActivityControllers\CommentApiController::class, 'list']);
74+
7375
Route::get('shelves', [EntityControllers\BookshelfApiController::class, 'list']);
7476
Route::post('shelves', [EntityControllers\BookshelfApiController::class, 'create']);
7577
Route::get('shelves/{id}', [EntityControllers\BookshelfApiController::class, 'read']);
@@ -101,6 +103,6 @@
101103
Route::get('content-permissions/{contentType}/{contentId}', [ContentPermissionApiController::class, 'read']);
102104
Route::put('content-permissions/{contentType}/{contentId}', [ContentPermissionApiController::class, 'update']);
103105

104-
Route::get('audit-log', [AuditLogApiController::class, 'list']);
106+
Route::get('audit-log', [ActivityControllers\AuditLogApiController::class, 'list']);
105107

106108
Route::get('system', [SystemApiController::class, 'read']);

0 commit comments

Comments
 (0)