Add disable_embeds setting, and fix cache invalidation in other settings

This commit is contained in:
Daniel Supernault 2024-06-04 03:46:06 -06:00
parent e97b5b7a69
commit c5e7e91777
No known key found for this signature in database
GPG key ID: 23740873EE6F76A1
8 changed files with 475 additions and 383 deletions

View file

@ -194,19 +194,7 @@ class ApiV1Controller extends Controller
]; ];
if ($request->has(self::PF_API_ENTITY_KEY)) { if ($request->has(self::PF_API_ENTITY_KEY)) {
$settings = $user->settings; $res['settings'] = AccountService::getAccountSettings($user->profile_id);
$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'],
];
} }
return $this->json($res); 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:hideFollowing:'.$profile->id);
Cache::forget('pf:acct-trans:hideFollowers:'.$profile->id); Cache::forget('pf:acct-trans:hideFollowers:'.$profile->id);
AccountService::del($user->profile_id); AccountService::del($user->profile_id);
AccountService::forgetAccountSettings($profile->id);
} }
if ($syncLicenses && $licenseChanged) { if ($syncLicenses && $licenseChanged) {

View file

@ -361,7 +361,7 @@ class ProfileController extends Controller
return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']); 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']); return response($res)->withHeaders(['X-Frame-Options' => 'ALLOWALL']);
} }

View file

