Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions app/Actions/Photos/FilterPhotosAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ class FilterPhotosAction
/**
* @return LengthAwarePaginator<int, Photo>
*/
public function run(User $user): LengthAwarePaginator
public function run(User $user, bool $showAllPhotos = false): LengthAwarePaginator
{
$photos = $this->baseQuery($user)
$photos = $this->baseQuery($user, $showAllPhotos)
->withExists([
'items',
'photoItemSuggestions' => fn (Builder $query) => $query->whereNull('is_accepted')->where('score', '>=', 80),
Expand All @@ -35,25 +35,32 @@ public function run(User $user): LengthAwarePaginator
/**
* @return array<int, mixed>
*/
public function allIds(User $user): array
public function allIds(User $user, bool $showAllPhotos = false): array
{
return $this->baseQuery($user)->pluck('id')->all();
return $this->baseQuery($user, $showAllPhotos)->pluck('id')->all();
}

/**
* @return HasMany<Photo, User>
* @return HasMany<Photo, User>|\Illuminate\Database\Eloquent\Builder<Photo>
*/
private function baseQuery(User $user): HasMany
private function baseQuery(User $user, bool $showAllPhotos = false): HasMany|\Illuminate\Database\Eloquent\Builder
{
$query = $user
->photos()
$query = $showAllPhotos
? Photo::query()->whereHas('user')
: $user->photos();

$query = $query
->filter($user->settings->photo_filters)
->orderBy($user->settings->sort_column, $user->settings->sort_direction);

if ($user->settings->sort_column !== 'id') {
$query->orderBy('id');
}

if ($showAllPhotos) {
$query->with('user');
}

return $query;
}
}
10 changes: 6 additions & 4 deletions app/Actions/Photos/GetNextPhotoAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@

class GetNextPhotoAction
{
public function run(User $user, Photo $photo): ?string
public function run(User $user, Photo $photo, bool $showAllPhotos = false): ?string
{
$attribute = $photo->getAttribute($user->settings->sort_column);

/** @var Builder $builder */
$builder = $user
->photos()
->filter($user->settings->photo_filters);
$builder = $showAllPhotos
? Photo::query()->whereHas('user')
: $user->photos();

$builder->filter($user->settings->photo_filters);

Check failure on line 20 in app/Actions/Photos/GetNextPhotoAction.php

View workflow job for this annotation

GitHub Actions / phpstan

Ignored error pattern #^Call to an undefined method Illuminate\\Contracts\\Database\\Eloquent\\Builder::filter\(\)\.$# (method.notFound) in path /app/app/Actions/Photos/GetNextPhotoAction.php is expected to occur 2 times, but occurred only 1 time.

if ($attribute) {
$nextPhoto = $builder->clone()
Expand Down
12 changes: 7 additions & 5 deletions app/Actions/Photos/GetPreviousPhotoAction.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?php

namespace App\Actions\Photos;
Expand All @@ -8,14 +8,16 @@

class GetPreviousPhotoAction
{
public function run(User $user, Photo $photo): ?string
public function run(User $user, Photo $photo, bool $showAllPhotos = false): ?string
{
$attribute = $photo->getAttribute($user->settings->sort_column);

/** @var Builder $builder */
$builder = $user
->photos()
->filter($user->settings->photo_filters);
/** @var \Illuminate\Database\Eloquent\Builder<Photo> $builder */
$builder = $showAllPhotos
? Photo::query()->whereHas('user')
: $user->photos();

$builder->filter($user->settings->photo_filters);

if ($attribute) {
$previousPhoto = $builder->clone()
Expand Down
32 changes: 32 additions & 0 deletions app/Http/Controllers/Photos/BulkPhotoItemsController.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<?php

namespace App\Http\Controllers\Photos;
Expand All @@ -7,6 +7,7 @@
use App\DTO\BulkPhotoItems;
use App\Http\Controllers\Controller;
use App\Models\Item;
use App\Models\Photo;
use App\Models\PhotoItem;
use App\Models\PhotoItemSuggestion;
use App\Models\PhotoItemTag;
Expand All @@ -16,8 +17,35 @@

class BulkPhotoItemsController extends Controller
{
/**
* @param array<int> $photoIds
*/
private function authorizeAdminOrOwnPhotos(array $photoIds): void
{
/** @var \App\Models\User|null $user */
$user = auth()->user();

if (! $user) {
abort(404);
}

$photoUserIds = Photo::query()->whereIn('id', $photoIds)->pluck('user_id')->unique()->values();

if ($user->is_admin) {
return;
}

if ($photoUserIds->count() === 1 && $photoUserIds->first() === $user->id) {
return;
}

abort(404);
}

public function store(BulkPhotoItems $bulkPhotoItems): void
{
$this->authorizeAdminOrOwnPhotos($bulkPhotoItems->photo_ids);

$items = Item::query()->find(array_column($bulkPhotoItems->items, 'id'))->keyBy('id');
$usedShortcuts = TagShortcut::query()->find($bulkPhotoItems->used_shortcuts)->keyBy('id');

Expand Down Expand Up @@ -58,6 +86,8 @@

public function destroy(BulkDeletePhotoItems $bulkDeletePhotoItems): void
{
$this->authorizeAdminOrOwnPhotos($bulkDeletePhotoItems->photo_ids);

DB::transaction(function () use ($bulkDeletePhotoItems): void {
PhotoItem::query()
->whereIn('photo_id', $bulkDeletePhotoItems->photo_ids)
Expand All @@ -77,6 +107,8 @@

public function addTags(BulkAddPhotoTags $bulkAddPhotoTags): RedirectResponse
{
$this->authorizeAdminOrOwnPhotos($bulkAddPhotoTags->photo_ids);

$photosWithMultipleItems = [];
$photosWithNoItems = [];
$tagsAdded = false;
Expand Down
11 changes: 10 additions & 1 deletion app/Http/Controllers/Photos/CopyPhotoItemController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,16 @@ class CopyPhotoItemController extends Controller
{
public function __invoke(PhotoItem $photoItem): JsonResponse
{
if (auth()->id() !== $photoItem->photo->user_id) {
/** @var \App\Models\User|null $user */
$user = auth()->user();

if (! $user) {
abort(404);
}

$photo = $photoItem->photo;

if (! $user->is_admin && $user->id !== $photo->user_id) {
abort(404);
}

Expand Down
22 changes: 20 additions & 2 deletions app/Http/Controllers/Photos/PhotoItemTagsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ class PhotoItemTagsController extends Controller
{
public function store(PhotoItem $photoItem, StorePhotoItemTagRequest $request): JsonResponse
{
if (auth()->id() !== $photoItem->photo->user_id) {
/** @var \App\Models\User|null $user */
$user = auth()->user();

if (! $user) {
abort(404);
}

$photo = $photoItem->photo;

if (! $user->is_admin && $user->id !== $photo->user_id) {
abort(404);
}

Expand All @@ -23,7 +32,16 @@ public function store(PhotoItem $photoItem, StorePhotoItemTagRequest $request):

public function destroy(PhotoItem $photoItem, Tag $tag): JsonResponse
{
if (auth()->id() !== $photoItem->photo->user_id) {
/** @var \App\Models\User|null $user */
$user = auth()->user();

if (! $user) {
abort(404);
}

$photo = $photoItem->photo;

if (! $user->is_admin && $user->id !== $photo->user_id) {
abort(404);
}

Expand Down
12 changes: 9 additions & 3 deletions app/Http/Controllers/Photos/PhotoItemsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
/** @var User $user */
$user = auth()->user();

if ($user->id !== $photo->user_id) {
if (! $user->is_admin && $user->id !== $photo->user_id) {
abort(404);
}

Expand All @@ -41,7 +41,10 @@

public function update(PhotoItem $photoItem, UpdatePhotoItemRequest $request): JsonResponse
{
if (auth()->id() !== $photoItem->photo->user_id) {
$user = auth()->user();
$photo = $photoItem->photo;

if (! $user->is_admin && $user->id !== $photo->user_id) {

Check failure on line 47 in app/Http/Controllers/Photos/PhotoItemsController.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $is_admin on App\Models\User|null.

Check failure on line 47 in app/Http/Controllers/Photos/PhotoItemsController.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $id on App\Models\User|null.
abort(404);
}

Expand All @@ -68,7 +71,10 @@

public function destroy(PhotoItem $photoItem): JsonResponse
{
if (auth()->id() !== $photoItem->photo->user_id) {
$user = auth()->user();
$photo = $photoItem->photo;

if (! $user->is_admin && $user->id !== $photo->user_id) {

Check failure on line 77 in app/Http/Controllers/Photos/PhotoItemsController.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $is_admin on App\Models\User|null.

Check failure on line 77 in app/Http/Controllers/Photos/PhotoItemsController.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $id on App\Models\User|null.
abort(404);
}

Expand Down
17 changes: 11 additions & 6 deletions app/Http/Controllers/Photos/PhotosController.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,19 @@
$user->save();
}

$photos = $filterPhotosAction->run($user);
$showAllPhotos = $user->is_admin;
$photos = $filterPhotosAction->run($user, $showAllPhotos);

$tagsAndItems = $getTagsAndItemsAction->run();

return Inertia::render('Photos/Index', [
'photos' => $photos,
'allPhotoIds' => $filterPhotosAction->allIds($user),
'allPhotoIds' => $filterPhotosAction->allIds($user, $showAllPhotos),
'filters' => $user->settings->photo_filters,
'items' => $tagsAndItems['items'],
'tags' => $tagsAndItems['tags'],
'tagShortcuts' => $this->getTagShortcuts($user),
'isAdmin' => $user->is_admin,
]);
}

Expand All @@ -81,19 +83,20 @@
/** @var User $user */
$user = auth()->user();

if ($user->id !== $photo->user_id) {
if (! $user->is_admin && $user->id !== $photo->user_id) {
abort(404);
}

if (! request()->wantsJson()) {
$tagsAndItems = $getTagsAndItemsAction->run();
$showAllPhotos = $user->is_admin;

return Inertia::render('Photos/Show', [
'photoId' => $photo->id,
'items' => $tagsAndItems['items'],
'tags' => $tagsAndItems['tags'],
'nextPhotoUrl' => $getNextPhotoAction->run($user, $photo),
'previousPhotoUrl' => $getPreviousPhotoAction->run($user, $photo),
'nextPhotoUrl' => $getNextPhotoAction->run($user, $photo, $showAllPhotos),
'previousPhotoUrl' => $getPreviousPhotoAction->run($user, $photo, $showAllPhotos),
'tagShortcuts' => $this->getTagShortcuts($user),
]);
}
Expand All @@ -120,7 +123,9 @@

public function destroy(Photo $photo): RedirectResponse
{
if (auth()->id() !== $photo->user_id) {
$user = auth()->user();

if (! $user->is_admin && $user->id !== $photo->user_id) {

Check failure on line 128 in app/Http/Controllers/Photos/PhotosController.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $is_admin on App\Models\User|null.

Check failure on line 128 in app/Http/Controllers/Photos/PhotosController.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $id on App\Models\User|null.
abort(404);
}

Expand Down
8 changes: 7 additions & 1 deletion app/Rules/PhotosBelongToUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,15 @@
return;
}

$user = auth()->user();

if ($user->is_admin) {

Check failure on line 25 in app/Rules/PhotosBelongToUser.php

View workflow job for this annotation

GitHub Actions / phpstan

Cannot access property $is_admin on App\Models\User|null.
return;
}

$photosBelongsToOthers = Photo::query()
->whereIn('id', $value)
->where('user_id', '!=', auth()->id())
->where('user_id', '!=', $user->id)
->exists();

if ($photosBelongsToOthers) {
Expand Down
18 changes: 18 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,21 @@ parameters:
identifier: return.type
count: 1
path: app/Models/TagShortcutItem.php

-
message: '#^Call to an undefined method Illuminate\\Contracts\\Database\\Eloquent\\Builder::filter\(\)\.$#'
identifier: method.notFound
count: 2
path: app/Actions/Photos/GetNextPhotoAction.php

-
message: '#^Call to an undefined method Illuminate\\Contracts\\Database\\Eloquent\\Builder::filter\(\)\.$#'
identifier: method.notFound
count: 2
path: app/Actions/Photos/GetPreviousPhotoAction.php

-
message: '#^Method App\\Http\\Controllers\\Photos\\BulkPhotoItemsController::authorizeAdminOrOwnPhotos\(\) has parameter \$photoIds with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: app/Http/Controllers/Photos/BulkPhotoItemsController.php
5 changes: 5 additions & 0 deletions resources/js/Pages/Photos/Index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const props = defineProps({
items: Array,
filters: Object,
tagShortcuts: Array,
isAdmin: Boolean,
});

const isSelecting = ref(localStorage.getItem('isSelecting') === 'true' || false);
Expand Down Expand Up @@ -353,6 +354,10 @@ const exportData = (format) => {

<ZoomIcon @click="zoomedImage = photo" class="absolute top-0 left-0"/>

<div v-if="isAdmin && photo.user" class="absolute top-2 left-8 text-xs shadow bg-blue-500 rounded px-2 py-1 text-white">
{{ photo.user.name }}
</div>

<div class="absolute top-2 right-2 flex gap-2">
<LocationIcon v-if="photo.latitude && photo.longitude"/>
<TaggedIcon v-if="photo.items_exists"/>
Expand Down
Loading