From c5e7e917779894b0356b29a18be984ecf2374714 Mon Sep 17 00:00:00 2001 From: Daniel Supernault Date: Tue, 4 Jun 2024 03:46:06 -0600 Subject: [PATCH] Add disable_embeds setting, and fix cache invalidation in other settings --- app/Http/Controllers/Api/ApiV1Controller.php | 15 +- app/Http/Controllers/ProfileController.php | 2 +- .../Controllers/Settings/HomeSettings.php | 43 +- .../Controllers/Settings/PrivacySettings.php | 19 + app/Http/Controllers/SettingsController.php | 563 +++++++++--------- app/Http/Controllers/StatusController.php | 8 + app/Services/AccountService.php | 200 ++++--- resources/views/settings/privacy.blade.php | 8 + 8 files changed, 475 insertions(+), 383 deletions(-) diff --git a/app/Http/Controllers/Api/ApiV1Controller.php b/app/Http/Controllers/Api/ApiV1Controller.php index 01c040e0f..58a155e84 100644 --- a/app/Http/Controllers/Api/ApiV1Controller.php +++ b/app/Http/Controllers/Api/ApiV1Controller.php @@ -194,19 +194,7 @@ class ApiV1Controller extends Controller ]; if ($request->has(self::PF_API_ENTITY_KEY)) { - $settings = $user->settings; - $other = array_merge(AccountService::defaultSettings()['other'], $settings->other ?? []); - $res['settings'] = [ - 'reduce_motion' => (bool) $settings->reduce_motion, - 'high_contrast_mode' => (bool) $settings->high_contrast_mode, - 'video_autoplay' => (bool) $settings->video_autoplay, - 'media_descriptions' => (bool) $settings->media_descriptions, - 'crawlable' => (bool) $settings->crawlable, - 'show_profile_follower_count' => (bool) $settings->show_profile_follower_count, - 'show_profile_following_count' => (bool) $settings->show_profile_following_count, - 'public_dm' => (bool) $settings->public_dm, - 'disable_embeds' => (bool) $other['disable_embeds'], - ]; + $res['settings'] = AccountService::getAccountSettings($user->profile_id); } return $this->json($res); @@ -466,6 +454,7 @@ class ApiV1Controller extends Controller Cache::forget('pf:acct-trans:hideFollowing:'.$profile->id); Cache::forget('pf:acct-trans:hideFollowers:'.$profile->id); AccountService::del($user->profile_id); + AccountService::forgetAccountSettings($profile->id); } if ($syncLicenses && $licenseChanged) { diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index a27319c90..fba1e40b3 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -361,7 +361,7 @@ class ProfileController extends Controller return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']); } - if (AccountService::canEmbed($profile->user_id) == false) { + if (AccountService::canEmbed($profile->id) == false) { return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']); } diff --git a/app/Http/Controllers/Settings/HomeSettings.php b/app/Http/Controllers/Settings/HomeSettings.php index 99326c097..ce411e4fd 100644 --- a/app/Http/Controllers/Settings/HomeSettings.php +++ b/app/Http/Controllers/Settings/HomeSettings.php @@ -4,21 +4,17 @@ namespace App\Http\Controllers\Settings; use App\AccountLog; use App\EmailVerification; +use App\Mail\PasswordChange; use App\Media; -use App\Profile; -use App\User; -use App\UserFilter; +use App\Services\AccountService; +use App\Services\PronounService; use App\Util\Lexer\Autolink; use App\Util\Lexer\PrettyNumber; use Auth; use Cache; -use DB; +use Illuminate\Http\Request; use Mail; use Purify; -use App\Mail\PasswordChange; -use Illuminate\Http\Request; -use App\Services\AccountService; -use App\Services\PronounService; trait HomeSettings { @@ -40,11 +36,11 @@ trait HomeSettings public function homeUpdate(Request $request) { $this->validate($request, [ - 'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'), - 'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'), + 'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'), + 'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'), 'website' => 'nullable|url', 'language' => 'nullable|string|min:2|max:5', - 'pronouns' => 'nullable|array|max:4' + 'pronouns' => 'nullable|array|max:4', ]); $changes = false; @@ -57,14 +53,14 @@ trait HomeSettings $pronouns = $request->input('pronouns'); $existingPronouns = PronounService::get($profile->id); $layout = $request->input('profile_layout'); - if($layout) { - $layout = !in_array($layout, ['metro', 'moment']) ? 'metro' : $layout; + if ($layout) { + $layout = ! in_array($layout, ['metro', 'moment']) ? 'metro' : $layout; } $enforceEmailVerification = config_cache('pixelfed.enforce_email_verification'); // Only allow email to be updated if not yet verified - if (!$enforceEmailVerification || !$changes && $user->email_verified_at) { + if (! $enforceEmailVerification || ! $changes && $user->email_verified_at) { if ($profile->name != $name) { $changes = true; $user->name = $name; @@ -81,7 +77,7 @@ trait HomeSettings $profile->bio = Autolink::create()->autolink($bio); } - if($user->language != $language && + if ($user->language != $language && in_array($language, \App\Util\Localization\Localization::languages()) ) { $changes = true; @@ -89,8 +85,8 @@ trait HomeSettings session()->put('locale', $language); } - if($existingPronouns != $pronouns) { - if($pronouns && in_array('Select Pronoun(s)', $pronouns)) { + if ($existingPronouns != $pronouns) { + if ($pronouns && in_array('Select Pronoun(s)', $pronouns)) { PronounService::clear($profile->id); } else { PronounService::put($profile->id, $pronouns); @@ -102,7 +98,9 @@ trait HomeSettings $user->save(); $profile->save(); Cache::forget('user:account:id:'.$user->id); + AccountService::forgetAccountSettings($profile->id); AccountService::del($profile->id); + return redirect('/settings/home')->with('status', 'Profile successfully updated!'); } @@ -117,10 +115,10 @@ trait HomeSettings public function passwordUpdate(Request $request) { $this->validate($request, [ - 'current' => 'required|string', - 'password' => 'required|string', - 'password_confirmation' => 'required|string', - ]); + 'current' => 'required|string', + 'password' => 'required|string', + 'password_confirmation' => 'required|string', + ]); $current = $request->input('current'); $new = $request->input('password'); @@ -144,6 +142,7 @@ trait HomeSettings $log->save(); Mail::to($request->user())->send(new PasswordChange($user)); + return redirect('/settings/home')->with('status', 'Password successfully updated!'); } else { return redirect()->back()->with('error', 'There was an error with your request! Please try again.'); @@ -159,7 +158,7 @@ trait HomeSettings public function emailUpdate(Request $request) { $this->validate($request, [ - 'email' => 'required|email|unique:users,email', + 'email' => 'required|email|unique:users,email', ]); $changes = false; $email = $request->input('email'); diff --git a/app/Http/Controllers/Settings/PrivacySettings.php b/app/Http/Controllers/Settings/PrivacySettings.php index f4cc07178..c9caa168d 100644 --- a/app/Http/Controllers/Settings/PrivacySettings.php +++ b/app/Http/Controllers/Settings/PrivacySettings.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Settings; use App\Follower; use App\Profile; +use App\Services\AccountService; use App\Services\RelationshipService; use App\UserFilter; use Auth; @@ -19,7 +20,13 @@ trait PrivacySettings $settings = $user->settings; $profile = $user->profile; $is_private = $profile->is_private; + $cachedSettings = AccountService::getAccountSettings($profile->id); $settings['is_private'] = (bool) $is_private; + if ($cachedSettings && isset($cachedSettings['disable_embeds'])) { + $settings['disable_embeds'] = (bool) $cachedSettings['disable_embeds']; + } else { + $settings['disable_embeds'] = false; + } return view('settings.privacy', compact('settings', 'profile')); } @@ -28,6 +35,7 @@ trait PrivacySettings { $settings = $request->user()->settings; $profile = $request->user()->profile; + $other = $settings->other; $fields = [ 'is_private', 'crawlable', @@ -42,6 +50,16 @@ trait PrivacySettings $profile->is_suggestable = $request->input('is_suggestable') == 'on'; $profile->save(); + if ($request->has('disable_embeds')) { + $other['disable_embeds'] = true; + $settings->other = $other; + $settings->save(); + } else { + $other['disable_embeds'] = false; + $settings->other = $other; + $settings->save(); + } + foreach ($fields as $field) { $form = $request->input($field); if ($field == 'is_private') { @@ -91,6 +109,7 @@ trait PrivacySettings Cache::forget('pf:acct-trans:hideFollowers:'.$pid); Cache::forget('pfc:cached-user:wt:'.strtolower($profile->username)); Cache::forget('pfc:cached-user:wot:'.strtolower($profile->username)); + AccountService::forgetAccountSettings($profile->id); return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!'); } diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 2eb9df65f..981c47784 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -2,270 +2,275 @@ namespace App\Http\Controllers; -use App\AccountLog; -use App\Following; -use App\ProfileSponsor; -use App\Report; -use App\UserFilter; -use App\UserSetting; -use Auth, Cookie, DB, Cache, Purify; -use Illuminate\Support\Facades\Redis; -use Carbon\Carbon; -use Illuminate\Http\Request; -use Illuminate\Support\Str; -use App\Http\Controllers\Settings\{ - ExportSettings, - LabsSettings, - HomeSettings, - PrivacySettings, - RelationshipSettings, - SecuritySettings -}; +use App\Http\Controllers\Settings\ExportSettings; +use App\Http\Controllers\Settings\HomeSettings; +use App\Http\Controllers\Settings\LabsSettings; +use App\Http\Controllers\Settings\PrivacySettings; +use App\Http\Controllers\Settings\RelationshipSettings; +use App\Http\Controllers\Settings\SecuritySettings; use App\Jobs\DeletePipeline\DeleteAccountPipeline; use App\Jobs\MediaPipeline\MediaSyncLicensePipeline; +use App\ProfileSponsor; use App\Services\AccountService; +use App\UserSetting; +use Auth; +use Cache; +use Carbon\Carbon; +use Cookie; +use Illuminate\Http\Request; +use Illuminate\Support\Facades\Redis; +use Illuminate\Support\Str; class SettingsController extends Controller { - use ExportSettings, - LabsSettings, - HomeSettings, - PrivacySettings, - RelationshipSettings, - SecuritySettings; + use ExportSettings, + HomeSettings, + LabsSettings, + PrivacySettings, + RelationshipSettings, + SecuritySettings; - public function __construct() - { - $this->middleware('auth'); - } + public function __construct() + { + $this->middleware('auth'); + } - public function accessibility() - { - $settings = Auth::user()->settings; + public function accessibility() + { + $settings = Auth::user()->settings; - return view('settings.accessibility', compact('settings')); - } + return view('settings.accessibility', compact('settings')); + } - public function accessibilityStore(Request $request) - { - $settings = Auth::user()->settings; - $fields = [ - 'compose_media_descriptions', - 'reduce_motion', - 'optimize_screen_reader', - 'high_contrast_mode', - 'video_autoplay', - ]; - foreach ($fields as $field) { - $form = $request->input($field); - if ($form == 'on') { - $settings->{$field} = true; - } else { - $settings->{$field} = false; - } - $settings->save(); - } + public function accessibilityStore(Request $request) + { + $user = $request->user(); + $settings = $user->settings; + $fields = [ + 'compose_media_descriptions', + 'reduce_motion', + 'optimize_screen_reader', + 'high_contrast_mode', + 'video_autoplay', + ]; + foreach ($fields as $field) { + $form = $request->input($field); + if ($form == 'on') { + $settings->{$field} = true; + } else { + $settings->{$field} = false; + } + $settings->save(); + } + AccountService::forgetAccountSettings($user->profile_id); - return redirect(route('settings.accessibility'))->with('status', 'Settings successfully updated!'); - } + return redirect(route('settings.accessibility'))->with('status', 'Settings successfully updated!'); + } - public function notifications() - { - return view('settings.notifications'); - } + public function notifications() + { + return view('settings.notifications'); + } - public function applications() - { - return view('settings.applications'); - } + public function applications() + { + return view('settings.applications'); + } - public function dataImport() - { - return view('settings.import.home'); - } + public function dataImport() + { + return view('settings.import.home'); + } - public function dataImportInstagram() - { - abort(404); - } + public function dataImportInstagram() + { + abort(404); + } - public function developers() - { - return view('settings.developers'); - } + public function developers() + { + return view('settings.developers'); + } - public function removeAccountTemporary(Request $request) - { - $user = Auth::user(); - abort_if(!config('pixelfed.account_deletion'), 403); - abort_if($user->is_admin, 403); + public function removeAccountTemporary(Request $request) + { + $user = Auth::user(); + abort_if(! config('pixelfed.account_deletion'), 403); + abort_if($user->is_admin, 403); - return view('settings.remove.temporary'); - } + return view('settings.remove.temporary'); + } - public function removeAccountTemporarySubmit(Request $request) - { - $user = Auth::user(); - abort_if(!config('pixelfed.account_deletion'), 403); - abort_if($user->is_admin, 403); - $profile = $user->profile; - $user->status = 'disabled'; - $profile->status = 'disabled'; - $user->save(); - $profile->save(); - Auth::logout(); - Cache::forget('profiles:private'); - return redirect('/'); - } + public function removeAccountTemporarySubmit(Request $request) + { + $user = Auth::user(); + abort_if(! config('pixelfed.account_deletion'), 403); + abort_if($user->is_admin, 403); + $profile = $user->profile; + $user->status = 'disabled'; + $profile->status = 'disabled'; + $user->save(); + $profile->save(); + Auth::logout(); + Cache::forget('profiles:private'); - public function removeAccountPermanent(Request $request) - { - $user = Auth::user(); - abort_if($user->is_admin, 403); - return view('settings.remove.permanent'); - } + return redirect('/'); + } - public function removeAccountPermanentSubmit(Request $request) - { - if(config('pixelfed.account_deletion') == false) { - abort(404); - } - $user = Auth::user(); - abort_if(!config('pixelfed.account_deletion'), 403); - abort_if($user->is_admin, 403); - $profile = $user->profile; - $ts = Carbon::now()->addMonth(); - $user->email = $user->id; - $user->password = ''; - $user->status = 'delete'; - $profile->status = 'delete'; - $user->delete_after = $ts; - $profile->delete_after = $ts; - $user->save(); - $profile->save(); - Cache::forget('profiles:private'); - AccountService::del($profile->id); - Auth::logout(); - DeleteAccountPipeline::dispatch($user)->onQueue('low'); - return redirect('/'); - } + public function removeAccountPermanent(Request $request) + { + $user = Auth::user(); + abort_if($user->is_admin, 403); - public function requestFullExport(Request $request) - { - $user = Auth::user(); - return view('settings.export.show'); - } + return view('settings.remove.permanent'); + } - public function metroDarkMode(Request $request) - { - $this->validate($request, [ - 'mode' => 'required|string|in:light,dark' - ]); + public function removeAccountPermanentSubmit(Request $request) + { + if (config('pixelfed.account_deletion') == false) { + abort(404); + } + $user = Auth::user(); + abort_if(! config('pixelfed.account_deletion'), 403); + abort_if($user->is_admin, 403); + $profile = $user->profile; + $ts = Carbon::now()->addMonth(); + $user->email = $user->id; + $user->password = ''; + $user->status = 'delete'; + $profile->status = 'delete'; + $user->delete_after = $ts; + $profile->delete_after = $ts; + $user->save(); + $profile->save(); + Cache::forget('profiles:private'); + AccountService::del($profile->id); + Auth::logout(); + DeleteAccountPipeline::dispatch($user)->onQueue('low'); - $mode = $request->input('mode'); + return redirect('/'); + } - if($mode == 'dark') { - $cookie = Cookie::make('dark-mode', 'true', 43800); - } else { - $cookie = Cookie::forget('dark-mode'); - } + public function requestFullExport(Request $request) + { + $user = Auth::user(); - return response()->json([200])->cookie($cookie); - } + return view('settings.export.show'); + } - public function sponsor() - { - $default = [ - 'patreon' => null, - 'liberapay' => null, - 'opencollective' => null - ]; - $sponsors = ProfileSponsor::whereProfileId(Auth::user()->profile->id)->first(); - $sponsors = $sponsors ? json_decode($sponsors->sponsors, true) : $default; - return view('settings.sponsor', compact('sponsors')); - } - - public function sponsorStore(Request $request) - { - $this->validate($request, [ - 'patreon' => 'nullable|string', - 'liberapay' => 'nullable|string', - 'opencollective' => 'nullable|string' - ]); - - $patreon = Str::startsWith($request->input('patreon'), 'https://') ? - substr($request->input('patreon'), 8) : - $request->input('patreon'); - - $liberapay = Str::startsWith($request->input('liberapay'), 'https://') ? - substr($request->input('liberapay'), 8) : - $request->input('liberapay'); - - $opencollective = Str::startsWith($request->input('opencollective'), 'https://') ? - substr($request->input('opencollective'), 8) : - $request->input('opencollective'); - - $patreon = Str::startsWith($patreon, 'patreon.com/') ? e($patreon) : null; - $liberapay = Str::startsWith($liberapay, 'liberapay.com/') ? e($liberapay) : null; - $opencollective = Str::startsWith($opencollective, 'opencollective.com/') ? e($opencollective) : null; - - if(empty($patreon) && empty($liberapay) && empty($opencollective)) { - return redirect(route('settings'))->with('error', 'An error occured. Please try again later.'); - } - - $res = [ - 'patreon' => $patreon, - 'liberapay' => $liberapay, - 'opencollective' => $opencollective - ]; - - $sponsors = ProfileSponsor::firstOrCreate([ - 'profile_id' => Auth::user()->profile_id ?? Auth::user()->profile->id - ]); - $sponsors->sponsors = json_encode($res); - $sponsors->save(); - $sponsors = $res; - return redirect(route('settings'))->with('status', 'Sponsor settings successfully updated!'); - } - - public function timelineSettings(Request $request) - { - $uid = $request->user()->id; - $pid = $request->user()->profile_id; - $top = Redis::zscore('pf:tl:top', $pid) != false; - $replies = Redis::zscore('pf:tl:replies', $pid) != false; - $userSettings = UserSetting::firstOrCreate([ - 'user_id' => $uid + public function metroDarkMode(Request $request) + { + $this->validate($request, [ + 'mode' => 'required|string|in:light,dark', ]); - if(!$userSettings || !$userSettings->other) { + + $mode = $request->input('mode'); + + if ($mode == 'dark') { + $cookie = Cookie::make('dark-mode', 'true', 43800); + } else { + $cookie = Cookie::forget('dark-mode'); + } + + return response()->json([200])->cookie($cookie); + } + + public function sponsor() + { + $default = [ + 'patreon' => null, + 'liberapay' => null, + 'opencollective' => null, + ]; + $sponsors = ProfileSponsor::whereProfileId(Auth::user()->profile->id)->first(); + $sponsors = $sponsors ? json_decode($sponsors->sponsors, true) : $default; + + return view('settings.sponsor', compact('sponsors')); + } + + public function sponsorStore(Request $request) + { + $this->validate($request, [ + 'patreon' => 'nullable|string', + 'liberapay' => 'nullable|string', + 'opencollective' => 'nullable|string', + ]); + + $patreon = Str::startsWith($request->input('patreon'), 'https://') ? + substr($request->input('patreon'), 8) : + $request->input('patreon'); + + $liberapay = Str::startsWith($request->input('liberapay'), 'https://') ? + substr($request->input('liberapay'), 8) : + $request->input('liberapay'); + + $opencollective = Str::startsWith($request->input('opencollective'), 'https://') ? + substr($request->input('opencollective'), 8) : + $request->input('opencollective'); + + $patreon = Str::startsWith($patreon, 'patreon.com/') ? e($patreon) : null; + $liberapay = Str::startsWith($liberapay, 'liberapay.com/') ? e($liberapay) : null; + $opencollective = Str::startsWith($opencollective, 'opencollective.com/') ? e($opencollective) : null; + + if (empty($patreon) && empty($liberapay) && empty($opencollective)) { + return redirect(route('settings'))->with('error', 'An error occured. Please try again later.'); + } + + $res = [ + 'patreon' => $patreon, + 'liberapay' => $liberapay, + 'opencollective' => $opencollective, + ]; + + $sponsors = ProfileSponsor::firstOrCreate([ + 'profile_id' => Auth::user()->profile_id ?? Auth::user()->profile->id, + ]); + $sponsors->sponsors = json_encode($res); + $sponsors->save(); + $sponsors = $res; + + return redirect(route('settings'))->with('status', 'Sponsor settings successfully updated!'); + } + + public function timelineSettings(Request $request) + { + $uid = $request->user()->id; + $pid = $request->user()->profile_id; + $top = Redis::zscore('pf:tl:top', $pid) != false; + $replies = Redis::zscore('pf:tl:replies', $pid) != false; + $userSettings = UserSetting::firstOrCreate([ + 'user_id' => $uid, + ]); + if (! $userSettings || ! $userSettings->other) { $userSettings = [ 'enable_reblogs' => false, - 'photo_reblogs_only' => false + 'photo_reblogs_only' => false, ]; } else { $userSettings = array_merge([ 'enable_reblogs' => false, - 'photo_reblogs_only' => false + 'photo_reblogs_only' => false, ], - $userSettings->other); + $userSettings->other); } - return view('settings.timeline', compact('top', 'replies', 'userSettings')); - } - public function updateTimelineSettings(Request $request) - { + return view('settings.timeline', compact('top', 'replies', 'userSettings')); + } + + public function updateTimelineSettings(Request $request) + { $pid = $request->user()->profile_id; - $uid = $request->user()->id; + $uid = $request->user()->id; $this->validate($request, [ 'enable_reblogs' => 'sometimes', - 'photo_reblogs_only' => 'sometimes' + 'photo_reblogs_only' => 'sometimes', ]); - Redis::zrem('pf:tl:top', $pid); - Redis::zrem('pf:tl:replies', $pid); + Redis::zrem('pf:tl:top', $pid); + Redis::zrem('pf:tl:replies', $pid); $userSettings = UserSetting::firstOrCreate([ - 'user_id' => $uid + 'user_id' => $uid, ]); - if($userSettings->other) { + if ($userSettings->other) { $other = $userSettings->other; $other['enable_reblogs'] = $request->has('enable_reblogs'); $other['photo_reblogs_only'] = $request->has('photo_reblogs_only'); @@ -275,72 +280,74 @@ class SettingsController extends Controller } $userSettings->other = $other; $userSettings->save(); - return redirect(route('settings'))->with('status', 'Timeline settings successfully updated!'); - } - public function mediaSettings(Request $request) - { - $setting = UserSetting::whereUserId($request->user()->id)->firstOrFail(); - $compose = $setting->compose_settings ? ( - is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings - ) : [ - 'default_license' => null, - 'media_descriptions' => false - ]; - return view('settings.media', compact('compose')); - } + return redirect(route('settings'))->with('status', 'Timeline settings successfully updated!'); + } - public function updateMediaSettings(Request $request) - { - $this->validate($request, [ - 'default' => 'required|int|min:1|max:16', - 'sync' => 'nullable', - 'media_descriptions' => 'nullable' - ]); + public function mediaSettings(Request $request) + { + $setting = UserSetting::whereUserId($request->user()->id)->firstOrFail(); + $compose = $setting->compose_settings ? ( + is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings + ) : [ + 'default_license' => null, + 'media_descriptions' => false, + ]; - $license = $request->input('default'); - $sync = $request->input('sync') == 'on'; - $media_descriptions = $request->input('media_descriptions') == 'on'; - $uid = $request->user()->id; + return view('settings.media', compact('compose')); + } - $setting = UserSetting::whereUserId($uid)->firstOrFail(); - $compose = is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings; - $changed = false; + public function updateMediaSettings(Request $request) + { + $this->validate($request, [ + 'default' => 'required|int|min:1|max:16', + 'sync' => 'nullable', + 'media_descriptions' => 'nullable', + ]); - if($sync) { - $key = 'pf:settings:mls_recently:'.$uid; - if(Cache::get($key) == 2) { - $msg = 'You can only sync licenses twice per 24 hours. Try again later.'; - return redirect(route('settings')) - ->with('error', $msg); - } - } + $license = $request->input('default'); + $sync = $request->input('sync') == 'on'; + $media_descriptions = $request->input('media_descriptions') == 'on'; + $uid = $request->user()->id; - if(!isset($compose['default_license']) || $compose['default_license'] !== $license) { - $compose['default_license'] = (int) $license; - $changed = true; - } + $setting = UserSetting::whereUserId($uid)->firstOrFail(); + $compose = is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings; + $changed = false; - if(!isset($compose['media_descriptions']) || $compose['media_descriptions'] !== $media_descriptions) { - $compose['media_descriptions'] = $media_descriptions; - $changed = true; - } + if ($sync) { + $key = 'pf:settings:mls_recently:'.$uid; + if (Cache::get($key) == 2) { + $msg = 'You can only sync licenses twice per 24 hours. Try again later.'; - if($changed) { - $setting->compose_settings = $compose; - $setting->save(); - Cache::forget('profile:compose:settings:' . $request->user()->id); - } + return redirect(route('settings')) + ->with('error', $msg); + } + } - if($sync) { - $val = Cache::has($key) ? 2 : 1; - Cache::put($key, $val, 86400); - MediaSyncLicensePipeline::dispatch($uid, $license); - return redirect(route('settings'))->with('status', 'Media licenses successfully synced! It may take a few minutes to take effect for every post.'); - } + if (! isset($compose['default_license']) || $compose['default_license'] !== $license) { + $compose['default_license'] = (int) $license; + $changed = true; + } - return redirect(route('settings'))->with('status', 'Media settings successfully updated!'); - } + if (! isset($compose['media_descriptions']) || $compose['media_descriptions'] !== $media_descriptions) { + $compose['media_descriptions'] = $media_descriptions; + $changed = true; + } + if ($changed) { + $setting->compose_settings = $compose; + $setting->save(); + Cache::forget('profile:compose:settings:'.$request->user()->id); + } + + if ($sync) { + $val = Cache::has($key) ? 2 : 1; + Cache::put($key, $val, 86400); + MediaSyncLicensePipeline::dispatch($uid, $license); + + return redirect(route('settings'))->with('status', 'Media licenses successfully synced! It may take a few minutes to take effect for every post.'); + } + + return redirect(route('settings'))->with('status', 'Media settings successfully updated!'); + } } - diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index e0864a4a7..b9718698b 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -135,6 +135,14 @@ class StatusController extends Controller return response($content)->header('X-Frame-Options', 'ALLOWALL'); } + $embedCheck = AccountService::canEmbed($profile['id']); + + if (! $embedCheck) { + $content = view('status.embed-removed'); + + return response($content)->header('X-Frame-Options', 'ALLOWALL'); + } + $aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile['id'], 3600, function () use ($profile) { $user = Profile::find($profile['id']); if (! $user) { diff --git a/app/Services/AccountService.php b/app/Services/AccountService.php index 5ffc1e9b5..8490ad0ab 100644 --- a/app/Services/AccountService.php +++ b/app/Services/AccountService.php @@ -2,50 +2,54 @@ namespace App\Services; -use Cache; +use App\Models\UserDomainBlock; use App\Profile; use App\Status; +use App\Transformer\Api\AccountTransformer; use App\User; use App\UserSetting; -use App\Models\UserDomainBlock; -use App\Transformer\Api\AccountTransformer; -use League\Fractal; -use League\Fractal\Serializer\ArraySerializer; +use Cache; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; -use \NumberFormatter; +use League\Fractal; +use League\Fractal\Serializer\ArraySerializer; +use NumberFormatter; class AccountService { const CACHE_KEY = 'pf:services:account:'; + const CACHE_PF_ACCT_SETTINGS_KEY = 'pf:services:account-settings:'; + public static function get($id, $softFail = false) { - $res = Cache::remember(self::CACHE_KEY . $id, 43200, function() use($id) { + $res = Cache::remember(self::CACHE_KEY.$id, 43200, function () use ($id) { $fractal = new Fractal\Manager(); $fractal->setSerializer(new ArraySerializer()); $profile = Profile::find($id); - if(!$profile || $profile->status === 'delete') { + if (! $profile || $profile->status === 'delete') { return null; } $resource = new Fractal\Resource\Item($profile, new AccountTransformer()); + return $fractal->createData($resource)->toArray(); }); - if(!$res) { + if (! $res) { return $softFail ? null : abort(404); } + return $res; } public static function getMastodon($id, $softFail = false) { $account = self::get($id, $softFail); - if(!$account) { + if (! $account) { return null; } - if(config('exp.emc') == false) { + if (config('exp.emc') == false) { return $account; } @@ -73,41 +77,86 @@ class AccountService public static function del($id) { - Cache::forget('pf:activitypub:user-object:by-id:' . $id); - return Cache::forget(self::CACHE_KEY . $id); + Cache::forget('pf:activitypub:user-object:by-id:'.$id); + + return Cache::forget(self::CACHE_KEY.$id); } public static function settings($id) { - return Cache::remember('profile:compose:settings:' . $id, 604800, function() use($id) { + return Cache::remember('profile:compose:settings:'.$id, 604800, function () use ($id) { $settings = UserSetting::whereUserId($id)->first(); - if(!$settings) { + if (! $settings) { return self::defaultSettings(); } - return collect($settings) - ->filter(function($item, $key) { - return in_array($key, array_keys(self::defaultSettings())) == true; - }) - ->map(function($item, $key) { - if($key == 'compose_settings') { - $cs = self::defaultSettings()['compose_settings']; - $ms = is_array($item) ? $item : []; - return array_merge($cs, $ms); - } - if($key == 'other') { - $other = self::defaultSettings()['other']; - $mo = is_array($item) ? $item : []; - return array_merge($other, $mo); - } - return $item; - }); + return collect($settings) + ->filter(function ($item, $key) { + return in_array($key, array_keys(self::defaultSettings())) == true; + }) + ->map(function ($item, $key) { + if ($key == 'compose_settings') { + $cs = self::defaultSettings()['compose_settings']; + $ms = is_array($item) ? $item : []; + + return array_merge($cs, $ms); + } + + if ($key == 'other') { + $other = self::defaultSettings()['other']; + $mo = is_array($item) ? $item : []; + + return array_merge($other, $mo); + } + + return $item; + }); }); } + public static function getAccountSettings($pid) + { + $key = self::CACHE_PF_ACCT_SETTINGS_KEY.$pid; + + return Cache::remember($key, 14400, function () use ($pid) { + $user = User::with('profile')->whereProfileId($pid)->whereNull('status')->first(); + if (! $user) { + return []; + } + + $settings = $user->settings; + $other = array_merge(self::defaultSettings()['other'], $settings->other ?? []); + + return [ + 'reduce_motion' => (bool) $settings->reduce_motion, + 'high_contrast_mode' => (bool) $settings->high_contrast_mode, + 'video_autoplay' => (bool) $settings->video_autoplay, + 'media_descriptions' => (bool) $settings->media_descriptions, + 'crawlable' => (bool) $settings->crawlable, + 'show_profile_follower_count' => (bool) $settings->show_profile_follower_count, + 'show_profile_following_count' => (bool) $settings->show_profile_following_count, + 'public_dm' => (bool) $settings->public_dm, + 'disable_embeds' => (bool) $other['disable_embeds'], + 'show_atom' => (bool) $settings->show_atom, + 'is_suggestable' => (bool) $user->profile->is_suggestable, + 'indexable' => (bool) $user->profile->indexable, + ]; + }); + } + + public static function forgetAccountSettings($pid) + { + return Cache::forget(self::CACHE_PF_ACCT_SETTINGS_KEY.$pid); + } + public static function canEmbed($id) { - return self::settings($id)['other']['disable_embeds'] == false; + $res = self::getAccountSettings($id); + if (! $res || ! isset($res['disable_embeds'])) { + return false; + } + + return ! $res['disable_embeds']; } public static function defaultSettings() @@ -123,7 +172,7 @@ class AccountService 'compose_settings' => [ 'default_scope' => 'public', 'default_license' => 1, - 'media_descriptions' => false + 'media_descriptions' => false, ], 'other' => [ 'advanced_atom' => false, @@ -134,7 +183,7 @@ class AccountService 'hide_groups' => false, 'hide_stories' => false, 'disable_cw' => false, - ] + ], ]; } @@ -142,13 +191,13 @@ class AccountService { $profile = Profile::find($id); - if(!$profile) { + if (! $profile) { return false; } - $key = self::CACHE_KEY . 'pcs:' . $id; + $key = self::CACHE_KEY.'pcs:'.$id; - if(Cache::has($key)) { + if (Cache::has($key)) { return; } @@ -162,23 +211,26 @@ class AccountService $profile->save(); Cache::put($key, 1, 900); + return true; } public static function usernameToId($username) { - $key = self::CACHE_KEY . 'u2id:' . hash('sha256', $username); - return Cache::remember($key, 14400, function() use($username) { + $key = self::CACHE_KEY.'u2id:'.hash('sha256', $username); + + return Cache::remember($key, 14400, function () use ($username) { $s = Str::of($username); - if($s->contains('@') && !$s->startsWith('@')) { + if ($s->contains('@') && ! $s->startsWith('@')) { $username = "@{$username}"; } $profile = DB::table('profiles') ->whereUsername($username) ->first(); - if(!$profile) { + if (! $profile) { return null; } + return (string) $profile->id; }); } @@ -186,19 +238,20 @@ class AccountService public static function hiddenFollowers($id) { $account = self::get($id, true); - if(!$account || !isset($account['local']) || $account['local'] == false) { + if (! $account || ! isset($account['local']) || $account['local'] == false) { return false; } - return Cache::remember('pf:acct:settings:hidden-followers:' . $id, 43200, function() use($id) { + return Cache::remember('pf:acct:settings:hidden-followers:'.$id, 43200, function () use ($id) { $user = User::whereProfileId($id)->first(); - if(!$user) { + if (! $user) { return false; } $settings = UserSetting::whereUserId($user->id)->first(); - if($settings) { + if ($settings) { return $settings->show_profile_follower_count == false; } + return false; }); } @@ -206,60 +259,66 @@ class AccountService public static function hiddenFollowing($id) { $account = self::get($id, true); - if(!$account || !isset($account['local']) || $account['local'] == false) { + if (! $account || ! isset($account['local']) || $account['local'] == false) { return false; } - return Cache::remember('pf:acct:settings:hidden-following:' . $id, 43200, function() use($id) { + return Cache::remember('pf:acct:settings:hidden-following:'.$id, 43200, function () use ($id) { $user = User::whereProfileId($id)->first(); - if(!$user) { + if (! $user) { return false; } $settings = UserSetting::whereUserId($user->id)->first(); - if($settings) { + if ($settings) { return $settings->show_profile_following_count == false; } + return false; }); } public static function setLastActive($id = false) { - if(!$id) { return; } - $key = 'user:last_active_at:id:' . $id; - if(!Cache::has($key)) { + if (! $id) { + return; + } + $key = 'user:last_active_at:id:'.$id; + if (! Cache::has($key)) { $user = User::find($id); - if(!$user) { return; } + if (! $user) { + return; + } $user->last_active_at = now(); $user->save(); Cache::put($key, 1, 14400); } - return; + } public static function blocksDomain($pid, $domain = false) { - if(!$domain) { + if (! $domain) { return; } return UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->exists(); } - public static function formatNumber($num) { - if(!$num || $num < 1) { - return "0"; + public static function formatNumber($num) + { + if (! $num || $num < 1) { + return '0'; } $num = intval($num); $formatter = new NumberFormatter('en_US', NumberFormatter::DECIMAL); $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 1); if ($num >= 1000000000) { - return $formatter->format($num / 1000000000) . 'B'; - } else if ($num >= 1000000) { - return $formatter->format($num / 1000000) . 'M'; + return $formatter->format($num / 1000000000).'B'; + } elseif ($num >= 1000000) { + return $formatter->format($num / 1000000).'M'; } elseif ($num >= 1000) { - return $formatter->format($num / 1000) . 'K'; + return $formatter->format($num / 1000).'K'; } else { return $formatter->format($num); } @@ -269,14 +328,17 @@ class AccountService { $account = self::get($id, true); - if(!$account) return ""; + if (! $account) { + return ''; + } - $posts = self::formatNumber($account['statuses_count']) . ' Posts, '; - $following = self::formatNumber($account['following_count']) . ' Following, '; - $followers = self::formatNumber($account['followers_count']) . ' Followers'; + $posts = self::formatNumber($account['statuses_count']).' Posts, '; + $following = self::formatNumber($account['following_count']).' Following, '; + $followers = self::formatNumber($account['followers_count']).' Followers'; $note = $account['note'] && strlen($account['note']) ? - ' · ' . \Purify::clean(strip_tags(str_replace("\n", '', str_replace("\r", '', $account['note'])))) : + ' · '.\Purify::clean(strip_tags(str_replace("\n", '', str_replace("\r", '', $account['note'])))) : ''; - return $posts . $following . $followers . $note; + + return $posts.$following.$followers.$note; } } diff --git a/resources/views/settings/privacy.blade.php b/resources/views/settings/privacy.blade.php index 369ddbbb4..9a0ca36a2 100644 --- a/resources/views/settings/privacy.blade.php +++ b/resources/views/settings/privacy.blade.php @@ -97,6 +97,14 @@

Display following count on profile

+
+ disable_embeds ? 'checked=""':''}}> + +

Disable post and profile embeds

+
+ @if(!$settings->is_private)
show_atom ? 'checked=""':''}}>