@ -4,21 +4,17 @@ namespace App\Http\Controllers\Settings;
use App\AccountLog; use App\AccountLog;
use App\EmailVerification; use App\EmailVerification;
use App\Mail\PasswordChange;
use App\Media; use App\Media;
use App\Profile; use App\Services\AccountService;
use App\User; use App\Services\PronounService;
use App\UserFilter;
use App\Util\Lexer\Autolink; use App\Util\Lexer\Autolink;
use App\Util\Lexer\PrettyNumber; use App\Util\Lexer\PrettyNumber;
use Auth; use Auth;
use Cache; use Cache;
use DB; use Illuminate\Http\Request;
use Mail; use Mail;
use Purify; use Purify;
use App\Mail\PasswordChange;
use Illuminate\Http\Request;
use App\Services\AccountService;
use App\Services\PronounService;
trait HomeSettings trait HomeSettings
{ {
@ -40,11 +36,11 @@ trait HomeSettings
public function homeUpdate(Request $request) public function homeUpdate(Request $request)
{ {
$this->validate($request, [ $this->validate($request, [
'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'), 'name' => 'nullable|string|max:'.config('pixelfed.max_name_length'),
'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'), 'bio' => 'nullable|string|max:'.config('pixelfed.max_bio_length'),
'website' => 'nullable|url', 'website' => 'nullable|url',
'language' => 'nullable|string|min:2|max:5', 'language' => 'nullable|string|min:2|max:5',
'pronouns' => 'nullable|array|max:4' 'pronouns' => 'nullable|array|max:4',
]); ]);
$changes = false; $changes = false;
@ -57,14 +53,14 @@ trait HomeSettings
$pronouns = $request->input('pronouns'); $pronouns = $request->input('pronouns');
$existingPronouns = PronounService::get($profile->id); $existingPronouns = PronounService::get($profile->id);
$layout = $request->input('profile_layout'); $layout = $request->input('profile_layout');
if($layout) { if ($layout) {
$layout = !in_array($layout, ['metro', 'moment']) ? 'metro' : $layout; $layout = ! in_array($layout, ['metro', 'moment']) ? 'metro' : $layout;
} }
$enforceEmailVerification = config_cache('pixelfed.enforce_email_verification'); $enforceEmailVerification = config_cache('pixelfed.enforce_email_verification');
// Only allow email to be updated if not yet verified // 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) { if ($profile->name != $name) {
$changes = true; $changes = true;
$user->name = $name; $user->name = $name;
@ -81,7 +77,7 @@ trait HomeSettings
$profile->bio = Autolink::create()->autolink($bio); $profile->bio = Autolink::create()->autolink($bio);
} }
if($user->language != $language && if ($user->language != $language &&
in_array($language, \App\Util\Localization\Localization::languages()) in_array($language, \App\Util\Localization\Localization::languages())
) { ) {
$changes = true; $changes = true;
@ -89,8 +85,8 @@ trait HomeSettings
session()->put('locale', $language); session()->put('locale', $language);
} }
if($existingPronouns != $pronouns) { if ($existingPronouns != $pronouns) {
if($pronouns && in_array('Select Pronoun(s)', $pronouns)) { if ($pronouns && in_array('Select Pronoun(s)', $pronouns)) {
PronounService::clear($profile->id); PronounService::clear($profile->id);
} else { } else {
PronounService::put($profile->id, $pronouns); PronounService::put($profile->id, $pronouns);
@ -102,7 +98,9 @@ trait HomeSettings
$user->save(); $user->save();
$profile->save(); $profile->save();
Cache::forget('user:account:id:'.$user->id); Cache::forget('user:account:id:'.$user->id);
AccountService::forgetAccountSettings($profile->id);
AccountService::del($profile->id); AccountService::del($profile->id);
return redirect('/settings/home')->with('status', 'Profile successfully updated!'); return redirect('/settings/home')->with('status', 'Profile successfully updated!');
} }
@ -117,10 +115,10 @@ trait HomeSettings
public function passwordUpdate(Request $request) public function passwordUpdate(Request $request)
{ {
$this->validate($request, [ $this->validate($request, [
'current' => 'required|string', 'current' => 'required|string',
'password' => 'required|string', 'password' => 'required|string',
'password_confirmation' => 'required|string', 'password_confirmation' => 'required|string',
]); ]);
$current = $request->input('current'); $current = $request->input('current');
$new = $request->input('password'); $new = $request->input('password');
@ -144,6 +142,7 @@ trait HomeSettings
$log->save(); $log->save();
Mail::to($request->user())->send(new PasswordChange($user)); Mail::to($request->user())->send(new PasswordChange($user));
return redirect('/settings/home')->with('status', 'Password successfully updated!'); return redirect('/settings/home')->with('status', 'Password successfully updated!');
} else { } else {
return redirect()->back()->with('error', 'There was an error with your request! Please try again.'); 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) public function emailUpdate(Request $request)
{ {
$this->validate($request, [ $this->validate($request, [
'email' => 'required|email|unique:users,email', 'email' => 'required|email|unique:users,email',
]); ]);
$changes = false; $changes = false;
$email = $request->input('email'); $email = $request->input('email');

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Settings;
use App\Follower; use App\Follower;
use App\Profile; use App\Profile;
use App\Services\AccountService;
use App\Services\RelationshipService; use App\Services\RelationshipService;
use App\UserFilter; use App\UserFilter;
use Auth; use Auth;
@ -19,7 +20,13 @@ trait PrivacySettings
$settings = $user->settings; $settings = $user->settings;
$profile = $user->profile; $profile = $user->profile;
$is_private = $profile->is_private; $is_private = $profile->is_private;
$cachedSettings = AccountService::getAccountSettings($profile->id);
$settings['is_private'] = (bool) $is_private; $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')); return view('settings.privacy', compact('settings', 'profile'));
} }
@ -28,6 +35,7 @@ trait PrivacySettings
{ {
$settings = $request->user()->settings; $settings = $request->user()->settings;
$profile = $request->user()->profile; $profile = $request->user()->profile;
$other = $settings->other;
$fields = [ $fields = [
'is_private', 'is_private',
'crawlable', 'crawlable',
@ -42,6 +50,16 @@ trait PrivacySettings
$profile->is_suggestable = $request->input('is_suggestable') == 'on'; $profile->is_suggestable = $request->input('is_suggestable') == 'on';
$profile->save(); $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) { foreach ($fields as $field) {
$form = $request->input($field); $form = $request->input($field);
if ($field == 'is_private') { if ($field == 'is_private') {
@ -91,6 +109,7 @@ trait PrivacySettings
Cache::forget('pf:acct-trans:hideFollowers:'.$pid); Cache::forget('pf:acct-trans:hideFollowers:'.$pid);
Cache::forget('pfc:cached-user:wt:'.strtolower($profile->username)); Cache::forget('pfc:cached-user:wt:'.strtolower($profile->username));
Cache::forget('pfc:cached-user:wot:'.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!'); return redirect(route('settings.privacy'))->with('status', 'Settings successfully updated!');
} }

View file

@ -2,270 +2,275 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\AccountLog; use App\Http\Controllers\Settings\ExportSettings;
use App\Following; use App\Http\Controllers\Settings\HomeSettings;
use App\ProfileSponsor; use App\Http\Controllers\Settings\LabsSettings;
use App\Report; use App\Http\Controllers\Settings\PrivacySettings;
use App\UserFilter; use App\Http\Controllers\Settings\RelationshipSettings;
use App\UserSetting; use App\Http\Controllers\Settings\SecuritySettings;
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\Jobs\DeletePipeline\DeleteAccountPipeline; use App\Jobs\DeletePipeline\DeleteAccountPipeline;
use App\Jobs\MediaPipeline\MediaSyncLicensePipeline; use App\Jobs\MediaPipeline\MediaSyncLicensePipeline;
use App\ProfileSponsor;
use App\Services\AccountService; 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 class SettingsController extends Controller
{ {
use ExportSettings, use ExportSettings,
LabsSettings, HomeSettings,
HomeSettings, LabsSettings,
PrivacySettings, PrivacySettings,
RelationshipSettings, RelationshipSettings,
SecuritySettings; SecuritySettings;
public function __construct() public function __construct()
{ {
$this->middleware('auth'); $this->middleware('auth');
} }
public function accessibility() public function accessibility()
{ {
$settings = Auth::user()->settings; $settings = Auth::user()->settings;
return view('settings.accessibility', compact('settings')); return view('settings.accessibility', compact('settings'));
} }
public function accessibilityStore(Request $request) public function accessibilityStore(Request $request)
{ {
$settings = Auth::user()->settings; $user = $request->user();
$fields = [ $settings = $user->settings;
'compose_media_descriptions', $fields = [
'reduce_motion', 'compose_media_descriptions',
'optimize_screen_reader', 'reduce_motion',
'high_contrast_mode', 'optimize_screen_reader',
'video_autoplay', 'high_contrast_mode',
]; 'video_autoplay',
foreach ($fields as $field) { ];
$form = $request->input($field); foreach ($fields as $field) {
if ($form == 'on') { $form = $request->input($field);
$settings->{$field} = true; if ($form == 'on') {
} else { $settings->{$field} = true;
$settings->{$field} = false; } else {
} $settings->{$field} = false;
$settings->save(); }
} $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() public function notifications()
{ {
return view('settings.notifications'); return view('settings.notifications');
} }
public function applications() public function applications()
{ {
return view('settings.applications'); return view('settings.applications');
} }
public function dataImport() public function dataImport()
{ {
return view('settings.import.home'); return view('settings.import.home');
} }
public function dataImportInstagram() public function dataImportInstagram()
{ {
abort(404); abort(404);
} }
public function developers() public function developers()
{ {
return view('settings.developers'); return view('settings.developers');
} }
public function removeAccountTemporary(Request $request) public function removeAccountTemporary(Request $request)
{ {
$user = Auth::user(); $user = Auth::user();
abort_if(!config('pixelfed.account_deletion'), 403); abort_if(! config('pixelfed.account_deletion'), 403);
abort_if($user->is_admin, 403); abort_if($user->is_admin, 403);
return view('settings.remove.temporary'); return view('settings.remove.temporary');
} }
public function removeAccountTemporarySubmit(Request $request) public function removeAccountTemporarySubmit(Request $request)
{ {
$user = Auth::user(); $user = Auth::user();
abort_if(!config('pixelfed.account_deletion'), 403); abort_if(! config('pixelfed.account_deletion'), 403);
abort_if($user->is_admin, 403); abort_if($user->is_admin, 403);
$profile = $user->profile; $profile = $user->profile;
$user->status = 'disabled'; $user->status = 'disabled';
$profile->status = 'disabled'; $profile->status = 'disabled';
$user->save(); $user->save();
$profile->save(); $profile->save();
Auth::logout(); Auth::logout();
Cache::forget('profiles:private'); Cache::forget('profiles:private');
return redirect('/');
}
public function removeAccountPermanent(Request $request) return redirect('/');
{ }
$user = Auth::user();
abort_if($user->is_admin, 403);
return view('settings.remove.permanent');
}
public function removeAccountPermanentSubmit(Request $request) public function removeAccountPermanent(Request $request)
{ {
if(config('pixelfed.account_deletion') == false) { $user = Auth::user();
abort(404); abort_if($user->is_admin, 403);
}
$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 requestFullExport(Request $request) return view('settings.remove.permanent');
{ }
$user = Auth::user();
return view('settings.export.show');
}
public function metroDarkMode(Request $request) public function removeAccountPermanentSubmit(Request $request)
{ {
$this->validate($request, [ if (config('pixelfed.account_deletion') == false) {
'mode' => 'required|string|in:light,dark' 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') { public function requestFullExport(Request $request)
$cookie = Cookie::make('dark-mode', 'true', 43800); {
} else { $user = Auth::user();
$cookie = Cookie::forget('dark-mode');
}
return response()->json([200])->cookie($cookie); return view('settings.export.show');
} }
public function sponsor() public function metroDarkMode(Request $request)
{ {
$default = [ $this->validate($request, [
'patreon' => null, 'mode' => 'required|string|in:light,dark',
'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) {
$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 = [ $userSettings = [
'enable_reblogs' => false, 'enable_reblogs' => false,
'photo_reblogs_only' => false 'photo_reblogs_only' => false,
]; ];
} else { } else {
$userSettings = array_merge([ $userSettings = array_merge([
'enable_reblogs' => false, '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; $pid = $request->user()->profile_id;
$uid = $request->user()->id; $uid = $request->user()->id;
$this->validate($request, [ $this->validate($request, [
'enable_reblogs' => 'sometimes', 'enable_reblogs' => 'sometimes',
'photo_reblogs_only' => 'sometimes' 'photo_reblogs_only' => 'sometimes',
]); ]);
Redis::zrem('pf:tl:top', $pid); Redis::zrem('pf:tl:top', $pid);
Redis::zrem('pf:tl:replies', $pid); Redis::zrem('pf:tl:replies', $pid);
$userSettings = UserSetting::firstOrCreate([ $userSettings = UserSetting::firstOrCreate([
'user_id' => $uid 'user_id' => $uid,
]); ]);
if($userSettings->other) { if ($userSettings->other) {
$other = $userSettings->other; $other = $userSettings->other;
$other['enable_reblogs'] = $request->has('enable_reblogs'); $other['enable_reblogs'] = $request->has('enable_reblogs');
$other['photo_reblogs_only'] = $request->has('photo_reblogs_only'); $other['photo_reblogs_only'] = $request->has('photo_reblogs_only');
@ -275,72 +280,74 @@ class SettingsController extends Controller
} }
$userSettings->other = $other; $userSettings->other = $other;
$userSettings->save(); $userSettings->save();
return redirect(route('settings'))->with('status', 'Timeline settings successfully updated!');
}
public function mediaSettings(Request $request) return redirect(route('settings'))->with('status', 'Timeline settings successfully updated!');
{ }
$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'));
}
public function updateMediaSettings(Request $request) public function mediaSettings(Request $request)
{ {
$this->validate($request, [ $setting = UserSetting::whereUserId($request->user()->id)->firstOrFail();
'default' => 'required|int|min:1|max:16', $compose = $setting->compose_settings ? (
'sync' => 'nullable', is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings
'media_descriptions' => 'nullable' ) : [
]); 'default_license' => null,
'media_descriptions' => false,
];
$license = $request->input('default'); return view('settings.media', compact('compose'));
$sync = $request->input('sync') == 'on'; }
$media_descriptions = $request->input('media_descriptions') == 'on';
$uid = $request->user()->id;
$setting = UserSetting::whereUserId($uid)->firstOrFail(); public function updateMediaSettings(Request $request)
$compose = is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings; {
$changed = false; $this->validate($request, [
'default' => 'required|int|min:1|max:16',
'sync' => 'nullable',
'media_descriptions' => 'nullable',
]);
if($sync) { $license = $request->input('default');
$key = 'pf:settings:mls_recently:'.$uid; $sync = $request->input('sync') == 'on';
if(Cache::get($key) == 2) { $media_descriptions = $request->input('media_descriptions') == 'on';
$msg = 'You can only sync licenses twice per 24 hours. Try again later.'; $uid = $request->user()->id;
return redirect(route('settings'))
->with('error', $msg);
}
}
if(!isset($compose['default_license']) || $compose['default_license'] !== $license) { $setting = UserSetting::whereUserId($uid)->firstOrFail();
$compose['default_license'] = (int) $license; $compose = is_string($setting->compose_settings) ? json_decode($setting->compose_settings, true) : $setting->compose_settings;
$changed = true; $changed = false;
}
if(!isset($compose['media_descriptions']) || $compose['media_descriptions'] !== $media_descriptions) { if ($sync) {
$compose['media_descriptions'] = $media_descriptions; $key = 'pf:settings:mls_recently:'.$uid;
$changed = true; if (Cache::get($key) == 2) {
} $msg = 'You can only sync licenses twice per 24 hours. Try again later.';
if($changed) { return redirect(route('settings'))
$setting->compose_settings = $compose; ->with('error', $msg);
$setting->save(); }
Cache::forget('profile:compose:settings:' . $request->user()->id); }
}
if($sync) { if (! isset($compose['default_license']) || $compose['default_license'] !== $license) {
$val = Cache::has($key) ? 2 : 1; $compose['default_license'] = (int) $license;
Cache::put($key, $val, 86400); $changed = true;
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!'); 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!');
}
} }

View file

@ -135,6 +135,14 @@ class StatusController extends Controller
return response($content)->header('X-Frame-Options', 'ALLOWALL'); 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) { $aiCheck = Cache::remember('profile:ai-check:spam-login:'.$profile['id'], 3600, function () use ($profile) {
$user = Profile::find($profile['id']); $user = Profile::find($profile['id']);
if (! $user) { if (! $user) {

View file

@ -2,50 +2,54 @@
namespace App\Services; namespace App\Services;
use Cache; use App\Models\UserDomainBlock;
use App\Profile; use App\Profile;
use App\Status; use App\Status;
use App\Transformer\Api\AccountTransformer;
use App\User; use App\User;
use App\UserSetting; use App\UserSetting;
use App\Models\UserDomainBlock; use Cache;
use App\Transformer\Api\AccountTransformer;
use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use \NumberFormatter; use League\Fractal;
use League\Fractal\Serializer\ArraySerializer;
use NumberFormatter;
class AccountService class AccountService
{ {
const CACHE_KEY = 'pf:services:account:'; const CACHE_KEY = 'pf:services:account:';
const CACHE_PF_ACCT_SETTINGS_KEY = 'pf:services:account-settings:';
public static function get($id, $softFail = false) 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 = new Fractal\Manager();
$fractal->setSerializer(new ArraySerializer()); $fractal->setSerializer(new ArraySerializer());
$profile = Profile::find($id); $profile = Profile::find($id);
if(!$profile || $profile->status === 'delete') { if (! $profile || $profile->status === 'delete') {
return null; return null;
} }
$resource = new Fractal\Resource\Item($profile, new AccountTransformer()); $resource = new Fractal\Resource\Item($profile, new AccountTransformer());
return $fractal->createData($resource)->toArray(); return $fractal->createData($resource)->toArray();
}); });
if(!$res) { if (! $res) {
return $softFail ? null : abort(404); return $softFail ? null : abort(404);
} }
return $res; return $res;
} }
public static function getMastodon($id, $softFail = false) public static function getMastodon($id, $softFail = false)
{ {
$account = self::get($id, $softFail); $account = self::get($id, $softFail);
if(!$account) { if (! $account) {
return null; return null;
} }
if(config('exp.emc') == false) { if (config('exp.emc') == false) {
return $account; return $account;
} }
@ -73,41 +77,86 @@ class AccountService
public static function del($id) public static function del($id)
{ {
Cache::forget('pf:activitypub:user-object:by-id:' . $id); Cache::forget('pf:activitypub:user-object:by-id:'.$id);
return Cache::forget(self::CACHE_KEY . $id);
return Cache::forget(self::CACHE_KEY.$id);
} }
public static function settings($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(); $settings = UserSetting::whereUserId($id)->first();
if(!$settings) { if (! $settings) {
return self::defaultSettings(); 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') { return collect($settings)
$other = self::defaultSettings()['other']; ->filter(function ($item, $key) {
$mo = is_array($item) ? $item : []; return in_array($key, array_keys(self::defaultSettings())) == true;
return array_merge($other, $mo); })
} ->map(function ($item, $key) {
return $item; 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) 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() public static function defaultSettings()
@ -123,7 +172,7 @@ class AccountService
'compose_settings' => [ 'compose_settings' => [
'default_scope' => 'public', 'default_scope' => 'public',
'default_license' => 1, 'default_license' => 1,
'media_descriptions' => false 'media_descriptions' => false,
], ],
'other' => [ 'other' => [
'advanced_atom' => false, 'advanced_atom' => false,
@ -134,7 +183,7 @@ class AccountService
'hide_groups' => false, 'hide_groups' => false,
'hide_stories' => false, 'hide_stories' => false,
'disable_cw' => false, 'disable_cw' => false,
] ],
]; ];
} }
@ -142,13 +191,13 @@ class AccountService
{ {
$profile = Profile::find($id); $profile = Profile::find($id);
if(!$profile) { if (! $profile) {
return false; return false;
} }
$key = self::CACHE_KEY . 'pcs:' . $id; $key = self::CACHE_KEY.'pcs:'.$id;
if(Cache::has($key)) { if (Cache::has($key)) {
return; return;
} }
@ -162,23 +211,26 @@ class AccountService
$profile->save(); $profile->save();
Cache::put($key, 1, 900); Cache::put($key, 1, 900);
return true; return true;
} }
public static function usernameToId($username) public static function usernameToId($username)
{ {
$key = self::CACHE_KEY . 'u2id:' . hash('sha256', $username); $key = self::CACHE_KEY.'u2id:'.hash('sha256', $username);
return Cache::remember($key, 14400, function() use($username) {
return Cache::remember($key, 14400, function () use ($username) {
$s = Str::of($username); $s = Str::of($username);
if($s->contains('@') && !$s->startsWith('@')) { if ($s->contains('@') && ! $s->startsWith('@')) {
$username = "@{$username}"; $username = "@{$username}";
} }
$profile = DB::table('profiles') $profile = DB::table('profiles')
->whereUsername($username) ->whereUsername($username)
->first(); ->first();
if(!$profile) { if (! $profile) {
return null; return null;
} }
return (string) $profile->id; return (string) $profile->id;
}); });
} }
@ -186,19 +238,20 @@ class AccountService
public static function hiddenFollowers($id) public static function hiddenFollowers($id)
{ {
$account = self::get($id, true); $account = self::get($id, true);
if(!$account || !isset($account['local']) || $account['local'] == false) { if (! $account || ! isset($account['local']) || $account['local'] == false) {
return 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(); $user = User::whereProfileId($id)->first();
if(!$user) { if (! $user) {
return false; return false;
} }
$settings = UserSetting::whereUserId($user->id)->first(); $settings = UserSetting::whereUserId($user->id)->first();
if($settings) { if ($settings) {
return $settings->show_profile_follower_count == false; return $settings->show_profile_follower_count == false;
} }
return false; return false;
}); });
} }
@ -206,60 +259,66 @@ class AccountService
public static function hiddenFollowing($id) public static function hiddenFollowing($id)
{ {
$account = self::get($id, true); $account = self::get($id, true);
if(!$account || !isset($account['local']) || $account['local'] == false) { if (! $account || ! isset($account['local']) || $account['local'] == false) {
return 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(); $user = User::whereProfileId($id)->first();
if(!$user) { if (! $user) {
return false; return false;
} }
$settings = UserSetting::whereUserId($user->id)->first(); $settings = UserSetting::whereUserId($user->id)->first();
if($settings) { if ($settings) {
return $settings->show_profile_following_count == false; return $settings->show_profile_following_count == false;
} }
return false; return false;
}); });
} }
public static function setLastActive($id = false) public static function setLastActive($id = false)
{ {
if(!$id) { return; } if (! $id) {
$key = 'user:last_active_at:id:' . $id; return;
if(!Cache::has($key)) { }
$key = 'user:last_active_at:id:'.$id;
if (! Cache::has($key)) {
$user = User::find($id); $user = User::find($id);
if(!$user) { return; } if (! $user) {
return;
}
$user->last_active_at = now(); $user->last_active_at = now();
$user->save(); $user->save();
Cache::put($key, 1, 14400); Cache::put($key, 1, 14400);
} }
return;
} }
public static function blocksDomain($pid, $domain = false) public static function blocksDomain($pid, $domain = false)
{ {
if(!$domain) { if (! $domain) {
return; return;
} }
return UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->exists(); return UserDomainBlock::whereProfileId($pid)->whereDomain($domain)->exists();
} }
public static function formatNumber($num) { public static function formatNumber($num)
if(!$num || $num < 1) { {
return "0"; if (! $num || $num < 1) {
return '0';
} }
$num = intval($num); $num = intval($num);
$formatter = new NumberFormatter('en_US', NumberFormatter::DECIMAL); $formatter = new NumberFormatter('en_US', NumberFormatter::DECIMAL);
$formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 1); $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, 1);
if ($num >= 1000000000) { if ($num >= 1000000000) {
return $formatter->format($num / 1000000000) . 'B'; return $formatter->format($num / 1000000000).'B';
} else if ($num >= 1000000) { } elseif ($num >= 1000000) {
return $formatter->format($num / 1000000) . 'M'; return $formatter->format($num / 1000000).'M';
} elseif ($num >= 1000) { } elseif ($num >= 1000) {
return $formatter->format($num / 1000) . 'K'; return $formatter->format($num / 1000).'K';
} else { } else {
return $formatter->format($num); return $formatter->format($num);
} }
@ -269,14 +328,17 @@ class AccountService
{ {
$account = self::get($id, true); $account = self::get($id, true);
if(!$account) return ""; if (! $account) {
return '';
}
$posts = self::formatNumber($account['statuses_count']) . ' Posts, '; $posts = self::formatNumber($account['statuses_count']).' Posts, ';
$following = self::formatNumber($account['following_count']) . ' Following, '; $following = self::formatNumber($account['following_count']).' Following, ';
$followers = self::formatNumber($account['followers_count']) . ' Followers'; $followers = self::formatNumber($account['followers_count']).' Followers';
$note = $account['note'] && strlen($account['note']) ? $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;
} }
} }

View file

@ -97,6 +97,14 @@
<p class="text-muted small help-text">Display following count on profile</p> <p class="text-muted small help-text">Display following count on profile</p>
</div> </div>
<div class="form-check pb-3">
<input class="form-check-input" type="checkbox" name="disable_embeds" id="disable_embeds" {{$settings->disable_embeds ? 'checked=""':''}}>
<label class="form-check-label font-weight-bold" for="disable_embeds">
{{__('Disable Embeds')}}
</label>
<p class="text-muted small help-text">Disable post and profile embeds</p>
</div>
@if(!$settings->is_private) @if(!$settings->is_private)
<div class="form-check pb-3"> <div class="form-check pb-3">
<input class="form-check-input" type="checkbox" name="show_atom" id="show_atom" {{$settings->show_atom ? 'checked=""':''}}> <input class="form-check-input" type="checkbox" name="show_atom" id="show_atom" {{$settings->show_atom ? 'checked=""':''}}>