1
0
mirror of https://github.com/BookStackApp/BookStack.git synced 2025-10-25 06:37:36 +03:00

API: Started building comments API endpoints

This commit is contained in:
Dan Brown
2025-10-22 14:58:29 +01:00
parent abe9c1e5a3
commit 082dbc9944
6 changed files with 112 additions and 10 deletions

View File

@@ -5,9 +5,9 @@ namespace BookStack\Activity;
use BookStack\Activity\Models\Comment;
use BookStack\Entities\Models\Entity;
use BookStack\Exceptions\NotifyException;
use BookStack\Exceptions\PrettyException;
use BookStack\Facades\Activity as ActivityService;
use BookStack\Util\HtmlDescriptionFilter;
use Illuminate\Database\Eloquent\Builder;
class CommentRepo
{
@@ -19,6 +19,14 @@ class CommentRepo
return Comment::query()->findOrFail($id);
}
/**
* Start a query for comments visible to the user.
*/
public function getQueryForVisible(): Builder
{
return Comment::query()->scopes('visible');
}
/**
* Create a new comment on an entity.
*/

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace BookStack\Activity\Controllers;
use BookStack\Activity\CommentRepo;
use BookStack\Http\ApiController;
use Illuminate\Http\JsonResponse;
class CommentApiController extends ApiController
{
// TODO - Add tree-style comment listing to page-show responses.
// TODO - list
// TODO - create
// TODO - read
// TODO - update
// TODO - delete
// TODO - Test visibility controls
// TODO - Test permissions of each action
// TODO - Support intro block for API docs so we can explain the
// properties for comments in a shared kind of way?
public function __construct(
protected CommentRepo $commentRepo,
) {
}
/**
* Get a listing of comments visible to the user.
*/
public function list(): JsonResponse
{
$query = $this->commentRepo->getQueryForVisible();
return $this->apiListingResponse($query, [
'id', 'commentable_id', 'commentable_type', 'parent_id', 'local_id', 'content_ref', 'created_by', 'updated_by', 'created_at', 'updated_at'
]);
}
}

View File

@@ -3,12 +3,15 @@
namespace BookStack\Activity\Models;
use BookStack\App\Model;
use BookStack\Permissions\Models\JointPermission;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Users\Models\HasCreatorAndUpdater;
use BookStack\Users\Models\OwnableInterface;
use BookStack\Users\Models\User;
use BookStack\Util\HtmlContentFilter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
@@ -17,8 +20,8 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
* @property string $html
* @property int|null $parent_id - Relates to local_id, not id
* @property int $local_id
* @property string $entity_type
* @property int $entity_id
* @property string $commentable_type
* @property int $commentable_id
* @property string $content_ref
* @property bool $archived
*/
@@ -44,8 +47,8 @@ class Comment extends Model implements Loggable, OwnableInterface
public function parent(): BelongsTo
{
return $this->belongsTo(Comment::class, 'parent_id', 'local_id', 'parent')
->where('entity_type', '=', $this->entity_type)
->where('entity_id', '=', $this->entity_id);
->where('commentable_type', '=', $this->commentable_type)
->where('commentable_id', '=', $this->commentable_id);
}
/**
@@ -58,11 +61,27 @@ class Comment extends Model implements Loggable, OwnableInterface
public function logDescriptor(): string
{
return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->entity_type} (ID: {$this->entity_id})";
return "Comment #{$this->local_id} (ID: {$this->id}) for {$this->commentable_type} (ID: {$this->commentable_id})";
}
public function safeHtml(): string
{
return HtmlContentFilter::removeScriptsFromHtmlString($this->html ?? '');
}
public function jointPermissions(): HasMany
{
return $this->hasMany(JointPermission::class, 'entity_id', 'commentable_id')
->whereColumn('joint_permissions.entity_type', '=', 'comments.commentable_type');
}
/**
* Scope the query to just the comments visible to the user based upon the
* user visibility of what has been commented on.
*/
public function scopeVisible(Builder $query): Builder
{
return app()->make(PermissionApplicator::class)
->restrictEntityRelationQuery($query, 'comments', 'commentable_id', 'commentable_type');
}
}

View File

@@ -240,7 +240,7 @@ abstract class Entity extends Model implements
*/
public function comments(bool $orderByCreated = true): MorphMany
{
$query = $this->morphMany(Comment::class, 'entity');
$query = $this->morphMany(Comment::class, 'commentable');
return $orderByCreated ? $query->orderBy('created_at', 'asc') : $query;
}

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('comments', function (Blueprint $table) {
$table->renameColumn('entity_id', 'commentable_id');
$table->renameColumn('entity_type', 'commentable_type');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('comments', function (Blueprint $table) {
$table->renameColumn('commentable_id', 'entity_id');
$table->renameColumn('commentable_type', 'entity_type');
});
}
};

View File

@@ -6,7 +6,7 @@
* Controllers all end with "ApiController"
*/
use BookStack\Activity\Controllers\AuditLogApiController;
use BookStack\Activity\Controllers as ActivityControllers;
use BookStack\Api\ApiDocsController;
use BookStack\App\SystemApiController;
use BookStack\Entities\Controllers as EntityControllers;
@@ -70,6 +70,8 @@ Route::delete('image-gallery/{id}', [ImageGalleryApiController::class, 'delete']
Route::get('search', [SearchApiController::class, 'all']);
Route::get('comments', [ActivityControllers\CommentApiController::class, 'list']);
Route::get('shelves', [EntityControllers\BookshelfApiController::class, 'list']);
Route::post('shelves', [EntityControllers\BookshelfApiController::class, 'create']);
Route::get('shelves/{id}', [EntityControllers\BookshelfApiController::class, 'read']);
@@ -101,6 +103,6 @@ Route::delete('recycle-bin/{deletionId}', [EntityControllers\RecycleBinApiContro
Route::get('content-permissions/{contentType}/{contentId}', [ContentPermissionApiController::class, 'read']);
Route::put('content-permissions/{contentType}/{contentId}', [ContentPermissionApiController::class, 'update']);
Route::get('audit-log', [AuditLogApiController::class, 'list']);
Route::get('audit-log', [ActivityControllers\AuditLogApiController::class, 'list']);
Route::get('system', [SystemApiController::class, 'read']);