115 lines
4.8 KiB
PHP
115 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\Admin;
|
|
use App\Models\Bank;
|
|
use App\Models\Organization;
|
|
use App\Models\Poll;
|
|
use App\Models\Post;
|
|
use App\Models\PostTranslation;
|
|
use App\Models\User;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\App;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\Gate;
|
|
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
|
|
|
/**
|
|
* Dedicated page of a poll: its post (title, image, intro, text) with the poll form below.
|
|
*/
|
|
class PollController extends Controller
|
|
{
|
|
public function show(string $slug)
|
|
{
|
|
// Polls are for logged-in profiles only. The route requires a login already; this is a
|
|
// safety net should the route ever be moved out of its protected group.
|
|
if (!Auth::guard('web')->check()) {
|
|
session(['url.intended' => request()->url()]);
|
|
|
|
return redirect(LaravelLocalization::localizeUrl('/login'));
|
|
}
|
|
|
|
$postId = PostTranslation::where('slug', $slug)->value('post_id');
|
|
$post = $postId ? Post::with(['poll', 'category.translations'])->find($postId) : null;
|
|
if (!$post || !$post->poll) {
|
|
return view('posts.not_found');
|
|
}
|
|
|
|
// Poll admins may preview unpublished polls and drafts; for everyone else a draft does
|
|
// not exist yet, even when its post already has a publication date (security audit R3)
|
|
$preview = Poll::viewerIsPollAdmin();
|
|
if ($post->poll->status === Poll::STATUS_DRAFT && !$preview) {
|
|
return view('posts.not_found');
|
|
}
|
|
$published = function ($query) use ($preview) {
|
|
if (!$preview) {
|
|
$query->whereDate('from', '<=', now())
|
|
->where(fn ($q) => $q->whereDate('till', '>', now())->orWhereNull('till'));
|
|
}
|
|
};
|
|
|
|
// The current language, otherwise any published language
|
|
$translation = $post->translations()->where('locale', App::getLocale())->where($published)->first()
|
|
?? $post->translations()->where($published)->first();
|
|
if (!$translation) {
|
|
return view('posts.not_found');
|
|
}
|
|
|
|
// Another of the user's profiles may take part while the active one may not: right after
|
|
// logging in to this poll (a one-time session marker set by LoginResponse) with only one
|
|
// such profile, switch to it straight away (its own login flow, password for banks and
|
|
// admins); otherwise offer them on the page.
|
|
$justLoggedIn = session()->pull('poll_switch_after_login') === '/' . ltrim(request()->path(), '/');
|
|
$switchProfiles = $this->switchProfiles($post->poll, request()->url());
|
|
if ($justLoggedIn && count($switchProfiles) === 1) {
|
|
return redirect($switchProfiles[0]['url']);
|
|
}
|
|
|
|
$media = $post->getFirstMedia('posts');
|
|
$category = $post->category?->translations->firstWhere('locale', App::getLocale())?->name
|
|
?? $post->category?->translations->first()?->name;
|
|
$update = Carbon::parse($translation->updated_at)->isoFormat('LL');
|
|
|
|
return view('polls.show', compact('post', 'translation', 'media', 'category', 'update', 'switchProfiles'));
|
|
}
|
|
|
|
/**
|
|
* The logged-in user's other profiles that may take part when the active profile may not,
|
|
* each with the direct login link of that profile that returns to this poll.
|
|
*
|
|
* @return array<int, array{name: string, type: string, url: string}>
|
|
*/
|
|
private function switchProfiles(Poll $poll, string $pollUrl): array
|
|
{
|
|
$user = Auth::guard('web')->user();
|
|
$active = getActiveProfile();
|
|
if (!$user instanceof User || !$active || !$poll->isOpen() || $poll->isTargetedAt($active, $user)) {
|
|
return [];
|
|
}
|
|
|
|
$user->loadMissing(['organizations', 'banksManaged', 'admins']);
|
|
$routes = [
|
|
User::class => ['user.direct-login', 'userId'],
|
|
Organization::class => ['organization.direct-login', 'organizationId'],
|
|
Bank::class => ['bank.direct-login', 'bankId'],
|
|
Admin::class => ['admin.direct-login', 'adminId'],
|
|
];
|
|
|
|
$profiles = collect([$user])->concat($user->organizations)->concat($user->banksManaged)->concat($user->admins)
|
|
->reject(fn (Model $profile) => $profile->is($active))
|
|
->filter(fn (Model $profile) => $poll->isTargetedAt($profile, $user));
|
|
|
|
return $profiles->map(function (Model $profile) use ($routes, $pollUrl) {
|
|
[$route, $parameter] = $routes[get_class($profile)];
|
|
|
|
return [
|
|
'name' => $profile->name,
|
|
'type' => class_basename($profile),
|
|
'url' => route($route, [$parameter => $profile->getKey()]) . '?intended=' . urlencode($pollUrl),
|
|
];
|
|
})->values()->all();
|
|
}
|
|
}
|