Skip to content

Commit f40fd4c

Browse files
Check whether users can view responsibilities (by ability or because they are marked as public)
1 parent 4d787e4 commit f40fd4c

24 files changed

+242
-78
lines changed

app/Http/Requests/Traits/ValidatesResponsibleUsers.php

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ protected function rulesForResponsibleUsers(): array
1717
'responsible_user_id.*' => [
1818
Rule::exists('users', 'id'),
1919
],
20+
'responsible_user_data.*.publicly_visible' => [
21+
'nullable',
22+
'boolean',
23+
],
2024
'responsible_user_data.*.position' => [
2125
'nullable',
2226
'string',
@@ -34,6 +38,7 @@ protected function rulesForResponsibleUsers(): array
3438
protected function attributesForResponsibleUsers(): array
3539
{
3640
return [
41+
'responsible_user_data.*.publicly_visible' => __('publicly visible'),
3742
'responsible_user_data.*.position' => __('Position'),
3843
'responsible_user_data.*.sort' => __('Sort'),
3944
];

app/Models/Event.php

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use App\Models\Traits\HasResponsibleUsers;
1111
use App\Models\Traits\HasSlugForRouting;
1212
use App\Models\Traits\HasWebsite;
13+
use App\Options\Ability;
1314
use App\Options\EventType;
1415
use App\Options\Visibility;
1516
use Carbon\Carbon;
@@ -171,6 +172,11 @@ public function fillAndSave(array $validatedData): bool
171172
&& $this->saveResponsibleUsers($validatedData);
172173
}
173174

175+
public function getAbilityToViewResponsibilities(): Ability
176+
{
177+
return Ability::ViewResponsibilitiesOfEvents;
178+
}
179+
174180
public function getBookingOptions(): Collection
175181
{
176182
if (isset($this->parentEvent)) {

app/Models/EventSeries.php

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use App\Models\Traits\HasDocuments;
88
use App\Models\Traits\HasResponsibleUsers;
99
use App\Models\Traits\HasSlugForRouting;
10+
use App\Options\Ability;
1011
use App\Options\EventSeriesType;
1112
use App\Options\Visibility;
1213
use Illuminate\Database\Eloquent\Builder;
@@ -101,6 +102,11 @@ public function fillAndSave(array $validatedData): bool
101102
&& $this->saveResponsibleUsers($validatedData);
102103
}
103104

105+
public function getAbilityToViewResponsibilities(): Ability
106+
{
107+
return Ability::ViewResponsibilitiesOfEventSeries;
108+
}
109+
104110
public function getRoute(): string
105111
{
106112
return route('event-series.show', $this);

app/Models/Organization.php

+6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\Models\Traits\HasLocation;
99
use App\Models\Traits\HasResponsibleUsers;
1010
use App\Models\Traits\HasWebsite;
11+
use App\Options\Ability;
1112
use App\Options\ActiveStatus;
1213
use Illuminate\Database\Eloquent\Collection;
1314
use Illuminate\Database\Eloquent\Factories\HasFactory;
@@ -80,6 +81,11 @@ public function fillAndSave(array $validatedData): bool
8081
&& $this->saveResponsibleUsers($validatedData);
8182
}
8283

84+
public function getAbilityToViewResponsibilities(): Ability
85+
{
86+
return Ability::ViewResponsibilitiesOfOrganizations;
87+
}
88+
8389
public function getRoute(): string
8490
{
8591
return route('organizations.show', $this);

app/Models/Traits/HasResponsibleUsers.php

+42-7
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
namespace App\Models\Traits;
44

55
use App\Models\User;
6+
use App\Options\Ability;
7+
use Illuminate\Database\Eloquent\Collection;
68
use Illuminate\Database\Eloquent\Model;
79
use Illuminate\Database\Eloquent\Relations\MorphToMany;
10+
use Illuminate\Support\Facades\Auth;
811

912
/**
1013
* @property-read $responsibleUsers {@see self::responsibleUsers()}
@@ -17,6 +20,7 @@ public function responsibleUsers(): MorphToMany
1720
{
1821
return $this->morphToMany(User::class, 'responsible_for', 'user_responsibilities')
1922
->withPivot([
23+
'publicly_visible',
2024
'position',
2125
'sort',
2226
])
@@ -26,16 +30,47 @@ public function responsibleUsers(): MorphToMany
2630
->orderBy('first_name');
2731
}
2832

29-
public function saveResponsibleUsers(array $validatedData): bool
33+
abstract public function getAbilityToViewResponsibilities(): Ability;
34+
35+
public function getResponsibleUsersVisibleForCurrentUser(): Collection
3036
{
31-
$this->responsibleUsers()->sync($validatedData['responsible_user_id'] ?? []);
37+
$currentUser = Auth::user();
3238

33-
foreach ($validatedData['responsible_user_data'] ?? [] as $userId => $pivotData) {
34-
if ($this->responsibleUsers()->updateExistingPivot($userId, $pivotData) !== 1) {
35-
return false;
36-
}
39+
if (isset($currentUser) && $currentUser->hasAbility($this->getAbilityToViewResponsibilities())) {
40+
return $this->responsibleUsers;
3741
}
3842

39-
return true;
43+
return $this->getPubliclyVisibleResponsibleUsers();
44+
}
45+
46+
/**
47+
* @return Collection<User>
48+
*/
49+
public function getPubliclyVisibleResponsibleUsers(): Collection
50+
{
51+
return $this->responsibleUsers->filter(fn (User $responsibleUser) => self::isPubliclyVisible($responsibleUser));
52+
}
53+
54+
public function hasPubliclyVisibleResponsibleUsers(): bool
55+
{
56+
return $this->responsibleUsers->first(fn (User $responsibleUser) => self::isPubliclyVisible($responsibleUser)) !== null;
57+
}
58+
59+
public function saveResponsibleUsers(array $validatedData): array
60+
{
61+
$responsibleUsers = [];
62+
foreach ($validatedData['responsible_user_id'] ?? [] as $responsibleUserId) {
63+
$pivotData = $validatedData['responsible_user_data'][$responsibleUserId] ?? [];
64+
$pivotData['publicly_visible'] ??= false;
65+
$responsibleUsers[$responsibleUserId] = $pivotData;
66+
}
67+
68+
return $this->responsibleUsers()->sync($responsibleUsers);
69+
}
70+
71+
private static function isPubliclyVisible(User $responsibleUser): bool
72+
{
73+
return isset($responsibleUser->pivot->publicly_visible)
74+
&& $responsibleUser->pivot->publicly_visible;
4075
}
4176
}

app/Models/User.php

+1
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ private function responsibleFor(string $class): MorphToMany
132132
{
133133
return $this->morphedByMany($class, 'responsible_for', 'user_responsibilities')
134134
->withPivot([
135+
'publicly_visible',
135136
'position',
136137
'sort',
137138
]);

app/Options/Ability.php

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ enum Ability: string
3030
case AddDocumentsToEvents = 'events.documents.create';
3131
case EditDocumentsOfEvents = 'events.documents.edit';
3232
case DeleteDocumentsOfEvents = 'events.documents.delete';
33+
case ViewResponsibilitiesOfEvents = 'events.responsibilities.view';
3334

3435
case ViewEventSeries = 'event_series.view';
3536
case ViewPrivateEventSeries = 'event_series.view_private';
@@ -39,6 +40,7 @@ enum Ability: string
3940
case AddDocumentsToEventSeries = 'event_series.documents.create';
4041
case EditDocumentsOfEventSeries = 'event_series.documents.edit';
4142
case DeleteDocumentsOfEventSeries = 'event_series.documents.delete';
43+
case ViewResponsibilitiesOfEventSeries = 'event_series.responsibilities.view';
4244

4345
case ViewLocations = 'locations.view';
4446
case CreateLocations = 'locations.create';
@@ -51,6 +53,7 @@ enum Ability: string
5153
case AddDocumentsToOrganizations = 'organizations.documents.create';
5254
case EditDocumentsOfOrganizations = 'organizations.documents.edit';
5355
case DeleteDocumentsOfOrganizations = 'organizations.documents.delete';
56+
case ViewResponsibilitiesOfOrganizations = 'organizations.responsibilities.view';
5457

5558
case ViewDocuments = 'documents.view';
5659

@@ -87,6 +90,7 @@ public function getTranslatedName(): string
8790
self::AddDocumentsToEvents => __('Add documents to events'),
8891
self::EditDocumentsOfEvents => __('Update documents of events'),
8992
self::DeleteDocumentsOfEvents => __('Delete documents of events'),
93+
self::ViewResponsibilitiesOfEvents => __('View responsibilities of events'),
9094

9195
self::ViewEventSeries => __('View event series'),
9296
self::ViewPrivateEventSeries => __('View private event series'),
@@ -96,6 +100,7 @@ public function getTranslatedName(): string
96100
self::AddDocumentsToEventSeries => __('Add documents to event series'),
97101
self::EditDocumentsOfEventSeries => __('Update documents of event series'),
98102
self::DeleteDocumentsOfEventSeries => __('Delete documents of event series'),
103+
self::ViewResponsibilitiesOfEventSeries => __('View responsibilities of event series'),
99104

100105
self::ViewLocations => __('View locations'),
101106
self::CreateLocations => __('Create locations'),
@@ -108,6 +113,7 @@ public function getTranslatedName(): string
108113
self::AddDocumentsToOrganizations => __('Add documents to organizations'),
109114
self::EditDocumentsOfOrganizations => __('Update documents of organizations'),
110115
self::DeleteDocumentsOfOrganizations => __('Delete documents of organizations'),
116+
self::ViewResponsibilitiesOfOrganizations => __('View responsibilities of organizations'),
111117

112118
self::ViewDocuments => __('View documents'),
113119

app/Policies/EventPolicy.php

+14
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ public function viewGroups(User $user, Event $event): Response
4444
return $this->requireAbility($user, Ability::ViewBookingsOfEvent);
4545
}
4646

47+
public function viewResponsibilities(?User $user, Event $event): Response
48+
{
49+
$viewResponse = $this->view($user, $event);
50+
if ($viewResponse->denied()) {
51+
return $viewResponse;
52+
}
53+
54+
return $this->requireAbilityOrCheck(
55+
$user,
56+
Ability::ViewResponsibilitiesOfEvents,
57+
fn () => $this->response($event->hasPubliclyVisibleResponsibleUsers())
58+
);
59+
}
60+
4761
/**
4862
* Determine whether the user can create models.
4963
*/

app/Policies/EventSeriesPolicy.php

+14
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ public function view(?User $user, EventSeries $eventSeries): Response
3838
return $this->requireAbility($user, Ability::ViewPrivateEventSeries);
3939
}
4040

41+
public function viewResponsibilities(?User $user, EventSeries $eventSeries): Response
42+
{
43+
$viewResponse = $this->view($user, $eventSeries);
44+
if ($viewResponse->denied()) {
45+
return $viewResponse;
46+
}
47+
48+
return $this->requireAbilityOrCheck(
49+
$user,
50+
Ability::ViewResponsibilitiesOfEventSeries,
51+
fn () => $this->response($eventSeries->hasPubliclyVisibleResponsibleUsers())
52+
);
53+
}
54+
4155
/**
4256
* Determine whether the user can create models.
4357
*/

app/Policies/OrganizationPolicy.php

+14
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,20 @@ public function view(User $user, Organization $organization): Response
2828
return $this->viewAny($user);
2929
}
3030

