diff --git a/app/Http/Controllers/API/v1/StatusController.php b/app/Http/Controllers/API/v1/StatusController.php index 2ebf5a305..a9443271d 100644 --- a/app/Http/Controllers/API/v1/StatusController.php +++ b/app/Http/Controllers/API/v1/StatusController.php @@ -18,6 +18,7 @@ use App\Models\Stopover; use App\Models\Trip; use Illuminate\Auth\Access\AuthorizationException; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -29,7 +30,6 @@ use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\ValidationException; use InvalidArgumentException; -use OpenApi\Annotations as OA; class StatusController extends Controller { @@ -251,6 +251,35 @@ public function getLivePositionForStatus($ids): AnonymousResourceCollection { return JsonResource::collection(StatusBackend::getLivePositionForStatus($ids)); } + /** + * Experimental - do not add to docs now. + * + * Used in Lokbuch for testing. + */ + public function list(Request $request): AnonymousResourceCollection { + $validated = $request->validate([ + 'body' => ['nullable', 'string', 'max:32'], + ]); + + $user = auth()->user(); + $query = Status::query()->orderByDesc('created_at'); + + if (isset($validated['body'])) { + $query->where('body', 'like', '%' . $validated['body'] . '%'); + } + + $query->join('train_checkins', 'train_checkins.status_id', '=', 'statuses.id') + ->join('users', 'statuses.user_id', '=', 'users.id') + ->where(\App\Http\Controllers\Backend\Transport\StatusController::filterStatusVisibility($user)) + ->where('train_checkins.departure', '<', now()->addMinutes(20)) + ->whereNotIn('statuses.user_id', $user->mutedUsers()->select('muted_id')) + ->whereNotIn('statuses.user_id', $user->blockedUsers()->select('blocked_id')) + ->whereNotIn('statuses.user_id', $user->blockedByUsers()->select('user_id')) + ->select('statuses.*'); + + return StatusResource::collection($query->cursorPaginate(20)); + } + /** * @OA\Get( * path="/status/{id}", @@ -419,7 +448,7 @@ public function update(Request $request, int $statusId): JsonResponse { $this->authorize('update', $status); //Check for disallowed status visibility changes - if(auth()->user()->can('disallow-status-visibility-change') && $validated['visibility'] !== StatusVisibility::PRIVATE->value) { + if (auth()->user()->can('disallow-status-visibility-change') && $validated['visibility'] !== StatusVisibility::PRIVATE->value) { return $this->sendError('You are not allowed to change the visibility to anything else than private', 403); } diff --git a/app/Http/Controllers/Backend/Transport/StatusController.php b/app/Http/Controllers/Backend/Transport/StatusController.php index 8ac8cc1c5..a4f14c5eb 100644 --- a/app/Http/Controllers/Backend/Transport/StatusController.php +++ b/app/Http/Controllers/Backend/Transport/StatusController.php @@ -2,11 +2,15 @@ namespace App\Http\Controllers\Backend\Transport; +use App\Enum\StatusVisibility; use App\Http\Controllers\Backend\Support\MentionHelper; use App\Http\Controllers\Controller; use App\Models\Station; use App\Models\Status; use App\Models\Stopover; +use App\Models\User; +use Closure; +use Illuminate\Database\Eloquent\Builder; abstract class StatusController extends Controller { @@ -42,4 +46,35 @@ public static function getPrintableEscapedBody(Status $status): string { //Replace line breaks with
tags return nl2br($body); } + + /** + * @param User|null $viewingUser The user who is viewing the statuses (null = guest) + * + * @return Closure + */ + public static function filterStatusVisibility(?User $viewingUser = null): Closure { + return function(Builder $query) use ($viewingUser) { + //Visibility checks: One of the following options must be true + + //Option 1: User is public AND status is public + $query->where(function(Builder $query) use ($viewingUser) { + $query->where('users.private_profile', 0) + ->whereIn('visibility', [StatusVisibility::PUBLIC->value] + ($viewingUser !== null ? [StatusVisibility::AUTHENTICATED->value] : [])); + }); + + if ($viewingUser !== null) { + //Option 2: Status is from oneself + $query->orWhere('users.id', $viewingUser->id); + + //Option 3: Status is from a followed BUT not unlisted or private + $query->orWhere(function(Builder $query) use ($viewingUser) { + $query->whereIn('users.id', $viewingUser->follows()->select('follow_id')) + ->whereNotIn('statuses.visibility', [ + StatusVisibility::UNLISTED->value, + StatusVisibility::PRIVATE->value, + ]); + }); + } + }; + } } diff --git a/app/Http/Controllers/Backend/User/DashboardController.php b/app/Http/Controllers/Backend/User/DashboardController.php index 3cceb2070..60406e332 100644 --- a/app/Http/Controllers/Backend/User/DashboardController.php +++ b/app/Http/Controllers/Backend/User/DashboardController.php @@ -3,12 +3,12 @@ namespace App\Http\Controllers\Backend\User; use App\Enum\StatusVisibility; +use App\Http\Controllers\Backend\Transport\StatusController; use App\Http\Controllers\Controller; use App\Models\Status; use App\Models\User; use Carbon\Carbon; use Illuminate\Contracts\Pagination\Paginator; -use Illuminate\Database\Eloquent\Builder; abstract class DashboardController extends Controller { @@ -59,30 +59,7 @@ public static function getGlobalDashboard(User $user): Paginator { ]) ->join('train_checkins', 'train_checkins.status_id', '=', 'statuses.id') ->join('users', 'statuses.user_id', '=', 'users.id') - ->where(function(Builder $query) use ($user) { - //Visibility checks: One of the following options must be true - - //Option 1: User is public AND status is public - $query->where(function(Builder $query) { - $query->where('users.private_profile', 0) - ->whereIn('visibility', [ - StatusVisibility::PUBLIC->value, - StatusVisibility::AUTHENTICATED->value - ]); - }); - - //Option 2: Status is from oneself - $query->orWhere('users.id', $user->id); - - //Option 3: Status is from a followed BUT not unlisted or private - $query->orWhere(function(Builder $query) use ($user) { - $query->whereIn('users.id', $user->follows()->select('follow_id')) - ->whereNotIn('statuses.visibility', [ - StatusVisibility::UNLISTED->value, - StatusVisibility::PRIVATE->value, - ]); - }); - }) + ->where(StatusController::filterStatusVisibility($user)) ->where('train_checkins.departure', '<', Carbon::now()->addMinutes(20)) ->whereNotIn('statuses.user_id', $user->mutedUsers()->select('muted_id')) ->whereNotIn('statuses.user_id', $user->blockedUsers()->select('blocked_id')) diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index cfff41d40..3b8a8b43e 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -216,34 +216,7 @@ public static function getStatusesByEvent(Event $event): array { ->select('statuses.*') ->join('users', 'statuses.user_id', '=', 'users.id') ->join('train_checkins', 'statuses.id', '=', 'train_checkins.status_id') - ->where(function(Builder $query) { - //Visibility checks: One of the following options must be true - - //Option 1: User is public AND status is public - $query->where(function(Builder $query) { - $visibilities = [StatusVisibility::PUBLIC->value]; - if (auth()->check()) { - $visibilities[] = StatusVisibility::AUTHENTICATED->value; - } - - $query->where('users.private_profile', 0) - ->whereIn('visibility', $visibilities); - }); - - if (auth()->check()) { - //Option 2: Status is from oneself - $query->orWhere('users.id', auth()->id()); - - //Option 3: Status is from a followed BUT not unlisted or private - $query->orWhere(function(Builder $query) { - $query->whereIn('users.id', auth()->user()->follows()->select('follow_id')) - ->whereNotIn('visibility', [ - StatusVisibility::UNLISTED->value, - StatusVisibility::PRIVATE->value, - ]); - }); - } - }) + ->where(Backend\Transport\StatusController::filterStatusVisibility(auth()->user())) ->orderBy('train_checkins.departure', 'desc'); if (auth()->check()) { diff --git a/routes/api.php b/routes/api.php index 7dfd078e4..250bf1fc5 100644 --- a/routes/api.php +++ b/routes/api.php @@ -196,6 +196,7 @@ Route::get('statuses', [StatusController::class, 'enRoute']); Route::get('positions', [StatusController::class, 'livePositions']); Route::get('positions/{ids}', [StatusController::class, 'getLivePositionForStatus']); + Route::get('status', [StatusController::class, 'list']); Route::get('status/{id}', [StatusController::class, 'show']); Route::get('status/{id}/likes', [LikesController::class, 'show']); Route::get('status/{statusId}/tags', [StatusTagController::class, 'index']);