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
{
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.
*/
--- /dev/null
+<?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'
+ ]);
+ }
+}
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;
/**
* @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
*/
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);
}
/**
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');
+ }
}
*/
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;
}
--- /dev/null
+<?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');
+ });
+ }
+};
* 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;
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']);
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']);