Skip to content

Commit 6a552b6

Browse files
authored
✨ add more moderation tools to backend (#3157)
1 parent 9237f80 commit 6a552b6

File tree

11 files changed

+214
-85
lines changed

11 files changed

+214
-85
lines changed

app/Http/Controllers/API/v1/StatusController.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,11 @@ public function update(Request $request, int $statusId): JsonResponse {
445445
'visibility' => StatusVisibility::from($validated['visibility']),
446446
];
447447

448+
if($status->lock_visibility) {
449+
// If moderation has locked the visibility, prevent the user from changing it
450+
unset($updatePayload['visibility']);
451+
}
452+
448453
if (array_key_exists('eventId', $validated)) { // don't use isset here as it would return false if eventId is null
449454
$updatePayload['event_id'] = $validated['eventId'];
450455
}

app/Http/Controllers/Frontend/Admin/StatusEditController.php

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,16 @@ public function renderEdit(Request $request): View {
5050

5151
public function edit(Request $request): RedirectResponse {
5252
$validated = $request->validate([
53-
'statusId' => ['required', 'exists:statuses,id'],
54-
'origin' => ['required', 'exists:train_stations,id'],
55-
'destination' => ['required', 'exists:train_stations,id'],
56-
'body' => ['nullable', 'string'],
57-
'visibility' => ['required', new Enum(StatusVisibility::class)],
58-
'event_id' => ['nullable', 'integer', 'exists:events,id'],
59-
'points' => ['nullable', 'integer', 'gte:0'], //if null, points will be recalculated
53+
'statusId' => ['required', 'exists:statuses,id'],
54+
'origin' => ['required', 'exists:train_stations,id'],
55+
'destination' => ['required', 'exists:train_stations,id'],
56+
'body' => ['nullable', 'string'],
57+
'visibility' => ['required', new Enum(StatusVisibility::class)],
58+
'event_id' => ['nullable', 'integer', 'exists:events,id'],
59+
'points' => ['nullable', 'integer', 'gte:0'], //if null, points will be recalculated
60+
'moderation_notes' => ['nullable', 'string', 'max:255'],
61+
'lock_visibility' => ['nullable', 'boolean'],
62+
'hide_body' => ['nullable', 'boolean'],
6063
]);
6164

6265
$status = Status::find($validated['statusId']);
@@ -102,16 +105,28 @@ public function edit(Request $request): RedirectResponse {
102105

103106
StatusUpdateEvent::dispatch($status->refresh());
104107

105-
$status->update([
106-
'visibility' => $validated['visibility'],
107-
'event_id' => $validated['event_id'],
108-
]);
108+
$payload = [
109+
'visibility' => $validated['visibility'],
110+
'event_id' => $validated['event_id'],
111+
];
109112

110113
if ($status->body !== $validated['body']) {
111-
$status->update(['body' => $validated['body']]);
114+
$payload['body'] = $validated['body'];
112115
}
116+
if (isset($validated['moderation_notes'])) {
117+
$payload['moderation_notes'] = $validated['moderation_notes'];
118+
}
119+
if (isset($validated['lock_visibility'])) {
120+
$payload['lock_visibility'] = $validated['lock_visibility'];
121+
}
122+
if (isset($validated['hide_body'])) {
123+
$payload['hide_body'] = $validated['hide_body'];
124+
}
125+
126+
$status->update($payload);
113127

114-
return redirect()->route('admin.status')->with('alert-success', 'Der Status wurde bearbeitet.');
128+
return redirect()->route('admin.status.edit', ['statusId' => $status->id])
129+
->with('alert-success', 'Status successfully updated');
115130
}
116131

117132
}

app/Http/Controllers/Frontend/Transport/StatusController.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
class StatusController extends Controller
2323
{
2424

25+
/**
26+
* @deprecated Use API endpoint instead
27+
*/
2528
public function updateStatus(Request $request): JsonResponse|RedirectResponse {
2629
$validated = $request->validate([
2730
'statusId' => ['required', 'exists:statuses,id'],
@@ -44,11 +47,18 @@ public function updateStatus(Request $request): JsonResponse|RedirectResponse {
4447
return back()->with('error', 'You are not allowed to update non-private statuses. Please set the status to private.');
4548
}
4649

47-
$status->update([
48-
'body' => $validated['body'] ?? null,
49-
'business' => Business::from($validated['business_check']),
50-
'visibility' => $newVisibility,
51-
]);
50+
$statusPayload = [
51+
'body' => $validated['body'] ?? null,
52+
'business' => Business::from($validated['business_check']),
53+
'visibility' => $newVisibility,
54+
];
55+
56+
if($status->lock_visibility) {
57+
// If moderation has locked the visibility, prevent the user from changing it
58+
unset($statusPayload['visibility']);
59+
}
60+
61+
$status->update($statusPayload);
5262

5363
$status->checkin->update([
5464
'manual_departure' => isset($validated['manualDeparture']) ?

app/Models/Status.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
* @property StatusVisibility visibility
2222
* @property int event_id
2323
* @property string mastodon_post_id
24+
* @property int client_id
25+
* @property string moderation_notes Notes from the moderation team - visible to the user
26+
* @property bool lock_visibility Prevent the user from changing the visibility of the status?
27+
* @property bool hide_body Hide the body of the status from other users?
2428
*
2529
* //relations
2630
* @property User $user
@@ -39,7 +43,10 @@ class Status extends Model
3943

4044
use HasFactory;
4145

42-
protected $fillable = ['user_id', 'body', 'business', 'visibility', 'event_id', 'mastodon_post_id', 'client_id'];
46+
protected $fillable = [
47+
'user_id', 'body', 'business', 'visibility', 'event_id', 'mastodon_post_id', 'client_id',
48+
'moderation_notes', 'lock_visibility', 'hide_body',
49+
];
4350
protected $hidden = ['user_id', 'business'];
4451
protected $appends = ['favorited', 'statusInvisibleToMe', 'description'];
4552
protected $casts = [
@@ -49,7 +56,10 @@ class Status extends Model
4956
'visibility' => StatusVisibility::class,
5057
'event_id' => 'integer',
5158
'mastodon_post_id' => 'string',
52-
'client_id' => 'integer'
59+
'client_id' => 'integer',
60+
'moderation_notes' => 'string',
61+
'lock_visibility' => 'boolean',
62+
'hide_body' => 'boolean'
5363
];
5464

5565
public function user(): BelongsTo {

app/Policies/StatusPolicy.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ public function view(?User $user, Status $status): Response|bool {
7373
return Response::deny('Congratulations! You\'ve found an edge-case!');
7474
}
7575

76+
public function viewBody(?User $user, Status $status): Response|bool {
77+
if($user?->id === $status->user_id) {
78+
return Response::allow();
79+
}
80+
return !$status->hide_body && $this->view($user, $status);
81+
}
82+
7683
public function create(User $user): bool {
7784
return $user->cannot('disallow-status-creation');
7885
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
10+
public function up(): void {
11+
Schema::table('statuses', function(Blueprint $table) {
12+
$table->string('moderation_notes')->nullable()
13+
->after('client_id')
14+
->comment('Notes from the moderation team - visible to the user');
15+
16+
$table->boolean('lock_visibility')
17+
->default(false)
18+
->after('moderation_notes')
19+
->comment('Prevent the user from changing the visibility of the status?');
20+
21+
$table->boolean('hide_body')
22+
->default(false)
23+
->after('lock_visibility')
24+
->comment('Hide the body of the status from other users?');
25+
});
26+
}
27+
28+
public function down(): void {
29+
Schema::table('statuses', function(Blueprint $table) {
30+
$table->dropColumn(['moderation_notes', 'lock_visibility', 'hide_body']);
31+
});
32+
}
33+
};

lang/de.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -744,8 +744,7 @@
744744
"missing-journey": "Ist deine Fahrt nicht dabei?",
745745
"create-journey": "Fahrt erstellen",
746746
"trip_creation.no-valid-times": "Die Zeiten der Stationen sind nicht in einer zeitlich korrekten Reihenfolge.",
747-
"trip_creation.title": "Reise manuell erstellen",
748-
"trip_creation.form.trip_data": "Fahrtinformationen",
747+
"trip_creation.title": "Reise manuell erstellen","trip_creation.form.trip_data": "Fahrtinformationen",
749748
"trip_creation.form.map": "Karte",
750749
"trip_creation.form.origin": "Abfahrtsbahnhof",
751750
"trip_creation.form.destination": "Zielbahnhof",
@@ -828,5 +827,7 @@
828827
"trip-info.user": "User",
829828
"trip-info.origin": "Abfahrtsort",
830829
"trip-info.destination": "Zielort",
831-
"requested-timestamp": "Abfragezeitpunkt"
830+
"requested-timestamp": "Abfragezeitpunkt",
831+
"status.locked-visibility": "Du kannst die Sichtbarkeit dieses Status nicht ändern.",
832+
"status.hidden-body": "Der Statustext ist für andere User nicht sichtbar."
832833
}

lang/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,5 +828,7 @@
828828
"trip-info.user": "User",
829829
"trip-info.origin": "Origin",
830830
"trip-info.destination": "Destination",
831-
"requested-timestamp": "Requested timestamp"
831+
"requested-timestamp": "Requested timestamp",
832+
"status.locked-visibility": "You cannot change the visibility of this status.",
833+
"status.hidden-body": "The status text is not visible to other users."
832834
}

resources/js/admin.js

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,9 @@ import "./components/maps";
55
import * as Popper from "@popperjs/core";
66
import "bootstrap";
77
import "leaflet";
8-
import {createApp} from "vue";
9-
import TripCreationForm from "../vue/components/TripCreation/TripCreationForm.vue";
108

119
window.addEventListener("load", () => {
1210
import("./components/station-autocomplete");
1311
});
1412

1513
window.Popper = Popper;
16-
17-
document.addEventListener("DOMContentLoaded", function () {
18-
const admin = createApp({});
19-
admin.component("TripCreationForm", TripCreationForm);
20-
admin.mount("#trip-creation-form");
21-
});

0 commit comments

Comments
 (0)