diff --git a/app/Bookmark.php b/app/Bookmark.php index f59e210d..c7427c8d 100644 --- a/app/Bookmark.php +++ b/app/Bookmark.php @@ -3,6 +3,10 @@ namespace App; use App\Profile; +use App\User; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphPivot; class Bookmark extends MorphPivot @@ -16,17 +20,36 @@ class Bookmark extends MorphPivot /** @var string The database table for this model. */ public $table = 'bookmarks'; + ////////////////// + // Query Scopes // + ////////////////// + + /** + * Query scope to get bookmarks of a particular model type. + */ + public function scopeOfType(Builder $q, Model|string $model): void + { + $q->where('userable_type', '=', is_object($model) ? get_class($model) : $model); + } + ////////////// // Relations// ////////////// /** * Bookmark has one Profile (one-to-one) - * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ - public function profile() + public function profile(): BelongsTo { return $this->belongsTo(Profile::class, 'userable_id'); } + + /** + * Bookmark(s) belong to one User (many-to-one) + */ + public function user(): BelongsTo + { + return $this->belongsTo(User::class, 'user_id'); + } + } diff --git a/app/Http/Controllers/UsersController.php b/app/Http/Controllers/UsersController.php index 993f4288..ef81e8e3 100644 --- a/app/Http/Controllers/UsersController.php +++ b/app/Http/Controllers/UsersController.php @@ -37,6 +37,8 @@ public function __construct() $this->middleware('can:view,user')->only('show'); + $this->middleware('can:viewBookmarks,user')->only('showBookmarks'); + $this->middleware('can:create,App\User')->only([ 'create', 'store', diff --git a/app/Http/Livewire/BookmarkButton.php b/app/Http/Livewire/BookmarkButton.php index 851c1e8e..a32efd30 100644 --- a/app/Http/Livewire/BookmarkButton.php +++ b/app/Http/Livewire/BookmarkButton.php @@ -2,10 +2,13 @@ namespace App\Http\Livewire; +use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Livewire\Component; class BookmarkButton extends Component { + use AuthorizesRequests; + public $model; public $user; @@ -26,6 +29,8 @@ public function render() public function bookmark() { + $this->authorize('create', 'App\Bookmark'); + $this->user->bookmark($this->model); $this->emit('alert', "Bookmarked!", 'success'); @@ -33,6 +38,8 @@ public function bookmark() public function unbookmark() { + $this->authorize('delete', $this->user->bookmarkFor($this->model)); + $this->user->unbookmark($this->model); $this->emit('alert', "Removed from your bookmarks", 'success'); diff --git a/app/Policies/UserBookmarkPolicy.php b/app/Policies/UserBookmarkPolicy.php new file mode 100644 index 00000000..9dfd4d35 --- /dev/null +++ b/app/Policies/UserBookmarkPolicy.php @@ -0,0 +1,60 @@ +hasRole('site_admin')) { + return true; + } + } + + /** + * Determine whether the user can view the owner's bookmarks. + * + * @param User $user + * @param User $owner + * @return \Illuminate\Auth\Access\Response|bool + */ + public function viewBookmarks(User $user, User $owner) + { + return $user->is($owner); + } + + /** + * Determine whether the user can create bookmarks. + * + * @param \App\User $user + * @return \Illuminate\Auth\Access\Response|bool + */ + public function create(User $user) + { + return true; + } + + /** + * Determine whether the user can delete the bookmark. + * + * @param \App\User $user + * @param \App\Bookmark $bookmark + * @return \Illuminate\Auth\Access\Response|bool + */ + public function delete(User $user, Bookmark $bookmark) + { + return $user->owns($bookmark); + } +} diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php index 2ad69c6c..cc479947 100644 --- a/app/Policies/UserPolicy.php +++ b/app/Policies/UserPolicy.php @@ -70,6 +70,18 @@ public function viewDelegations(User $user, User $delegator) return $user->can('viewForDelegator', [UserDelegation::class, $delegator]); } + /** + * Determine whether the user can view the owner's bookmarks. + * + * @param \App\User $user + * @param \App\User $model + * @return mixed + */ + public function viewBookmarks(User $user, User $owner) + { + return $user->can('viewBookmarks', ['App\Bookmark', $owner]); + } + /** * Determine whether the user can create models. * diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 7ea48be4..7dcbadaa 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -2,6 +2,7 @@ namespace App\Providers; +use App\Bookmark; use App\Policies\LogPolicy; use App\Policies\ProfilePolicy; use App\Policies\SettingPolicy; @@ -11,6 +12,7 @@ use App\Policies\UserDelegationPolicy; use App\LogEntry; use App\Policies\ProfileStudentPolicy; +use App\Policies\UserBookmarkPolicy; use App\Profile; use App\ProfileStudent; use App\Setting; @@ -37,6 +39,7 @@ class AuthServiceProvider extends ServiceProvider Tag::class => TagPolicy::class, User::class => UserPolicy::class, UserDelegation::class => UserDelegationPolicy::class, + Bookmark::class => UserBookmarkPolicy::class, ]; /** diff --git a/app/User.php b/app/User.php index ced551d4..311c85d9 100644 --- a/app/User.php +++ b/app/User.php @@ -10,7 +10,7 @@ use App\UserSetting; use App\Traits\RoleTrait; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Support\Facades\DB; +use Illuminate\Database\Eloquent\Model; use OwenIt\Auditing\Auditable as HasAudits; use OwenIt\Auditing\Contracts\Auditable; use Illuminate\Notifications\Notifiable; @@ -127,6 +127,21 @@ public function hasBookmarked($model) return $this->bookmarked($model)->where('userable_id', '=', $model->getKey())->exists(); } + /** + * Returns the User's Bookmark of the given model + */ + public function bookmarkFor(Model $model): ?Bookmark + { + if ($this->relationLoaded('bookmarks')) { + return $this->bookmarks->firstWhere(function($bookmark) use ($model) { + return $bookmark->userable_id === $model->getKey() && + $bookmark->userable_type === get_class($model); + }); + } + + return $this->bookmarks()->ofType($model)->firstWhere('userable_id', '=', $model->getKey()); + } + /** * Bookmark the given model *