From c53894fe16a6a147d4d6710f3a04c0780d350b4a Mon Sep 17 00:00:00 2001
From: Daniel Supernault
Date: Thu, 11 Jan 2024 01:35:15 -0700
Subject: [PATCH] Add Parental Controls feature
---
.../Controllers/Auth/RegisterController.php | 4 +-
.../ParentalControlsController.php | 207 ++++++++++++++++++
.../DispatchChildInvitePipeline.php | 38 ++++
app/Mail/ParentChildInvite.php | 49 +++++
app/Models/ParentalControls.php | 55 +++++
app/Services/UserRoleService.php | 72 ++++++
config/instance.php | 12 +-
..._052419_create_parental_controls_table.php | 45 ++++
resources/views/components/collapse.blade.php | 12 +
.../emails/parental-controls/invite.blade.php | 18 ++
.../settings/parental-controls/add.blade.php | 59 +++++
.../parental-controls/checkbox.blade.php | 7 +
.../parental-controls/child-status.blade.php | 44 ++++
.../parental-controls/delete-invite.blade.php | 32 +++
.../parental-controls/index.blade.php | 62 ++++++
.../invite-register-form.blade.php | 115 ++++++++++
.../parental-controls/manage.blade.php | 119 ++++++++++
.../parental-controls/stop-managing.blade.php | 32 +++
.../site/help/parental-controls.blade.php | 47 ++++
routes/web.php | 13 ++
20 files changed, 1039 insertions(+), 3 deletions(-)
create mode 100644 app/Http/Controllers/ParentalControlsController.php
create mode 100644 app/Jobs/ParentalControlsPipeline/DispatchChildInvitePipeline.php
create mode 100644 app/Mail/ParentChildInvite.php
create mode 100644 app/Models/ParentalControls.php
create mode 100644 database/migrations/2024_01_09_052419_create_parental_controls_table.php
create mode 100644 resources/views/components/collapse.blade.php
create mode 100644 resources/views/emails/parental-controls/invite.blade.php
create mode 100644 resources/views/settings/parental-controls/add.blade.php
create mode 100644 resources/views/settings/parental-controls/checkbox.blade.php
create mode 100644 resources/views/settings/parental-controls/child-status.blade.php
create mode 100644 resources/views/settings/parental-controls/delete-invite.blade.php
create mode 100644 resources/views/settings/parental-controls/index.blade.php
create mode 100644 resources/views/settings/parental-controls/invite-register-form.blade.php
create mode 100644 resources/views/settings/parental-controls/manage.blade.php
create mode 100644 resources/views/settings/parental-controls/stop-managing.blade.php
create mode 100644 resources/views/site/help/parental-controls.blade.php
diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php
index 5eb1159fe..8c10e5d0c 100644
--- a/app/Http/Controllers/Auth/RegisterController.php
+++ b/app/Http/Controllers/Auth/RegisterController.php
@@ -60,7 +60,7 @@ class RegisterController extends Controller
*
* @return \Illuminate\Contracts\Validation\Validator
*/
- protected function validator(array $data)
+ public function validator(array $data)
{
if(config('database.default') == 'pgsql') {
$data['username'] = strtolower($data['username']);
@@ -151,7 +151,7 @@ class RegisterController extends Controller
*
* @return \App\User
*/
- protected function create(array $data)
+ public function create(array $data)
{
if(config('database.default') == 'pgsql') {
$data['username'] = strtolower($data['username']);
diff --git a/app/Http/Controllers/ParentalControlsController.php b/app/Http/Controllers/ParentalControlsController.php
new file mode 100644
index 000000000..5c60cfae2
--- /dev/null
+++ b/app/Http/Controllers/ParentalControlsController.php
@@ -0,0 +1,207 @@
+user(), 404);
+ }
+ abort_unless(config('instance.parental_controls.enabled'), 404);
+ if(config_cache('pixelfed.open_registration') == false) {
+ abort_if(config('instance.parental_controls.limits.respect_open_registration'), 404);
+ }
+ if($maxUserCheck == true) {
+ $hasLimit = config('pixelfed.enforce_max_users');
+ if($hasLimit) {
+ $count = User::where(function($q){ return $q->whereNull('status')->orWhereNotIn('status', ['deleted','delete']); })->count();
+ $limit = (int) config('pixelfed.max_users');
+
+ abort_if($limit && $limit <= $count, 404);
+ }
+ }
+ }
+
+ public function index(Request $request)
+ {
+ $this->authPreflight($request);
+ $children = ParentalControls::whereParentId($request->user()->id)->latest()->paginate(5);
+ return view('settings.parental-controls.index', compact('children'));
+ }
+
+ public function add(Request $request)
+ {
+ $this->authPreflight($request, true);
+ return view('settings.parental-controls.add');
+ }
+
+ public function view(Request $request, $id)
+ {
+ $this->authPreflight($request);
+ $uid = $request->user()->id;
+ $pc = ParentalControls::whereParentId($uid)->findOrFail($id);
+ return view('settings.parental-controls.manage', compact('pc'));
+ }
+
+ public function update(Request $request, $id)
+ {
+ $this->authPreflight($request);
+ $uid = $request->user()->id;
+ $pc = ParentalControls::whereParentId($uid)->findOrFail($id);
+ $pc->permissions = $this->requestFormFields($request);
+ $pc->save();
+ return redirect($pc->manageUrl() . '?permissions');
+ }
+
+ public function store(Request $request)
+ {
+ $this->authPreflight($request, true);
+ $this->validate($request, [
+ 'email' => 'required|email|unique:parental_controls,email|unique:users,email',
+ ]);
+
+ $state = $this->requestFormFields($request);
+
+ $pc = new ParentalControls;
+ $pc->parent_id = $request->user()->id;
+ $pc->email = $request->input('email');
+ $pc->verify_code = str_random(32);
+ $pc->permissions = $state;
+ $pc->save();
+
+ DispatchChildInvitePipeline::dispatch($pc);
+ return redirect($pc->manageUrl());
+ }
+
+ public function inviteRegister(Request $request, $id, $code)
+ {
+ $this->authPreflight($request, true, false);
+ $pc = ParentalControls::whereRaw('verify_code = BINARY ?', $code)->whereNull(['email_verified_at', 'child_id'])->findOrFail($id);
+ abort_unless(User::whereId($pc->parent_id)->exists(), 404);
+ return view('settings.parental-controls.invite-register-form', compact('pc'));
+ }
+
+ public function inviteRegisterStore(Request $request, $id, $code)
+ {
+ $this->authPreflight($request, true, false);
+
+ $pc = ParentalControls::whereRaw('verify_code = BINARY ?', $code)->whereNull('email_verified_at')->findOrFail($id);
+
+ $fields = $request->all();
+ $fields['email'] = $pc->email;
+ $defaults = UserRoleService::defaultRoles();
+ $validator = (new RegisterController)->validator($fields);
+ $valid = $validator->validate();
+ abort_if(!$valid, 404);
+ event(new Registered($user = (new RegisterController)->create($fields)));
+ sleep(5);
+ $user->has_roles = true;
+ $user->parent_id = $pc->parent_id;
+ if(config('instance.parental_controls.limits.auto_verify_email')) {
+ $user->email_verified_at = now();
+ $user->save();
+ sleep(3);
+ } else {
+ $user->save();
+ sleep(3);
+ }
+ $ur = UserRoles::updateOrCreate([
+ 'user_id' => $user->id,
+ ],[
+ 'roles' => UserRoleService::mapInvite($user->id, $pc->permissions)
+ ]);
+ $pc->email_verified_at = now();
+ $pc->child_id = $user->id;
+ $pc->save();
+ sleep(2);
+ Auth::guard()->login($user);
+
+ return redirect('/i/web');
+ }
+
+ public function cancelInvite(Request $request, $id)
+ {
+ $this->authPreflight($request);
+ $pc = ParentalControls::whereParentId($request->user()->id)
+ ->whereNull(['email_verified_at', 'child_id'])
+ ->findOrFail($id);
+
+ return view('settings.parental-controls.delete-invite', compact('pc'));
+ }
+
+ public function cancelInviteHandle(Request $request, $id)
+ {
+ $this->authPreflight($request);
+ $pc = ParentalControls::whereParentId($request->user()->id)
+ ->whereNull(['email_verified_at', 'child_id'])
+ ->findOrFail($id);
+
+ $pc->delete();
+
+ return redirect('/settings/parental-controls');
+ }
+
+ public function stopManaging(Request $request, $id)
+ {
+ $this->authPreflight($request);
+ $pc = ParentalControls::whereParentId($request->user()->id)
+ ->whereNotNull(['email_verified_at', 'child_id'])
+ ->findOrFail($id);
+
+ return view('settings.parental-controls.stop-managing', compact('pc'));
+ }
+
+ public function stopManagingHandle(Request $request, $id)
+ {
+ $this->authPreflight($request);
+ $pc = ParentalControls::whereParentId($request->user()->id)
+ ->whereNotNull(['email_verified_at', 'child_id'])
+ ->findOrFail($id);
+ $pc->child()->update([
+ 'has_roles' => false,
+ 'parent_id' => null,
+ ]);
+ $pc->delete();
+
+ return redirect('/settings/parental-controls');
+ }
+
+ protected function requestFormFields($request)
+ {
+ $state = [];
+ $fields = [
+ 'post',
+ 'comment',
+ 'like',
+ 'share',
+ 'follow',
+ 'bookmark',
+ 'story',
+ 'collection',
+ 'discovery_feeds',
+ 'dms',
+ 'federation',
+ 'hide_network',
+ 'private',
+ 'hide_cw'
+ ];
+
+ foreach ($fields as $field) {
+ $state[$field] = $request->input($field) == 'on';
+ }
+
+ return $state;
+ }
+}
diff --git a/app/Jobs/ParentalControlsPipeline/DispatchChildInvitePipeline.php b/app/Jobs/ParentalControlsPipeline/DispatchChildInvitePipeline.php
new file mode 100644
index 000000000..a67f4e444
--- /dev/null
+++ b/app/Jobs/ParentalControlsPipeline/DispatchChildInvitePipeline.php
@@ -0,0 +1,38 @@
+pc = $pc;
+ }
+
+ /**
+ * Execute the job.
+ */
+ public function handle(): void
+ {
+ $pc = $this->pc;
+
+ Mail::to($pc->email)->send(new ParentChildInvite($pc));
+ }
+}
diff --git a/app/Mail/ParentChildInvite.php b/app/Mail/ParentChildInvite.php
new file mode 100644
index 000000000..843ea472d
--- /dev/null
+++ b/app/Mail/ParentChildInvite.php
@@ -0,0 +1,49 @@
+
+ */
+ public function attachments(): array
+ {
+ return [];
+ }
+}
diff --git a/app/Models/ParentalControls.php b/app/Models/ParentalControls.php
new file mode 100644
index 000000000..83d47c18a
--- /dev/null
+++ b/app/Models/ParentalControls.php
@@ -0,0 +1,55 @@
+ 'array',
+ 'email_sent_at' => 'datetime',
+ 'email_verified_at' => 'datetime'
+ ];
+
+ protected $guarded = [];
+
+ public function parent()
+ {
+ return $this->belongsTo(User::class, 'parent_id');
+ }
+
+ public function child()
+ {
+ return $this->belongsTo(User::class, 'child_id');
+ }
+
+ public function childAccount()
+ {
+ if($u = $this->child) {
+ if($u->profile_id) {
+ return AccountService::get($u->profile_id, true);
+ } else {
+ return [];
+ }
+ } else {
+ return [];
+ }
+ }
+
+ public function manageUrl()
+ {
+ return url('/settings/parental-controls/manage/' . $this->id);
+ }
+
+ public function inviteUrl()
+ {
+ return url('/auth/pci/' . $this->id . '/' . $this->verify_code);
+ }
+}
diff --git a/app/Services/UserRoleService.php b/app/Services/UserRoleService.php
index 500a4666e..a18810bf0 100644
--- a/app/Services/UserRoleService.php
+++ b/app/Services/UserRoleService.php
@@ -52,6 +52,13 @@ class UserRoleService
'can-follow' => false,
'can-make-public' => false,
+
+ 'can-direct-message' => false,
+ 'can-use-stories' => false,
+ 'can-view-sensitive' => false,
+ 'can-bookmark' => false,
+ 'can-collections' => false,
+ 'can-federation' => false,
];
}
@@ -114,6 +121,71 @@ class UserRoleService
'title' => 'Can make account public',
'action' => 'Allows the ability to make account public'
],
+
+ 'can-direct-message' => [
+ 'title' => '',
+ 'action' => ''
+ ],
+ 'can-use-stories' => [
+ 'title' => '',
+ 'action' => ''
+ ],
+ 'can-view-sensitive' => [
+ 'title' => '',
+ 'action' => ''
+ ],
+ 'can-bookmark' => [
+ 'title' => '',
+ 'action' => ''
+ ],
+ 'can-collections' => [
+ 'title' => '',
+ 'action' => ''
+ ],
+ 'can-federation' => [
+ 'title' => '',
+ 'action' => ''
+ ],
];
}
+
+ public static function mapInvite($id, $data = [])
+ {
+ $roles = self::get($id);
+
+ $map = [
+ 'account-force-private' => 'private',
+ 'account-ignore-follow-requests' => 'private',
+
+ 'can-view-public-feed' => 'discovery_feeds',
+ 'can-view-network-feed' => 'discovery_feeds',
+ 'can-view-discover' => 'discovery_feeds',
+ 'can-view-hashtag-feed' => 'discovery_feeds',
+
+ 'can-post' => 'post',
+ 'can-comment' => 'comment',
+ 'can-like' => 'like',
+ 'can-share' => 'share',
+
+ 'can-follow' => 'follow',
+ 'can-make-public' => '!private',
+
+ 'can-direct-message' => 'dms',
+ 'can-use-stories' => 'story',
+ 'can-view-sensitive' => '!hide_cw',
+ 'can-bookmark' => 'bookmark',
+ 'can-collections' => 'collection',
+ 'can-federation' => 'federation',
+ ];
+
+ foreach ($map as $key => $value) {
+ if(!isset($data[$value], $data[substr($value, 1)])) {
+ $map[$key] = false;
+ continue;
+ }
+ $map[$key] = str_starts_with($value, '!') ? !$data[substr($value, 1)] : $data[$value];
+ }
+
+ return $map;
+ }
}
diff --git a/config/instance.php b/config/instance.php
index 6357afe63..5e173684c 100644
--- a/config/instance.php
+++ b/config/instance.php
@@ -129,5 +129,15 @@ return [
'banner' => [
'blurhash' => env('INSTANCE_BANNER_BLURHASH', 'UzJR]l{wHZRjM}R%XRkCH?X9xaWEjZj]kAjt')
- ]
+ ],
+
+ 'parental_controls' => [
+ 'enabled' => env('INSTANCE_PARENTAL_CONTROLS', true),
+
+ 'limits' => [
+ 'respect_open_registration' => env('INSTANCE_PARENTAL_CONTROLS_RESPECT_OPENREG', true),
+ 'max_children' => env('INSTANCE_PARENTAL_CONTROLS_MAX_CHILDREN', 10),
+ 'auto_verify_email' => true,
+ ],
+ ]
];
diff --git a/database/migrations/2024_01_09_052419_create_parental_controls_table.php b/database/migrations/2024_01_09_052419_create_parental_controls_table.php
new file mode 100644
index 000000000..4ef7fd2c7
--- /dev/null
+++ b/database/migrations/2024_01_09_052419_create_parental_controls_table.php
@@ -0,0 +1,45 @@
+id();
+ $table->unsignedInteger('parent_id')->index();
+ $table->unsignedInteger('child_id')->unique()->index()->nullable();
+ $table->string('email')->unique()->nullable();
+ $table->string('verify_code')->nullable();
+ $table->timestamp('email_sent_at')->nullable();
+ $table->timestamp('email_verified_at')->nullable();
+ $table->json('permissions')->nullable();
+ $table->softDeletes();
+ $table->timestamps();
+ });
+
+ Schema::table('user_roles', function (Blueprint $table) {
+ $table->dropIndex('user_roles_profile_id_unique');
+ $table->unsignedBigInteger('profile_id')->unique()->nullable()->index()->change();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('parental_controls');
+
+ Schema::table('user_roles', function (Blueprint $table) {
+ $table->dropIndex('user_roles_profile_id_unique');
+ $table->unsignedBigInteger('profile_id')->unique()->index()->change();
+ });
+ }
+};
diff --git a/resources/views/components/collapse.blade.php b/resources/views/components/collapse.blade.php
new file mode 100644
index 000000000..144579366
--- /dev/null
+++ b/resources/views/components/collapse.blade.php
@@ -0,0 +1,12 @@
+@php
+$cid = 'col' . str_random(6);
+@endphp
+
+
+
+ {{ $title }}
+
+
+ {{ $slot }}
+
+
diff --git a/resources/views/emails/parental-controls/invite.blade.php b/resources/views/emails/parental-controls/invite.blade.php
new file mode 100644
index 000000000..bece5b1bb
--- /dev/null
+++ b/resources/views/emails/parental-controls/invite.blade.php
@@ -0,0 +1,18 @@
+
+# You've been invited to join Pixelfed!
+
+
+A parent account with the username **{{ $verify->parent->username }}** has invited you to join Pixelfed with a special youth account managed by them.
+
+If you do not recognize this account as your parents or a trusted guardian, please check with them first.
+
+
+
+Accept Invite
+
+
+Thanks,
+Pixelfed
+
+This email is automatically generated. Please do not reply to this message.
+
diff --git a/resources/views/settings/parental-controls/add.blade.php b/resources/views/settings/parental-controls/add.blade.php
new file mode 100644
index 000000000..b7ca4c7ab
--- /dev/null
+++ b/resources/views/settings/parental-controls/add.blade.php
@@ -0,0 +1,59 @@
+@extends('settings.template-vue')
+
+@section('section')
+
+@endsection
+
diff --git a/resources/views/settings/parental-controls/checkbox.blade.php b/resources/views/settings/parental-controls/checkbox.blade.php
new file mode 100644
index 000000000..b6cedbe92
--- /dev/null
+++ b/resources/views/settings/parental-controls/checkbox.blade.php
@@ -0,0 +1,7 @@
+@php
+$id = str_random(6) . '_' . str_slug($name);
+$defaultChecked = isset($checked) && $checked ? 'checked=""' : '';
+@endphp
+
+ {{ $title }}
+
diff --git a/resources/views/settings/parental-controls/child-status.blade.php b/resources/views/settings/parental-controls/child-status.blade.php
new file mode 100644
index 000000000..9852dacd7
--- /dev/null
+++ b/resources/views/settings/parental-controls/child-status.blade.php
@@ -0,0 +1,44 @@
+@if($state)
+
+ @if($state === 'sent_invite')
+
+
+
Child Invite Sent!
+
+
+
Created child invite
+
Sent invite email to child
+
Child joined via invite
+
Child account is active
+
+
+ @elseif($state === 'awaiting_email_confirmation')
+
+
+
Child Invite Sent!
+
+
+
Created child invite
+
Sent invite email to child
+
Child joined via invite
+
Child account is active
+
+
+ @elseif($state === 'active')
+
+
+
Child Account Active
+
+
+
Created child invite
+
Sent invite email to child
+
Child joined via invite
+
Child account is active
+
+
+
View Account
+
+ @endif
+
+@else
+@endif
diff --git a/resources/views/settings/parental-controls/delete-invite.blade.php b/resources/views/settings/parental-controls/delete-invite.blade.php
new file mode 100644
index 000000000..36b66ab15
--- /dev/null
+++ b/resources/views/settings/parental-controls/delete-invite.blade.php
@@ -0,0 +1,32 @@
+@extends('settings.template-vue')
+
+@section('section')
+
+
+@endsection
diff --git a/resources/views/settings/parental-controls/index.blade.php b/resources/views/settings/parental-controls/index.blade.php
new file mode 100644
index 000000000..a99093f33
--- /dev/null
+++ b/resources/views/settings/parental-controls/index.blade.php
@@ -0,0 +1,62 @@
+@extends('settings.template-vue')
+
+@section('section')
+
+
+
+
+
+ @if($children->count())
+
+
+
+
+ {{ $children->links() }}
+
+
+ @else
+
+
You are not managing any children accounts.
+
+ @endif
+
+
+
+ Add Child
+
+
+
+ {{ $children->total() }}/{{ config('instance.parental_controls.limits.max_children') }}
+ children added
+
+
+
+
+@endsection
+
diff --git a/resources/views/settings/parental-controls/invite-register-form.blade.php b/resources/views/settings/parental-controls/invite-register-form.blade.php
new file mode 100644
index 000000000..5b894e8d2
--- /dev/null
+++ b/resources/views/settings/parental-controls/invite-register-form.blade.php
@@ -0,0 +1,115 @@
+@extends('layouts.app')
+
+@section('content')
+
+@endsection
diff --git a/resources/views/settings/parental-controls/manage.blade.php b/resources/views/settings/parental-controls/manage.blade.php
new file mode 100644
index 000000000..a6cc62119
--- /dev/null
+++ b/resources/views/settings/parental-controls/manage.blade.php
@@ -0,0 +1,119 @@
+@extends('settings.template-vue')
+
+@section('section')
+
+@endsection
+
+@push('scripts')
+
+@endpush
diff --git a/resources/views/settings/parental-controls/stop-managing.blade.php b/resources/views/settings/parental-controls/stop-managing.blade.php
new file mode 100644
index 000000000..747339fd8
--- /dev/null
+++ b/resources/views/settings/parental-controls/stop-managing.blade.php
@@ -0,0 +1,32 @@
+@extends('settings.template-vue')
+
+@section('section')
+
+
+@endsection
diff --git a/resources/views/site/help/parental-controls.blade.php b/resources/views/site/help/parental-controls.blade.php
new file mode 100644
index 000000000..d7b9710dd
--- /dev/null
+++ b/resources/views/site/help/parental-controls.blade.php
@@ -0,0 +1,47 @@
+@extends('site.help.partial.template', ['breadcrumb'=>'Parental Controls'])
+
+@section('section')
+
+
Parental Controls
+
+
+
+ In the digital age, ensuring your children's online safety is paramount. Designed with both fun and safety in mind, this feature allows parents to create child accounts, tailor-made for a worry-free social media experience.
+
+ Key Features:
+
+
+ Child Account Creation : Easily set up a child account with just a few clicks. This account is linked to your own, giving you complete oversight.
+ Post Control : Decide if your child can post content. This allows you to ensure they're only sharing what's appropriate and safe.
+ Comment Management : Control whether your child can comment on posts. This helps in safeguarding them from unwanted interactions and maintaining a positive online environment.
+ Like & Share Restrictions : You have the power to enable or disable the ability to like and share posts. This feature helps in controlling the extent of your child's social media engagement.
+ Disable Federation : For added safety, you can choose to disable federation for your child's account, limiting their interaction to a more controlled environment.
+
+
+
+
+
+ @if(config('instance.parental_controls.enabled'))
+
+ Click here and tap on the Add Child button in the bottom left corner
+ Select the Allowed Actions, Enabled features and Preferences
+ Enter your childs email address
+ Press the Add Child buttton
+ Open your childs email and tap on the Accept Invite button in the email, ensure your parent username is present in the email
+ Fill out the child display name, username and password
+ Press Register and your child account will be active!
+
+ @else
+
This feature has been disabled by server admins.
+ @endif
+
+
+
+@if(config('instance.parental_controls.enabled'))
+
+
+ You can create and manage up to {{ config('instance.parental_controls.limits.max_children') }} child accounts.
+
+
+@endif
+@endsection
diff --git a/routes/web.php b/routes/web.php
index b8149a605..dffd5e271 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -200,6 +200,8 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::post('auth/raw/mastodon/s/account-to-id', 'RemoteAuthController@accountToId');
Route::post('auth/raw/mastodon/s/finish-up', 'RemoteAuthController@finishUp');
Route::post('auth/raw/mastodon/s/login', 'RemoteAuthController@handleLogin');
+ Route::get('auth/pci/{id}/{code}', 'ParentalControlsController@inviteRegister');
+ Route::post('auth/pci/{id}/{code}', 'ParentalControlsController@inviteRegisterStore');
Route::get('discover', 'DiscoverController@home')->name('discover');
@@ -534,6 +536,16 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
});
+ Route::get('parental-controls', 'ParentalControlsController@index');
+ Route::get('parental-controls/add', 'ParentalControlsController@add')->name('settings.pc.add');
+ Route::post('parental-controls/add', 'ParentalControlsController@store');
+ Route::get('parental-controls/manage/{id}', 'ParentalControlsController@view');
+ Route::post('parental-controls/manage/{id}', 'ParentalControlsController@update');
+ Route::get('parental-controls/manage/{id}/cancel-invite', 'ParentalControlsController@cancelInvite')->name('settings.pc.cancel-invite');
+ Route::post('parental-controls/manage/{id}/cancel-invite', 'ParentalControlsController@cancelInviteHandle');
+ Route::get('parental-controls/manage/{id}/stop-managing', 'ParentalControlsController@stopManaging')->name('settings.pc.stop-managing');
+ Route::post('parental-controls/manage/{id}/stop-managing', 'ParentalControlsController@stopManagingHandle');
+
Route::get('applications', 'SettingsController@applications')->name('settings.applications')->middleware('dangerzone');
Route::get('data-export', 'SettingsController@dataExport')->name('settings.dataexport')->middleware('dangerzone');
Route::post('data-export/following', 'SettingsController@exportFollowing')->middleware('dangerzone');
@@ -618,6 +630,7 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
Route::view('licenses', 'site.help.licenses')->name('help.licenses');
Route::view('instance-max-users-limit', 'site.help.instance-max-users')->name('help.instance-max-users-limit');
Route::view('import', 'site.help.import')->name('help.import');
+ Route::view('parental-controls', 'site.help.parental-controls');
});
Route::get('newsroom/{year}/{month}/{slug}', 'NewsroomController@show');
Route::get('newsroom/archive', 'NewsroomController@archive');