31+
public function viewResponsibilities(?User $user, Organization $organization): Response
32+
{
33+
$viewResponse = $this->view($user, $organization);
34+
if ($viewResponse->denied()) {
35+
return $viewResponse;
36+
}
37+
38+
return $this->requireAbilityOrCheck(
39+
$user,
40+
Ability::ViewResponsibilitiesOfOrganizations,
41+
fn () => $this->response($organization->hasPubliclyVisibleResponsibleUsers())
42+
);
43+
}
44+
3145
/**
3246
* Determine whether the user can create models.
3347
*/

app/Policies/Traits/ChecksAbilities.php

+13
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use App\Models\User;
66
use App\Options\Ability;
7+
use Closure;
78
use Illuminate\Auth\Access\HandlesAuthorization;
89
use Illuminate\Auth\Access\Response;
910

@@ -24,4 +25,16 @@ public function requireAbility(?User $user, Ability $ability): Response
2425

2526
return $this->response($user->hasAbility($ability));
2627
}
28+
29+
/**
30+
* @param Closure(): Response $closure
31+
*/
32+
public function requireAbilityOrCheck(?User $user, Ability $ability, Closure $closure): Response
33+
{
34+
$abilityResponse = $this->requireAbility($user, $ability);
35+
36+
return $abilityResponse->allowed()
37+
? $abilityResponse
38+
: $closure();
39+
}
2740
}

