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 +

+ +

+ {{ $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') +
+ @csrf +
+
+
+

+

Add child

+
+
+ +
+ +
+

Choose your child's policies

+ +
+

Allowed Actions

+ + @include('settings.parental-controls.checkbox', ['name' => 'post', 'title' => 'Post', 'checked' => true]) + @include('settings.parental-controls.checkbox', ['name' => 'comment', 'title' => 'Comment', 'checked' => true]) + @include('settings.parental-controls.checkbox', ['name' => 'like', 'title' => 'Like', 'checked' => true]) + @include('settings.parental-controls.checkbox', ['name' => 'share', 'title' => 'Share', 'checked' => true]) + @include('settings.parental-controls.checkbox', ['name' => 'follow', 'title' => 'Follow']) + @include('settings.parental-controls.checkbox', ['name' => 'bookmark', 'title' => 'Bookmark']) + @include('settings.parental-controls.checkbox', ['name' => 'story', 'title' => 'Add to story']) + @include('settings.parental-controls.checkbox', ['name' => 'collection', 'title' => 'Add to collection']) +
+
+

Enabled features

+ + @include('settings.parental-controls.checkbox', ['name' => 'discovery_feeds', 'title' => 'Discovery Feeds']) + @include('settings.parental-controls.checkbox', ['name' => 'dms', 'title' => 'Direct Messages']) + @include('settings.parental-controls.checkbox', ['name' => 'federation', 'title' => 'Federation']) +
+
+

Preferences

+ + @include('settings.parental-controls.checkbox', ['name' => 'hide_network', 'title' => 'Hide my child\'s connections']) + @include('settings.parental-controls.checkbox', ['name' => 'private', 'title' => 'Make my child\'s account private']) + @include('settings.parental-controls.checkbox', ['name' => 'hide_cw', 'title' => 'Hide sensitive media']) +
+
+ +
+
+ +

Where should we send this invite?

+ +
+ + +
+
+
+@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
+ + +
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') +
+ @csrf +
+
+
+

+
+

Cancel child invite

+

Last updated: {{ $pc->updated_at->diffForHumans() }}

+
+
+
+
+
+
+ +
+

+ +

+

Are you sure you want to cancel this invite?

+

The child you invited will not be able to join if you cancel the invite.

+
+ + +
+
+ +@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') +
+
+
+

+

Parental Controls

+
+
+ +
+ + @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') +
+
+
+ +
+
Create your Account
+ +
+
+ @csrf + + +
+
+ + + + @if ($errors->has('name')) + + {{ $errors->first('name') }} + + @endif +
+
+ +
+
+ + + + @if ($errors->has('username')) + + {{ $errors->first('username') }} + + @endif +
+
+ +
+
+ + + + @if ($errors->has('password')) + + {{ $errors->first('password') }} + + @endif +
+
+ +
+
+ + +
+
+ +
+
+
+ + +
+
+
+ + @if(config('captcha.enabled') || config('captcha.active.register')) +
+ {!! Captcha::display() !!} +
+ @endif + +

By signing up, you agree to our Terms of Use and Privacy Policy, in addition, you understand that your account is managed by {{ $pc->parent->username }} and they can limit your account without your permission. For more details, view the Parental Controls help center page.

+ +
+
+ +
+
+
+
+
+
+
+
+@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') +
+ @csrf +
+
+
+

+
+

Manage child

+

Last updated: {{ $pc->updated_at->diffForHumans() }}

+
+
+ + +
+ +
+ +
+ +
+
+
+
+
+ @if(!$pc->child_id && !$pc->email_verified_at) + @include('settings.parental-controls.child-status', ['state' => 'sent_invite']) + @elseif($pc->child_id && !$pc->email_verified_at) + @include('settings.parental-controls.child-status', ['state' => 'awaiting_email_confirmation']) + @elseif($pc->child_id && $pc->email_verified_at) + @include('settings.parental-controls.child-status', ['state' => 'active']) + @else + @include('settings.parental-controls.child-status', ['state' => 'sent_invite']) + @endif +
+
+
+

Allowed Actions

+ + @include('settings.parental-controls.checkbox', ['name' => 'post', 'title' => 'Post', 'checked' => $pc->permissions['post']]) + @include('settings.parental-controls.checkbox', ['name' => 'comment', 'title' => 'Comment', 'checked' => $pc->permissions['comment']]) + @include('settings.parental-controls.checkbox', ['name' => 'like', 'title' => 'Like', 'checked' => $pc->permissions['like']]) + @include('settings.parental-controls.checkbox', ['name' => 'share', 'title' => 'Share', 'checked' => $pc->permissions['share']]) + @include('settings.parental-controls.checkbox', ['name' => 'follow', 'title' => 'Follow', 'checked' => $pc->permissions['follow']]) + @include('settings.parental-controls.checkbox', ['name' => 'bookmark', 'title' => 'Bookmark', 'checked' => $pc->permissions['bookmark']]) + @include('settings.parental-controls.checkbox', ['name' => 'story', 'title' => 'Add to story', 'checked' => $pc->permissions['story']]) + @include('settings.parental-controls.checkbox', ['name' => 'collection', 'title' => 'Add to collection', 'checked' => $pc->permissions['collection']]) +
+
+

Enabled features

+ + @include('settings.parental-controls.checkbox', ['name' => 'discovery_feeds', 'title' => 'Discovery Feeds', 'checked' => $pc->permissions['discovery_feeds']]) + @include('settings.parental-controls.checkbox', ['name' => 'dms', 'title' => 'Direct Messages', 'checked' => $pc->permissions['dms']]) + @include('settings.parental-controls.checkbox', ['name' => 'federation', 'title' => 'Federation', 'checked' => $pc->permissions['federation']]) +
+
+

Preferences

+ + @include('settings.parental-controls.checkbox', ['name' => 'hide_network', 'title' => 'Hide my child\'s connections', 'checked' => $pc->permissions['hide_network']]) + @include('settings.parental-controls.checkbox', ['name' => 'private', 'title' => 'Make my child\'s account private', 'checked' => $pc->permissions['private']]) + @include('settings.parental-controls.checkbox', ['name' => 'hide_cw', 'title' => 'Hide sensitive media', 'checked' => $pc->permissions['hide_cw']]) +
+
+
+
+
+ + +
+
+
+
+
+ @if(!$pc->child_id && !$pc->email_verified_at) +
+

Cancel Invite

+

Cancel the child invite and prevent it from being used.

+ Cancel Invite +
+ @else +
+

Stop Managing

+

Transition account to a regular account without parental controls.

+ Stop Managing Child +
+ @endif +
+
+
+
+ +
+
+@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') +
+ @csrf +
+
+
+

+
+

Stop Managing Child

+

Last updated: {{ $pc->updated_at->diffForHumans() }}

+
+
+
+
+
+
+ +
+

+ +

+

Confirm Stop Managing this Account?

+

This child account will be transitioned to a regular account without any limitations.

+
+ + +
+
+ +@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:

+ + +
+ + +
+ @if(config('instance.parental_controls.enabled')) +
    +
  1. Click here and tap on the Add Child button in the bottom left corner
  2. +
  3. Select the Allowed Actions, Enabled features and Preferences
  4. +
  5. Enter your childs email address
  6. +
  7. Press the Add Child buttton
  8. +
  9. Open your childs email and tap on the Accept Invite button in the email, ensure your parent username is present in the email
  10. +
  11. Fill out the child display name, username and password
  12. +
  13. Press Register and your child account will be active!
  14. +
+ @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');