use BookStack\App\Model;
use BookStack\Entities\Models\Entity;
use BookStack\Entities\Queries\TopFavourites;
+use BookStack\Entities\Tools\MixedEntityRequestHelper;
use BookStack\Http\Controller;
use Illuminate\Http\Request;
class FavouriteController extends Controller
{
+ public function __construct(
+ protected MixedEntityRequestHelper $entityHelper,
+ ) {
+ }
+
/**
* Show a listing of all favourite items for the current user.
*/
*/
public function add(Request $request)
{
- $favouritable = $this->getValidatedModelFromRequest($request);
- $favouritable->favourites()->firstOrCreate([
+ $modelInfo = $this->validate($request, $this->entityHelper->validationRules());
+ $entity = $this->entityHelper->getVisibleEntityFromRequestData($modelInfo);
+ $entity->favourites()->firstOrCreate([
'user_id' => user()->id,
]);
$this->showSuccessNotification(trans('activities.favourite_add_notification', [
- 'name' => $favouritable->name,
+ 'name' => $entity->name,
]));
return redirect()->back();
*/
public function remove(Request $request)
{
- $favouritable = $this->getValidatedModelFromRequest($request);
- $favouritable->favourites()->where([
+ $modelInfo = $this->validate($request, $this->entityHelper->validationRules());
+ $entity = $this->entityHelper->getVisibleEntityFromRequestData($modelInfo);
+ $entity->favourites()->where([
'user_id' => user()->id,
])->delete();
$this->showSuccessNotification(trans('activities.favourite_remove_notification', [
- 'name' => $favouritable->name,
+ 'name' => $entity->name,
]));
return redirect()->back();
}
-
- /**
- * @throws \Illuminate\Validation\ValidationException
- * @throws \Exception
- */
- protected function getValidatedModelFromRequest(Request $request): Entity
- {
- $modelInfo = $this->validate($request, [
- 'type' => ['required', 'string'],
- 'id' => ['required', 'integer'],
- ]);
-
- if (!class_exists($modelInfo['type'])) {
- throw new \Exception('Model not found');
- }
-
- /** @var Model $model */
- $model = new $modelInfo['type']();
- if (!$model instanceof Favouritable) {
- throw new \Exception('Model not favouritable');
- }
-
- $modelInstance = $model->newQuery()
- ->where('id', '=', $modelInfo['id'])
- ->first(['id', 'name', 'owned_by']);
-
- $inaccessibleEntity = ($modelInstance instanceof Entity && !userCan('view', $modelInstance));
- if (is_null($modelInstance) || $inaccessibleEntity) {
- throw new \Exception('Model instance not found');
- }
-
- return $modelInstance;
- }
}
namespace BookStack\Activity\Controllers;
use BookStack\Activity\Tools\UserEntityWatchOptions;
-use BookStack\App\Model;
-use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Tools\MixedEntityRequestHelper;
use BookStack\Http\Controller;
-use Exception;
use Illuminate\Http\Request;
-use Illuminate\Validation\ValidationException;
class WatchController extends Controller
{
- public function update(Request $request)
+ public function update(Request $request, MixedEntityRequestHelper $entityHelper)
{
$this->checkPermission('receive-notifications');
$this->preventGuestAccess();
$requestData = $this->validate($request, [
'level' => ['required', 'string'],
+ ...$entityHelper->validationRules()
]);
- $watchable = $this->getValidatedModelFromRequest($request);
+ $watchable = $entityHelper->getVisibleEntityFromRequestData($requestData);
$watchOptions = new UserEntityWatchOptions(user(), $watchable);
$watchOptions->updateLevelByName($requestData['level']);
return redirect()->back();
}
-
- /**
- * @throws ValidationException
- * @throws Exception
- */
- protected function getValidatedModelFromRequest(Request $request): Entity
- {
- $modelInfo = $this->validate($request, [
- 'type' => ['required', 'string'],
- 'id' => ['required', 'integer'],
- ]);
-
- if (!class_exists($modelInfo['type'])) {
- throw new Exception('Model not found');
- }
-
- /** @var Model $model */
- $model = new $modelInfo['type']();
- if (!$model instanceof Entity) {
- throw new Exception('Model not an entity');
- }
-
- $modelInstance = $model->newQuery()
- ->where('id', '=', $modelInfo['id'])
- ->first(['id', 'name', 'owned_by']);
-
- $inaccessibleEntity = ($modelInstance instanceof Entity && !userCan('view', $modelInstance));
- if (is_null($modelInstance) || $inaccessibleEntity) {
- throw new Exception('Model instance not found');
- }
-
- return $modelInstance;
- }
}
--- /dev/null
+<?php
+
+namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\EntityProvider;
+use BookStack\Entities\Models\Entity;
+
+class MixedEntityRequestHelper
+{
+ public function __construct(
+ protected EntityProvider $entities,
+ ) {
+ }
+
+ /**
+ * Query out an entity, visible to the current user, for the given
+ * entity request details (this provided in a request validated by
+ * this classes' validationRules method).
+ * @param array{type: string, id: string} $requestData
+ */
+ public function getVisibleEntityFromRequestData(array $requestData): Entity
+ {
+ $entityType = $this->entities->get($requestData['type']);
+
+ return $entityType->newQuery()->scopes(['visible'])->findOrFail($requestData['id']);
+ }
+
+ /**
+ * Get the validation rules for an abstract entity request.
+ * @return array{type: string[], id: string[]}
+ */
+ public function validationRules(): array
+ {
+ return [
+ 'type' => ['required', 'string'],
+ 'id' => ['required', 'integer'],
+ ];
+ }
+}
@endphp
<form action="{{ url('/favourites/' . ($isFavourite ? 'remove' : 'add')) }}" method="POST">
{{ csrf_field() }}
- <input type="hidden" name="type" value="{{ get_class($entity) }}">
+ <input type="hidden" name="type" value="{{ $entity->getMorphClass() }}">
<input type="hidden" name="id" value="{{ $entity->id }}">
<button type="submit" data-shortcut="favourite" class="icon-list-item text-link">
<span>@icon($isFavourite ? 'star' : 'star-outline')</span>
<form action="{{ url('/watching/update') }}" method="POST">
{{ csrf_field() }}
{{ method_field('PUT') }}
- <input type="hidden" name="type" value="{{ get_class($entity) }}">
+ <input type="hidden" name="type" value="{{ $entity->getMorphClass() }}">
<input type="hidden" name="id" value="{{ $entity->id }}">
<button type="submit"
name="level"
<form action="{{ url('/watching/update') }}" method="POST">
{{ method_field('PUT') }}
{{ csrf_field() }}
- <input type="hidden" name="type" value="{{ get_class($entity) }}">
+ <input type="hidden" name="type" value="{{ $entity->getMorphClass() }}">
<input type="hidden" name="id" value="{{ $entity->id }}">
<ul refs="dropdown@menu" class="dropdown-menu xl-limited anchor-left pb-none">
$this->actingAs($editor)->get($book->getUrl());
$resp = $this->put('/watching/update', [
- 'type' => get_class($book),
+ 'type' => $book->getMorphClass(),
'id' => $book->id,
'level' => 'comments'
]);
]);
$resp = $this->put('/watching/update', [
- 'type' => get_class($book),
+ 'type' => $book->getMorphClass(),
'id' => $book->id,
'level' => 'default'
]);
$book = $this->entities->book();
$resp = $this->put('/watching/update', [
- 'type' => get_class($book),
+ 'type' => $book->getMorphClass(),
'id' => $book->id,
'level' => 'comments'
]);
$resp = $this->actingAs($editor)->get($page->getUrl());
$this->withHtml($resp)->assertElementContains('button', 'Favourite');
- $this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/add"]');
+ $this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/add"] input[name="type"][value="page"]');
$resp = $this->post('/favourites/add', [
- 'type' => get_class($page),
+ 'type' => $page->getMorphClass(),
'id' => $page->id,
]);
$resp->assertRedirect($page->getUrl());
$this->withHtml($resp)->assertElementExists('form[method="POST"][action$="/favourites/remove"]');
$resp = $this->post('/favourites/remove', [
- 'type' => get_class($page),
+ 'type' => $page->getMorphClass(),
'id' => $page->id,
]);
$resp->assertRedirect($page->getUrl());
$this->actingAs($user)->get($book->getUrl());
$resp = $this->post('/favourites/add', [
- 'type' => get_class($book),
+ 'type' => $book->getMorphClass(),
'id' => $book->id,
]);
$resp->assertRedirect($book->getUrl());