database/migrations/2024_05_03_104055_create_user_responsibilities_table.php.php

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public function up(): void
1616
$table->id();
1717
$table->foreignId('user_id')->constrained('users');
1818
$table->numericMorphs('responsible_for', 'user_responsibilities_responsible_for_index');
19+
$table->boolean('publicly_visible');
1920
$table->string('position')->nullable();
2021
$table->unsignedInteger('sort')->nullable();
2122
$table->timestamps();

lang/de.json

+4
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@
231231
"Privacy": "Datenschutz",
232232
"private": "privat",
233233
"public": "öffentlich",
234+
"publicly visible": "öffentlich sichtbar",
234235
"radio buttons": "Radio-Buttons",
235236
"randomized": "zufallsbasiert",
236237
"randomized and age-based": "zufalls- und altersbasiert",
@@ -314,6 +315,9 @@
314315
"View payment status": "Zahlungsstatus ansehen",
315316
"View private event series": "Private Veranstaltungsreihen ansehen",
316317
"View private events": "Private Veranstaltungen ansehen",
318+
"View responsibilities of event series": "Verantwortlichkeiten von Verânstaltungsreihen ansehen",
319+
"View responsibilities of events": "Verantwortlichkeiten von Verânstaltungen ansehen",
320+
"View responsibilities of organizations": "Verantwortlichkeiten von Organisationen ansehen",
317321
"View user roles": "Benutzerrollen ansehen",
318322
"View users": "Benutzer ansehen",
319323
"Visibility": "Sichtbarkeit",

lang/de/validation.php

+2
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,10 @@
203203
'password' => 'Passwort',
204204
'password_confirmation' => 'Passwort Bestätigung',
205205
'phone' => 'Telefonnummer',
206+
'position' => 'Position',
206207
'postal_code' => 'Postleitzahl',
207208
'price' => 'Preis',
209+
'publicly_visible' => 'öffentlich sichtbar',
208210
'register_entry' => 'Registereintrag',
209211
'representatives' => 'Vertreter',
210212
'restrictions' => 'Einschränkungen',

resources/views/event_series/event_series_index.blade.php

+11-9
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,17 @@
8989
<x-bs::badge>{{ formatInt($eventSeriesItem->events_count) }}</x-bs::badge>
9090
</x-slot:end>
9191
</x-bs::list.item>
92-
<x-bs::list.item>
93-
<span class="text-nowrap"><i class="fa fa-fw fa-list-check"></i> {{ __('Responsibilities') }}</span>
94-
<x-slot:end>
95-
@include('users.shared.responsible_user_span', [
96-
'class' => 'text-end ms-2',
97-
'users' => $eventSeriesItem->responsibleUsers,
98-
])
99-
</x-slot:end>
100-
</x-bs::list.item>
92+
@can('viewResponsibilities', $eventSeriesItem)
93+
<x-bs::list.item>
94+
<span class="text-nowrap"><i class="fa fa-fw fa-list-check"></i> {{ __('Responsibilities') }}</span>
95+
<x-slot:end>
96+
@include('users.shared.responsible_user_span', [
97+
'class' => 'text-end ms-2',
98+
'users' => $eventSeriesItem->getResponsibleUsersVisibleForCurrentUser(),
99+
])
100+
</x-slot:end>
101+
</x-bs::list.item>
102+
@endcan
101103
<x-bs::list.item>
102104
<span class="text-nowrap">
103105
<i class="fa fa-fw fa-file"></i>

resources/views/event_series/event_series_show.blade.php

+17-7
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,24 @@
7676
'col-12 col-xl-6 col-xxl-4',
7777
'mt-4 mt-xxl-0' => $hasSubEventSeriesToShow,
7878
])>
79-
<section id="responsibilities">
80-
<h2>{{ __('Responsibilities') }}</h2>
81-
@include('users.shared.responsible_user_list', [
82-
'users' => $eventSeries->responsibleUsers,
83-
])
84-
</section>
79+
@php
80+
$responsibilitySectionEmpty = true;
81+
@endphp
82+
@can('viewResponsibilities', $eventSeries)
83+
@php
84+
$responsibilitySectionEmpty = false;
85+
@endphp
86+
<section id="responsibilities">
87+
<h2>{{ __('Responsibilities') }}</h2>
88+
@include('users.shared.responsible_user_list', [
89+
'users' => $eventSeries->getResponsibleUsersVisibleForCurrentUser(),
90+
])
91+
</section>
92+
@endcan
8593
@canany(['viewAny', 'create'], [\App\Models\Document::class, $eventSeries])
86-
<section id="documents" class="mt-4">
94+
<section id="documents" @class([
95+
'mt-4' => !$responsibilitySectionEmpty,
96+
])>
8797
<h2>{{ __('Documents') }}</h2>
8898
@can('viewAny', [\App\Models\Document::class, $eventSeries])
8999
@include('documents.shared.document_list', [

0 commit comments

Comments
 (0)