mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-12-22 13:03:16 +00:00
Merge branch 'staging' of github.com:pixelfed/pixelfed into jippi-fork
This commit is contained in:
commit
5a9cfe1f2a
136 changed files with 5575 additions and 1668 deletions
7
.gitattributes
vendored
7
.gitattributes
vendored
|
@ -3,3 +3,10 @@
|
|||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
CHANGELOG.md export-ignore
|
||||
|
||||
# Collapse diffs for generated files:
|
||||
public/**/*.js text -diff
|
||||
public/**/*.json text -diff
|
||||
public/**/*.css text -diff
|
||||
public/img/* binary -diff
|
||||
public/fonts/* binary -diff
|
||||
|
|
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -1,6 +1,19 @@
|
|||
# Release Notes
|
||||
|
||||
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.12...dev)
|
||||
|
||||
### Features
|
||||
|
||||
- Curated Onboarding ([8dac2caf](https://github.com/pixelfed/pixelfed/commit/8dac2caf))
|
||||
|
||||
### Updates
|
||||
|
||||
- Update Inbox, cast live filters to lowercase ([d835e0ad](https://github.com/pixelfed/pixelfed/commit/d835e0ad))
|
||||
- Update federation config, increase default timeline days falloff to 90 days from 2 days. Fixes #4905 ([011834f4](https://github.com/pixelfed/pixelfed/commit/011834f4))
|
||||
- Update cache config, use predis as default redis driver client ([ea6b1623](https://github.com/pixelfed/pixelfed/commit/ea6b1623))
|
||||
- Update .gitattributes to collapse diffs on generated files ([ThisIsMissEm](https://github.com/pixelfed/pixelfed/commit/9978b2b9))
|
||||
- Update api v1/v2 instance endpoints, bump mastoapi version from 2.7.2 to 3.5.3 ([545f7d5e](https://github.com/pixelfed/pixelfed/commit/545f7d5e))
|
||||
- Update ApiV1Controller, implement better limit logic to gracefully handle requests with limits that exceed the max ([1f74a95d](https://github.com/pixelfed/pixelfed/commit/1f74a95d))
|
||||
- ([](https://github.com/pixelfed/pixelfed/commit/))
|
||||
|
||||
## [v0.11.12 (2024-02-16)](https://github.com/pixelfed/pixelfed/compare/v0.11.11...v0.11.12)
|
||||
|
@ -9,7 +22,7 @@
|
|||
- Autospam Live Filters - block remote activities based on comma separated keywords ([40b45b2a](https://github.com/pixelfed/pixelfed/commit/40b45b2a))
|
||||
- Added Software Update banner to admin home feeds ([b0fb1988](https://github.com/pixelfed/pixelfed/commit/b0fb1988))
|
||||
|
||||
### Updated
|
||||
### Updates
|
||||
|
||||
- Update ApiV1Controller, fix network timeline ([0faf59e3](https://github.com/pixelfed/pixelfed/commit/0faf59e3))
|
||||
- Update public/network timelines, fix non-redis response and fix reblogs in home feed ([8b4ac5cc](https://github.com/pixelfed/pixelfed/commit/8b4ac5cc))
|
||||
|
|
|
@ -17,269 +17,288 @@ use Illuminate\Support\Str;
|
|||
|
||||
trait AdminSettingsController
|
||||
{
|
||||
public function settings(Request $request)
|
||||
{
|
||||
$cloud_storage = ConfigCacheService::get('pixelfed.cloud_storage');
|
||||
$cloud_disk = config('filesystems.cloud');
|
||||
$cloud_ready = !empty(config('filesystems.disks.' . $cloud_disk . '.key')) && !empty(config('filesystems.disks.' . $cloud_disk . '.secret'));
|
||||
$types = explode(',', ConfigCacheService::get('pixelfed.media_types'));
|
||||
$rules = ConfigCacheService::get('app.rules') ? json_decode(ConfigCacheService::get('app.rules'), true) : null;
|
||||
$jpeg = in_array('image/jpg', $types) || in_array('image/jpeg', $types);
|
||||
$png = in_array('image/png', $types);
|
||||
$gif = in_array('image/gif', $types);
|
||||
$mp4 = in_array('video/mp4', $types);
|
||||
$webp = in_array('image/webp', $types);
|
||||
public function settings(Request $request)
|
||||
{
|
||||
$cloud_storage = ConfigCacheService::get('pixelfed.cloud_storage');
|
||||
$cloud_disk = config('filesystems.cloud');
|
||||
$cloud_ready = !empty(config('filesystems.disks.' . $cloud_disk . '.key')) && !empty(config('filesystems.disks.' . $cloud_disk . '.secret'));
|
||||
$types = explode(',', ConfigCacheService::get('pixelfed.media_types'));
|
||||
$rules = ConfigCacheService::get('app.rules') ? json_decode(ConfigCacheService::get('app.rules'), true) : null;
|
||||
$jpeg = in_array('image/jpg', $types) || in_array('image/jpeg', $types);
|
||||
$png = in_array('image/png', $types);
|
||||
$gif = in_array('image/gif', $types);
|
||||
$mp4 = in_array('video/mp4', $types);
|
||||
$webp = in_array('image/webp', $types);
|
||||
|
||||
$availableAdmins = User::whereIsAdmin(true)->get();
|
||||
$currentAdmin = config_cache('instance.admin.pid') ? AccountService::get(config_cache('instance.admin.pid'), true) : null;
|
||||
$availableAdmins = User::whereIsAdmin(true)->get();
|
||||
$currentAdmin = config_cache('instance.admin.pid') ? AccountService::get(config_cache('instance.admin.pid'), true) : null;
|
||||
$openReg = (bool) config_cache('pixelfed.open_registration');
|
||||
$curOnboarding = (bool) config_cache('instance.curated_registration.enabled');
|
||||
$regState = $openReg ? 'open' : ($curOnboarding ? 'filtered' : 'closed');
|
||||
|
||||
// $system = [
|
||||
// 'permissions' => is_writable(base_path('storage')) && is_writable(base_path('bootstrap')),
|
||||
// 'max_upload_size' => ini_get('post_max_size'),
|
||||
// 'image_driver' => config('image.driver'),
|
||||
// 'image_driver_loaded' => extension_loaded(config('image.driver'))
|
||||
// ];
|
||||
return view('admin.settings.home', compact(
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'mp4',
|
||||
'webp',
|
||||
'rules',
|
||||
'cloud_storage',
|
||||
'cloud_disk',
|
||||
'cloud_ready',
|
||||
'availableAdmins',
|
||||
'currentAdmin',
|
||||
'regState'
|
||||
));
|
||||
}
|
||||
|
||||
return view('admin.settings.home', compact(
|
||||
'jpeg',
|
||||
'png',
|
||||
'gif',
|
||||
'mp4',
|
||||
'webp',
|
||||
'rules',
|
||||
'cloud_storage',
|
||||
'cloud_disk',
|
||||
'cloud_ready',
|
||||
'availableAdmins',
|
||||
'currentAdmin'
|
||||
// 'system'
|
||||
));
|
||||
}
|
||||
public function settingsHomeStore(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'name' => 'nullable|string',
|
||||
'short_description' => 'nullable',
|
||||
'long_description' => 'nullable',
|
||||
'max_photo_size' => 'nullable|integer|min:1',
|
||||
'max_album_length' => 'nullable|integer|min:1|max:100',
|
||||
'image_quality' => 'nullable|integer|min:1|max:100',
|
||||
'type_jpeg' => 'nullable',
|
||||
'type_png' => 'nullable',
|
||||
'type_gif' => 'nullable',
|
||||
'type_mp4' => 'nullable',
|
||||
'type_webp' => 'nullable',
|
||||
'admin_account_id' => 'nullable',
|
||||
'regs' => 'required|in:open,filtered,closed'
|
||||
]);
|
||||
|
||||
public function settingsHomeStore(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'name' => 'nullable|string',
|
||||
'short_description' => 'nullable',
|
||||
'long_description' => 'nullable',
|
||||
'max_photo_size' => 'nullable|integer|min:1',
|
||||
'max_album_length' => 'nullable|integer|min:1|max:100',
|
||||
'image_quality' => 'nullable|integer|min:1|max:100',
|
||||
'type_jpeg' => 'nullable',
|
||||
'type_png' => 'nullable',
|
||||
'type_gif' => 'nullable',
|
||||
'type_mp4' => 'nullable',
|
||||
'type_webp' => 'nullable',
|
||||
'admin_account_id' => 'nullable',
|
||||
]);
|
||||
$orb = false;
|
||||
$cob = false;
|
||||
switch($request->input('regs')) {
|
||||
case 'open':
|
||||
$orb = true;
|
||||
$cob = false;
|
||||
break;
|
||||
|
||||
if($request->filled('admin_account_id')) {
|
||||
ConfigCacheService::put('instance.admin.pid', $request->admin_account_id);
|
||||
Cache::forget('api:v1:instance-data:contact');
|
||||
Cache::forget('api:v1:instance-data-response-v1');
|
||||
}
|
||||
if($request->filled('rule_delete')) {
|
||||
$index = (int) $request->input('rule_delete');
|
||||
$rules = ConfigCacheService::get('app.rules');
|
||||
$json = json_decode($rules, true);
|
||||
if(!$rules || empty($json)) {
|
||||
return;
|
||||
}
|
||||
unset($json[$index]);
|
||||
$json = json_encode(array_values($json));
|
||||
ConfigCacheService::put('app.rules', $json);
|
||||
Cache::forget('api:v1:instance-data:rules');
|
||||
Cache::forget('api:v1:instance-data-response-v1');
|
||||
return 200;
|
||||
}
|
||||
case 'filtered':
|
||||
$orb = false;
|
||||
$cob = true;
|
||||
break;
|
||||
|
||||
$media_types = explode(',', config_cache('pixelfed.media_types'));
|
||||
$media_types_original = $media_types;
|
||||
case 'closed':
|
||||
$orb = false;
|
||||
$cob = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$mimes = [
|
||||
'type_jpeg' => 'image/jpeg',
|
||||
'type_png' => 'image/png',
|
||||
'type_gif' => 'image/gif',
|
||||
'type_mp4' => 'video/mp4',
|
||||
'type_webp' => 'image/webp',
|
||||
];
|
||||
ConfigCacheService::put('pixelfed.open_registration', (bool) $orb);
|
||||
ConfigCacheService::put('instance.curated_registration.enabled', (bool) $cob);
|
||||
|
||||
foreach ($mimes as $key => $value) {
|
||||
if($request->input($key) == 'on') {
|
||||
if(!in_array($value, $media_types)) {
|
||||
array_push($media_types, $value);
|
||||
}
|
||||
} else {
|
||||
$media_types = array_diff($media_types, [$value]);
|
||||
}
|
||||
}
|
||||
if($request->filled('admin_account_id')) {
|
||||
ConfigCacheService::put('instance.admin.pid', $request->admin_account_id);
|
||||
Cache::forget('api:v1:instance-data:contact');
|
||||
Cache::forget('api:v1:instance-data-response-v1');
|
||||
}
|
||||
if($request->filled('rule_delete')) {
|
||||
$index = (int) $request->input('rule_delete');
|
||||
$rules = ConfigCacheService::get('app.rules');
|
||||
$json = json_decode($rules, true);
|
||||
if(!$rules || empty($json)) {
|
||||
return;
|
||||
}
|
||||
unset($json[$index]);
|
||||
$json = json_encode(array_values($json));
|
||||
ConfigCacheService::put('app.rules', $json);
|
||||
Cache::forget('api:v1:instance-data:rules');
|
||||
Cache::forget('api:v1:instance-data-response-v1');
|
||||
return 200;
|
||||
}
|
||||
|
||||
if($media_types !== $media_types_original) {
|
||||
ConfigCacheService::put('pixelfed.media_types', implode(',', array_unique($media_types)));
|
||||
}
|
||||
$media_types = explode(',', config_cache('pixelfed.media_types'));
|
||||
$media_types_original = $media_types;
|
||||
|
||||
$keys = [
|
||||
'name' => 'app.name',
|
||||
'short_description' => 'app.short_description',
|
||||
'long_description' => 'app.description',
|
||||
'max_photo_size' => 'pixelfed.max_photo_size',
|
||||
'max_album_length' => 'pixelfed.max_album_length',
|
||||
'image_quality' => 'pixelfed.image_quality',
|
||||
'account_limit' => 'pixelfed.max_account_size',
|
||||
'custom_css' => 'uikit.custom.css',
|
||||
'custom_js' => 'uikit.custom.js',
|
||||
'about_title' => 'about.title'
|
||||
];
|
||||
$mimes = [
|
||||
'type_jpeg' => 'image/jpeg',
|
||||
'type_png' => 'image/png',
|
||||
'type_gif' => 'image/gif',
|
||||
'type_mp4' => 'video/mp4',
|
||||
'type_webp' => 'image/webp',
|
||||
];
|
||||
|
||||
foreach ($keys as $key => $value) {
|
||||
$cc = ConfigCache::whereK($value)->first();
|
||||
$val = $request->input($key);
|
||||
if($cc && $cc->v != $val) {
|
||||
ConfigCacheService::put($value, $val);
|
||||
} else if(!empty($val)) {
|
||||
ConfigCacheService::put($value, $val);
|
||||
}
|
||||
}
|
||||
foreach ($mimes as $key => $value) {
|
||||
if($request->input($key) == 'on') {
|
||||
if(!in_array($value, $media_types)) {
|
||||
array_push($media_types, $value);
|
||||
}
|
||||
} else {
|
||||
$media_types = array_diff($media_types, [$value]);
|
||||
}
|
||||
}
|
||||
|
||||
$bools = [
|
||||
'activitypub' => 'federation.activitypub.enabled',
|
||||
'open_registration' => 'pixelfed.open_registration',
|
||||
'mobile_apis' => 'pixelfed.oauth_enabled',
|
||||
'stories' => 'instance.stories.enabled',
|
||||
'ig_import' => 'pixelfed.import.instagram.enabled',
|
||||
'spam_detection' => 'pixelfed.bouncer.enabled',
|
||||
'require_email_verification' => 'pixelfed.enforce_email_verification',
|
||||
'enforce_account_limit' => 'pixelfed.enforce_account_limit',
|
||||
'show_custom_css' => 'uikit.show_custom.css',
|
||||
'show_custom_js' => 'uikit.show_custom.js',
|
||||
'cloud_storage' => 'pixelfed.cloud_storage',
|
||||
'account_autofollow' => 'account.autofollow',
|
||||
'show_directory' => 'instance.landing.show_directory',
|
||||
'show_explore_feed' => 'instance.landing.show_explore',
|
||||
];
|
||||
if($media_types !== $media_types_original) {
|
||||
ConfigCacheService::put('pixelfed.media_types', implode(',', array_unique($media_types)));
|
||||
}
|
||||
|
||||
foreach ($bools as $key => $value) {
|
||||
$active = $request->input($key) == 'on';
|
||||
$keys = [
|
||||
'name' => 'app.name',
|
||||
'short_description' => 'app.short_description',
|
||||
'long_description' => 'app.description',
|
||||
'max_photo_size' => 'pixelfed.max_photo_size',
|
||||
'max_album_length' => 'pixelfed.max_album_length',
|
||||
'image_quality' => 'pixelfed.image_quality',
|
||||
'account_limit' => 'pixelfed.max_account_size',
|
||||
'custom_css' => 'uikit.custom.css',
|
||||
'custom_js' => 'uikit.custom.js',
|
||||
'about_title' => 'about.title'
|
||||
];
|
||||
|
||||
if($key == 'activitypub' && $active && !InstanceActor::exists()) {
|
||||
Artisan::call('instance:actor');
|
||||
}
|
||||
foreach ($keys as $key => $value) {
|
||||
$cc = ConfigCache::whereK($value)->first();
|
||||
$val = $request->input($key);
|
||||
if($cc && $cc->v != $val) {
|
||||
ConfigCacheService::put($value, $val);
|
||||
} else if(!empty($val)) {
|
||||
ConfigCacheService::put($value, $val);
|
||||
}
|
||||
}
|
||||
|
||||
if( $key == 'mobile_apis' &&
|
||||
$active &&
|
||||
!file_exists(storage_path('oauth-public.key')) &&
|
||||
!file_exists(storage_path('oauth-private.key'))
|
||||
) {
|
||||
Artisan::call('passport:keys');
|
||||
Artisan::call('route:cache');
|
||||
}
|
||||
$bools = [
|
||||
'activitypub' => 'federation.activitypub.enabled',
|
||||
// 'open_registration' => 'pixelfed.open_registration',
|
||||
'mobile_apis' => 'pixelfed.oauth_enabled',
|
||||
'stories' => 'instance.stories.enabled',
|
||||
'ig_import' => 'pixelfed.import.instagram.enabled',
|
||||
'spam_detection' => 'pixelfed.bouncer.enabled',
|
||||
'require_email_verification' => 'pixelfed.enforce_email_verification',
|
||||
'enforce_account_limit' => 'pixelfed.enforce_account_limit',
|
||||
'show_custom_css' => 'uikit.show_custom.css',
|
||||
'show_custom_js' => 'uikit.show_custom.js',
|
||||
'cloud_storage' => 'pixelfed.cloud_storage',
|
||||
'account_autofollow' => 'account.autofollow',
|
||||
'show_directory' => 'instance.landing.show_directory',
|
||||
'show_explore_feed' => 'instance.landing.show_explore',
|
||||
];
|
||||
|
||||
if(config_cache($value) !== $active) {
|
||||
ConfigCacheService::put($value, (bool) $active);
|
||||
}
|
||||
}
|
||||
foreach ($bools as $key => $value) {
|
||||
$active = $request->input($key) == 'on';
|
||||
|
||||
if($request->filled('new_rule')) {
|
||||
$rules = ConfigCacheService::get('app.rules');
|
||||
$val = $request->input('new_rule');
|
||||
if(!$rules) {
|
||||
ConfigCacheService::put('app.rules', json_encode([$val]));
|
||||
} else {
|
||||
$json = json_decode($rules, true);
|
||||
$json[] = $val;
|
||||
ConfigCacheService::put('app.rules', json_encode(array_values($json)));
|
||||
}
|
||||
Cache::forget('api:v1:instance-data:rules');
|
||||
Cache::forget('api:v1:instance-data-response-v1');
|
||||
}
|
||||
if($key == 'activitypub' && $active && !InstanceActor::exists()) {
|
||||
Artisan::call('instance:actor');
|
||||
}
|
||||
|
||||
if($request->filled('account_autofollow_usernames')) {
|
||||
$usernames = explode(',', $request->input('account_autofollow_usernames'));
|
||||
$names = [];
|
||||
if( $key == 'mobile_apis' &&
|
||||
$active &&
|
||||
!file_exists(storage_path('oauth-public.key')) &&
|
||||
!file_exists(storage_path('oauth-private.key'))
|
||||
) {
|
||||
Artisan::call('passport:keys');
|
||||
Artisan::call('route:cache');
|
||||
}
|
||||
|
||||
foreach($usernames as $n) {
|
||||
$p = Profile::whereUsername($n)->first();
|
||||
if(!$p) {
|
||||
continue;
|
||||
}
|
||||
array_push($names, $p->username);
|
||||
}
|
||||
if(config_cache($value) !== $active) {
|
||||
ConfigCacheService::put($value, (bool) $active);
|
||||
}
|
||||
}
|
||||
|
||||
ConfigCacheService::put('account.autofollow_usernames', implode(',', $names));
|
||||
}
|
||||
if($request->filled('new_rule')) {
|
||||
$rules = ConfigCacheService::get('app.rules');
|
||||
$val = $request->input('new_rule');
|
||||
if(!$rules) {
|
||||
ConfigCacheService::put('app.rules', json_encode([$val]));
|
||||
} else {
|
||||
$json = json_decode($rules, true);
|
||||
$json[] = $val;
|
||||
ConfigCacheService::put('app.rules', json_encode(array_values($json)));
|
||||
}
|
||||
Cache::forget('api:v1:instance-data:rules');
|
||||
Cache::forget('api:v1:instance-data-response-v1');
|
||||
}
|
||||
|
||||
Cache::forget(Config::CACHE_KEY);
|
||||
if($request->filled('account_autofollow_usernames')) {
|
||||
$usernames = explode(',', $request->input('account_autofollow_usernames'));
|
||||
$names = [];
|
||||
|
||||
return redirect('/i/admin/settings')->with('status', 'Successfully updated settings!');
|
||||
}
|
||||
foreach($usernames as $n) {
|
||||
$p = Profile::whereUsername($n)->first();
|
||||
if(!$p) {
|
||||
continue;
|
||||
}
|
||||
array_push($names, $p->username);
|
||||
}
|
||||
|
||||
public function settingsBackups(Request $request)
|
||||
{
|
||||
$path = storage_path('app/'.config('app.name'));
|
||||
$files = is_dir($path) ? new \DirectoryIterator($path) : [];
|
||||
return view('admin.settings.backups', compact('files'));
|
||||
}
|
||||
ConfigCacheService::put('account.autofollow_usernames', implode(',', $names));
|
||||
}
|
||||
|
||||
public function settingsMaintenance(Request $request)
|
||||
{
|
||||
return view('admin.settings.maintenance');
|
||||
}
|
||||
Cache::forget(Config::CACHE_KEY);
|
||||
|
||||
public function settingsStorage(Request $request)
|
||||
{
|
||||
$storage = [];
|
||||
return view('admin.settings.storage', compact('storage'));
|
||||
}
|
||||
return redirect('/i/admin/settings')->with('status', 'Successfully updated settings!');
|
||||
}
|
||||
|
||||
public function settingsFeatures(Request $request)
|
||||
{
|
||||
return view('admin.settings.features');
|
||||
}
|
||||
public function settingsBackups(Request $request)
|
||||
{
|
||||
$path = storage_path('app/'.config('app.name'));
|
||||
$files = is_dir($path) ? new \DirectoryIterator($path) : [];
|
||||
return view('admin.settings.backups', compact('files'));
|
||||
}
|
||||
|
||||
public function settingsPages(Request $request)
|
||||
{
|
||||
$pages = Page::orderByDesc('updated_at')->paginate(10);
|
||||
return view('admin.pages.home', compact('pages'));
|
||||
}
|
||||
public function settingsMaintenance(Request $request)
|
||||
{
|
||||
return view('admin.settings.maintenance');
|
||||
}
|
||||
|
||||
public function settingsPageEdit(Request $request)
|
||||
{
|
||||
return view('admin.pages.edit');
|
||||
}
|
||||
public function settingsStorage(Request $request)
|
||||
{
|
||||
$storage = [];
|
||||
return view('admin.settings.storage', compact('storage'));
|
||||
}
|
||||
|
||||
public function settingsSystem(Request $request)
|
||||
{
|
||||
$sys = [
|
||||
'pixelfed' => config('pixelfed.version'),
|
||||
'php' => phpversion(),
|
||||
'laravel' => app()->version(),
|
||||
];
|
||||
switch (config('database.default')) {
|
||||
case 'pgsql':
|
||||
$exp = DB::raw('select version();');
|
||||
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
|
||||
$sys['database'] = [
|
||||
'name' => 'Postgres',
|
||||
'version' => explode(' ', DB::select($expQuery)[0]->version)[1]
|
||||
];
|
||||
break;
|
||||
public function settingsFeatures(Request $request)
|
||||
{
|
||||
return view('admin.settings.features');
|
||||
}
|
||||
|
||||
case 'mysql':
|
||||
$exp = DB::raw('select version()');
|
||||
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
|
||||
$sys['database'] = [
|
||||
'name' => 'MySQL',
|
||||
'version' => DB::select($expQuery)[0]->{'version()'}
|
||||
];
|
||||
break;
|
||||
public function settingsPages(Request $request)
|
||||
{
|
||||
$pages = Page::orderByDesc('updated_at')->paginate(10);
|
||||
return view('admin.pages.home', compact('pages'));
|
||||
}
|
||||
|
||||
default:
|
||||
$sys['database'] = [
|
||||
'name' => 'Unknown',
|
||||
'version' => '?'
|
||||
];
|
||||
break;
|
||||
}
|
||||
return view('admin.settings.system', compact('sys'));
|
||||
}
|
||||
public function settingsPageEdit(Request $request)
|
||||
{
|
||||
return view('admin.pages.edit');
|
||||
}
|
||||
|
||||
public function settingsSystem(Request $request)
|
||||
{
|
||||
$sys = [
|
||||
'pixelfed' => config('pixelfed.version'),
|
||||
'php' => phpversion(),
|
||||
'laravel' => app()->version(),
|
||||
];
|
||||
switch (config('database.default')) {
|
||||
case 'pgsql':
|
||||
$exp = DB::raw('select version();');
|
||||
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
|
||||
$sys['database'] = [
|
||||
'name' => 'Postgres',
|
||||
'version' => explode(' ', DB::select($expQuery)[0]->version)[1]
|
||||
];
|
||||
break;
|
||||
|
||||
case 'mysql':
|
||||
$exp = DB::raw('select version()');
|
||||
$expQuery = $exp->getValue(DB::connection()->getQueryGrammar());
|
||||
$sys['database'] = [
|
||||
'name' => 'MySQL',
|
||||
'version' => DB::select($expQuery)[0]->{'version()'}
|
||||
];
|
||||
break;
|
||||
|
||||
default:
|
||||
$sys['database'] = [
|
||||
'name' => 'Unknown',
|
||||
'version' => '?'
|
||||
];
|
||||
break;
|
||||
}
|
||||
return view('admin.settings.system', compact('sys'));
|
||||
}
|
||||
}
|
||||
|
|
226
app/Http/Controllers/AdminCuratedRegisterController.php
Normal file
226
app/Http/Controllers/AdminCuratedRegisterController.php
Normal file
|
@ -0,0 +1,226 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\CuratedRegister;
|
||||
use App\Models\CuratedRegisterActivity;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Mail\CuratedRegisterRequestDetailsFromUser;
|
||||
use App\Mail\CuratedRegisterAcceptUser;
|
||||
use App\Mail\CuratedRegisterRejectUser;
|
||||
use App\User;
|
||||
|
||||
class AdminCuratedRegisterController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(['auth','admin']);
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'filter' => 'sometimes|in:open,all,awaiting,approved,rejected'
|
||||
]);
|
||||
$filter = $request->input('filter', 'open');
|
||||
$records = CuratedRegister::when($filter, function($q, $filter) {
|
||||
if($filter === 'open') {
|
||||
return $q->where('is_rejected', false)
|
||||
->whereNotNull('email_verified_at')
|
||||
->whereIsClosed(false);
|
||||
} else if($filter === 'all') {
|
||||
return $q;
|
||||
} elseif ($filter === 'awaiting') {
|
||||
return $q->whereIsClosed(false)
|
||||
->whereNull('is_rejected')
|
||||
->whereNull('is_approved');
|
||||
} elseif ($filter === 'approved') {
|
||||
return $q->whereIsClosed(true)->whereIsApproved(true);
|
||||
} elseif ($filter === 'rejected') {
|
||||
return $q->whereIsClosed(true)->whereIsRejected(true);
|
||||
}
|
||||
})
|
||||
->latest()
|
||||
->paginate(10);
|
||||
return view('admin.curated-register.index', compact('records', 'filter'));
|
||||
}
|
||||
|
||||
public function show(Request $request, $id)
|
||||
{
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
return view('admin.curated-register.show', compact('record'));
|
||||
}
|
||||
|
||||
public function apiActivityLog(Request $request, $id)
|
||||
{
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
|
||||
$res = collect([
|
||||
[
|
||||
'id' => 1,
|
||||
'action' => 'created',
|
||||
'title' => 'Onboarding application created',
|
||||
'message' => null,
|
||||
'link' => null,
|
||||
'timestamp' => $record->created_at,
|
||||
]
|
||||
]);
|
||||
|
||||
if($record->email_verified_at) {
|
||||
$res->push([
|
||||
'id' => 3,
|
||||
'action' => 'email_verified_at',
|
||||
'title' => 'Applicant successfully verified email address',
|
||||
'message' => null,
|
||||
'link' => null,
|
||||
'timestamp' => $record->email_verified_at,
|
||||
]);
|
||||
}
|
||||
|
||||
$activities = CuratedRegisterActivity::whereRegisterId($record->id)->get();
|
||||
|
||||
$idx = 4;
|
||||
$userResponses = collect([]);
|
||||
|
||||
foreach($activities as $activity) {
|
||||
$idx++;
|
||||
if($activity->from_user) {
|
||||
$userResponses->push($activity);
|
||||
continue;
|
||||
}
|
||||
$res->push([
|
||||
'id' => $idx,
|
||||
'aid' => $activity->id,
|
||||
'action' => $activity->type,
|
||||
'title' => $activity->from_admin ? 'Admin requested info' : 'User responded',
|
||||
'message' => $activity->message,
|
||||
'link' => $activity->adminReviewUrl(),
|
||||
'timestamp' => $activity->created_at,
|
||||
]);
|
||||
}
|
||||
|
||||
foreach($userResponses as $ur) {
|
||||
$res = $res->map(function($r) use($ur) {
|
||||
if(!isset($r['aid'])) {
|
||||
return $r;
|
||||
}
|
||||
if($ur->reply_to_id === $r['aid']) {
|
||||
$r['user_response'] = $ur;
|
||||
return $r;
|
||||
}
|
||||
return $r;
|
||||
});
|
||||
}
|
||||
|
||||
if($record->is_approved) {
|
||||
$idx++;
|
||||
$res->push([
|
||||
'id' => $idx,
|
||||
'action' => 'approved',
|
||||
'title' => 'Application Approved',
|
||||
'message' => null,
|
||||
'link' => null,
|
||||
'timestamp' => $record->action_taken_at,
|
||||
]);
|
||||
} else if ($record->is_rejected) {
|
||||
$idx++;
|
||||
$res->push([
|
||||
'id' => $idx,
|
||||
'action' => 'rejected',
|
||||
'title' => 'Application Rejected',
|
||||
'message' => null,
|
||||
'link' => null,
|
||||
'timestamp' => $record->action_taken_at,
|
||||
]);
|
||||
}
|
||||
|
||||
return $res->reverse()->values();
|
||||
}
|
||||
|
||||
public function apiMessagePreviewStore(Request $request, $id)
|
||||
{
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
return $request->all();
|
||||
}
|
||||
|
||||
public function apiMessageSendStore(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'message' => 'required|string|min:5|max:1000'
|
||||
]);
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
|
||||
$activity = new CuratedRegisterActivity;
|
||||
$activity->register_id = $record->id;
|
||||
$activity->admin_id = $request->user()->id;
|
||||
$activity->secret_code = Str::random(32);
|
||||
$activity->type = 'request_details';
|
||||
$activity->from_admin = true;
|
||||
$activity->message = $request->input('message');
|
||||
$activity->save();
|
||||
$record->is_awaiting_more_info = true;
|
||||
$record->save();
|
||||
Mail::to($record->email)->send(new CuratedRegisterRequestDetailsFromUser($record, $activity));
|
||||
return $request->all();
|
||||
}
|
||||
|
||||
public function previewDetailsMessageShow(Request $request, $id)
|
||||
{
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
|
||||
$activity = new CuratedRegisterActivity;
|
||||
$activity->message = $request->input('message');
|
||||
return new \App\Mail\CuratedRegisterRequestDetailsFromUser($record, $activity);
|
||||
}
|
||||
|
||||
|
||||
public function previewMessageShow(Request $request, $id)
|
||||
{
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
abort_if($record->email_verified_at === null, 400, 'Cannot message an unverified email');
|
||||
$record->message = $request->input('message');
|
||||
return new \App\Mail\CuratedRegisterSendMessage($record);
|
||||
}
|
||||
|
||||
public function apiHandleReject(Request $request, $id)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'action' => 'required|in:reject-email,reject-silent'
|
||||
]);
|
||||
$action = $request->input('action');
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
abort_if($record->email_verified_at === null, 400, 'Cannot reject an unverified email');
|
||||
$record->is_rejected = true;
|
||||
$record->is_closed = true;
|
||||
$record->action_taken_at = now();
|
||||
$record->save();
|
||||
if($action === 'reject-email') {
|
||||
Mail::to($record->email)->send(new CuratedRegisterRejectUser($record));
|
||||
}
|
||||
return [200];
|
||||
}
|
||||
|
||||
public function apiHandleApprove(Request $request, $id)
|
||||
{
|
||||
$record = CuratedRegister::findOrFail($id);
|
||||
abort_if($record->email_verified_at === null, 400, 'Cannot reject an unverified email');
|
||||
$record->is_approved = true;
|
||||
$record->is_closed = true;
|
||||
$record->action_taken_at = now();
|
||||
$record->save();
|
||||
$user = User::create([
|
||||
'name' => $record->username,
|
||||
'username' => $record->username,
|
||||
'email' => $record->email,
|
||||
'password' => $record->password,
|
||||
'app_register_ip' => $record->ip_address,
|
||||
'email_verified_at' => now(),
|
||||
'register_source' => 'cur_onboarding'
|
||||
]);
|
||||
|
||||
Mail::to($record->email)->send(new CuratedRegisterAcceptUser($record));
|
||||
return [200];
|
||||
}
|
||||
}
|
|
@ -496,9 +496,12 @@ class ApiV1Controller extends Controller
|
|||
abort_if(!$account, 404);
|
||||
$pid = $request->user()->profile_id;
|
||||
$this->validate($request, [
|
||||
'limit' => 'sometimes|integer|min:1|max:80'
|
||||
'limit' => 'sometimes|integer|min:1'
|
||||
]);
|
||||
$limit = $request->input('limit', 10);
|
||||
if($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
$napi = $request->has(self::PF_API_ENTITY_KEY);
|
||||
|
||||
if($account && strpos($account['acct'], '@') != -1) {
|
||||
|
@ -594,9 +597,12 @@ class ApiV1Controller extends Controller
|
|||
abort_if(!$account, 404);
|
||||
$pid = $request->user()->profile_id;
|
||||
$this->validate($request, [
|
||||
'limit' => 'sometimes|integer|min:1|max:80'
|
||||
'limit' => 'sometimes|integer|min:1'
|
||||
]);
|
||||
$limit = $request->input('limit', 10);
|
||||
if($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
$napi = $request->has(self::PF_API_ENTITY_KEY);
|
||||
|
||||
if($account && strpos($account['acct'], '@') != -1) {
|
||||
|
@ -698,7 +704,7 @@ class ApiV1Controller extends Controller
|
|||
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'since_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'limit' => 'nullable|integer|min:1|max:100'
|
||||
'limit' => 'nullable|integer|min:1'
|
||||
]);
|
||||
|
||||
$napi = $request->has(self::PF_API_ENTITY_KEY);
|
||||
|
@ -713,7 +719,10 @@ class ApiV1Controller extends Controller
|
|||
abort_if(in_array($domain, InstanceService::getBannedDomains()), 404);
|
||||
}
|
||||
|
||||
$limit = $request->limit ?? 20;
|
||||
$limit = $request->input('limit') ?? 20;
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
$max_id = $request->max_id;
|
||||
$min_id = $request->min_id;
|
||||
|
||||
|
@ -959,12 +968,16 @@ class ApiV1Controller extends Controller
|
|||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'id' => 'required|array|min:1|max:20',
|
||||
'id' => 'required|array|min:1',
|
||||
'id.*' => 'required|integer|min:1|max:' . PHP_INT_MAX
|
||||
]);
|
||||
$ids = $request->input('id');
|
||||
if(count($ids) > 20) {
|
||||
$ids = collect($ids)->take(20)->toArray();
|
||||
}
|
||||
$napi = $request->has(self::PF_API_ENTITY_KEY);
|
||||
$pid = $request->user()->profile_id ?? $request->user()->profile->id;
|
||||
$res = collect($request->input('id'))
|
||||
$res = collect($ids)
|
||||
->filter(function($id) use($pid) {
|
||||
return intval($id) !== intval($pid);
|
||||
})
|
||||
|
@ -989,8 +1002,8 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'q' => 'required|string|min:1|max:255',
|
||||
'limit' => 'nullable|integer|min:1|max:40',
|
||||
'q' => 'required|string|min:1|max:30',
|
||||
'limit' => 'nullable|integer|min:1',
|
||||
'resolve' => 'nullable'
|
||||
]);
|
||||
|
||||
|
@ -1000,22 +1013,23 @@ class ApiV1Controller extends Controller
|
|||
AccountService::setLastActive($user->id);
|
||||
$query = $request->input('q');
|
||||
$limit = $request->input('limit') ?? 20;
|
||||
$resolve = (bool) $request->input('resolve', false);
|
||||
$q = '%' . $query . '%';
|
||||
if($limit > 20) {
|
||||
$limit = 20;
|
||||
}
|
||||
$resolve = $request->boolean('resolve', false);
|
||||
$q = $query . '%';
|
||||
|
||||
$profiles = Cache::remember('api:v1:accounts:search:' . sha1($query) . ':limit:' . $limit, 86400, function() use($q, $limit) {
|
||||
return Profile::whereNull('status')
|
||||
->where('username', 'like', $q)
|
||||
->orWhere('name', 'like', $q)
|
||||
->limit($limit)
|
||||
->pluck('id')
|
||||
->map(function($id) {
|
||||
return AccountService::getMastodon($id);
|
||||
})
|
||||
->filter(function($account) {
|
||||
return $account && isset($account['id']);
|
||||
});
|
||||
});
|
||||
$profiles = Profile::where('username', 'like', $q)
|
||||
->orderByDesc('followers_count')
|
||||
->limit($limit)
|
||||
->pluck('id')
|
||||
->map(function($id) {
|
||||
return AccountService::getMastodon($id);
|
||||
})
|
||||
->filter(function($account) {
|
||||
return $account && isset($account['id']);
|
||||
})
|
||||
->values();
|
||||
|
||||
return $this->json($profiles);
|
||||
}
|
||||
|
@ -1033,20 +1047,25 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'nullable|integer|min:1|max:40',
|
||||
'page' => 'nullable|integer|min:1|max:10'
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'page' => 'sometimes|integer|min:1'
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
$limit = $request->input('limit') ?? 40;
|
||||
if($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
|
||||
$blocked = UserFilter::select('filterable_id','filterable_type','filter_type','user_id')
|
||||
$blocks = UserFilter::select('filterable_id','filterable_type','filter_type','user_id')
|
||||
->whereUserId($user->profile_id)
|
||||
->whereFilterableType('App\Profile')
|
||||
->whereFilterType('block')
|
||||
->orderByDesc('id')
|
||||
->simplePaginate($limit)
|
||||
->pluck('filterable_id')
|
||||
->withQueryString();
|
||||
|
||||
$res = $blocks->pluck('filterable_id')
|
||||
->map(function($id) {
|
||||
return AccountService::get($id, true);
|
||||
})
|
||||
|
@ -1055,7 +1074,23 @@ class ApiV1Controller extends Controller
|
|||
})
|
||||
->values();
|
||||
|
||||
return $this->json($blocked);
|
||||
$baseUrl = config('app.url') . '/api/v1/blocks?limit=' . $limit . '&';
|
||||
$next = $blocks->nextPageUrl();
|
||||
$prev = $blocks->previousPageUrl();
|
||||
|
||||
if($next && !$prev) {
|
||||
$link = '<'.$next.'>; rel="next"';
|
||||
}
|
||||
|
||||
if(!$next && $prev) {
|
||||
$link = '<'.$prev.'>; rel="prev"';
|
||||
}
|
||||
|
||||
if($next && $prev) {
|
||||
$link = '<'.$next.'>; rel="next",<'.$prev.'>; rel="prev"';
|
||||
}
|
||||
$headers = isset($link) ? ['Link' => $link] : [];
|
||||
return $this->json($res, 200, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1247,13 +1282,16 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'sometimes|integer|min:1|max:40'
|
||||
'limit' => 'sometimes|integer|min:1'
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
$maxId = $request->input('max_id');
|
||||
$minId = $request->input('min_id');
|
||||
$limit = $request->input('limit') ?? 10;
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
|
||||
$res = Like::whereProfileId($user->profile_id)
|
||||
->when($maxId, function($q, $maxId) {
|
||||
|
@ -1612,15 +1650,15 @@ class ApiV1Controller extends Controller
|
|||
'short_description' => config_cache('app.short_description'),
|
||||
'description' => config_cache('app.description'),
|
||||
'email' => config('instance.email'),
|
||||
'version' => '2.7.2 (compatible; Pixelfed ' . config('pixelfed.version') .')',
|
||||
'version' => '3.5.3 (compatible; Pixelfed ' . config('pixelfed.version') .')',
|
||||
'urls' => [
|
||||
'streaming_api' => 'wss://' . config('pixelfed.domain.app')
|
||||
'streaming_api' => null,
|
||||
],
|
||||
'stats' => $stats,
|
||||
'thumbnail' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
|
||||
'languages' => [config('app.locale')],
|
||||
'registrations' => (bool) config_cache('pixelfed.open_registration'),
|
||||
'approval_required' => false,
|
||||
'approval_required' => (bool) config_cache('instance.curated_registration.enabled'),
|
||||
'contact_account' => $contact,
|
||||
'rules' => $rules,
|
||||
'configuration' => [
|
||||
|
@ -2049,18 +2087,23 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'nullable|integer|min:1|max:40'
|
||||
'limit' => 'sometimes|integer|min:1'
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
$limit = $request->input('limit', 40);
|
||||
if($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
|
||||
$mutes = UserFilter::whereUserId($user->profile_id)
|
||||
->whereFilterableType('App\Profile')
|
||||
->whereFilterType('mute')
|
||||
->orderByDesc('id')
|
||||
->simplePaginate($limit)
|
||||
->pluck('filterable_id')
|
||||
->withQueryString();
|
||||
|
||||
$res = $mutes->pluck('filterable_id')
|
||||
->map(function($id) {
|
||||
return AccountService::get($id, true);
|
||||
})
|
||||
|
@ -2069,7 +2112,23 @@ class ApiV1Controller extends Controller
|
|||
})
|
||||
->values();
|
||||
|
||||
return $this->json($mutes);
|
||||
$baseUrl = config('app.url') . '/api/v1/mutes?limit=' . $limit . '&';
|
||||
$next = $mutes->nextPageUrl();
|
||||
$prev = $mutes->previousPageUrl();
|
||||
|
||||
if($next && !$prev) {
|
||||
$link = '<'.$next.'>; rel="next"';
|
||||
}
|
||||
|
||||
if(!$next && $prev) {
|
||||
$link = '<'.$prev.'>; rel="prev"';
|
||||
}
|
||||
|
||||
if($next && $prev) {
|
||||
$link = '<'.$next.'>; rel="next",<'.$prev.'>; rel="prev"';
|
||||
}
|
||||
$headers = isset($link) ? ['Link' => $link] : [];
|
||||
return $this->json($res, 200, $headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2181,7 +2240,7 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'nullable|integer|min:1|max:100',
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'min_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
|
||||
'max_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
|
||||
'since_id' => 'nullable|integer|min:1|max:'.PHP_INT_MAX,
|
||||
|
@ -2191,6 +2250,9 @@ class ApiV1Controller extends Controller
|
|||
|
||||
$pid = $request->user()->profile_id;
|
||||
$limit = $request->input('limit', 20);
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
|
||||
$since = $request->input('since_id');
|
||||
$min = $request->input('min_id');
|
||||
|
@ -2200,6 +2262,10 @@ class ApiV1Controller extends Controller
|
|||
$min = 1;
|
||||
}
|
||||
|
||||
if($since) {
|
||||
$min = $since + 1;
|
||||
}
|
||||
|
||||
$types = $request->input('types');
|
||||
|
||||
$maxId = null;
|
||||
|
@ -2261,7 +2327,7 @@ class ApiV1Controller extends Controller
|
|||
'page' => 'sometimes|integer|max:40',
|
||||
'min_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'max_id' => 'sometimes|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'limit' => 'sometimes|integer|min:1|max:40',
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'include_reblogs' => 'sometimes',
|
||||
]);
|
||||
|
||||
|
@ -2270,6 +2336,9 @@ class ApiV1Controller extends Controller
|
|||
$min = $request->input('min_id');
|
||||
$max = $request->input('max_id');
|
||||
$limit = $request->input('limit') ?? 20;
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
$pid = $request->user()->profile_id;
|
||||
$includeReblogs = $request->filled('include_reblogs') ? $request->boolean('include_reblogs') : false;
|
||||
$nullFields = $includeReblogs ?
|
||||
|
@ -2515,7 +2584,7 @@ class ApiV1Controller extends Controller
|
|||
$this->validate($request,[
|
||||
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'limit' => 'nullable|integer|max:100',
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'remote' => 'sometimes',
|
||||
'local' => 'sometimes'
|
||||
]);
|
||||
|
@ -2525,6 +2594,9 @@ class ApiV1Controller extends Controller
|
|||
$max = $request->input('max_id');
|
||||
$minOrMax = $request->anyFilled(['max_id', 'min_id']);
|
||||
$limit = $request->input('limit') ?? 20;
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
$user = $request->user();
|
||||
|
||||
$remote = $request->has('remote');
|
||||
|
@ -3043,10 +3115,13 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'nullable|integer|min:1|max:80'
|
||||
'limit' => 'sometimes|integer|min:1'
|
||||
]);
|
||||
|
||||
$limit = $request->input('limit', 10);
|
||||
$limit = $request->input('limit', 40);
|
||||
if($limit > 80) {
|
||||
$limit = 80;
|
||||
}
|
||||
$user = $request->user();
|
||||
$pid = $user->profile_id;
|
||||
$status = Status::findOrFail($id);
|
||||
|
@ -3485,7 +3560,7 @@ class ApiV1Controller extends Controller
|
|||
'page' => 'nullable|integer|max:40',
|
||||
'min_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'max_id' => 'nullable|integer|min:0|max:' . PHP_INT_MAX,
|
||||
'limit' => 'nullable|integer|max:100',
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'only_media' => 'sometimes|boolean',
|
||||
'_pe' => 'sometimes'
|
||||
]);
|
||||
|
@ -3518,6 +3593,9 @@ class ApiV1Controller extends Controller
|
|||
$min = $request->input('min_id');
|
||||
$max = $request->input('max_id');
|
||||
$limit = $request->input('limit', 20);
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
$onlyMedia = $request->input('only_media', true);
|
||||
$pe = $request->has(self::PF_API_ENTITY_KEY);
|
||||
$pid = $request->user()->profile_id;
|
||||
|
@ -3547,7 +3625,7 @@ class ApiV1Controller extends Controller
|
|||
->whereStatusVisibility('public')
|
||||
->where('status_id', $dir, $id)
|
||||
->orderBy('status_id', 'desc')
|
||||
->limit($limit)
|
||||
->limit(100)
|
||||
->pluck('status_id')
|
||||
->map(function ($i) use($pe) {
|
||||
return $pe ? StatusService::get($i) : StatusService::getMastodon($i);
|
||||
|
@ -3565,6 +3643,7 @@ class ApiV1Controller extends Controller
|
|||
$domain = strtolower(parse_url($i['url'], PHP_URL_HOST));
|
||||
return !in_array($i['account']['id'], $filters) && !in_array($domain, $domainBlocks);
|
||||
})
|
||||
->take($limit)
|
||||
->values()
|
||||
->toArray();
|
||||
|
||||
|
@ -3584,7 +3663,7 @@ class ApiV1Controller extends Controller
|
|||
abort_unless($request->user()->tokenCan('read'), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'nullable|integer|min:1|max:40',
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'max_id' => 'nullable|integer|min:0',
|
||||
'since_id' => 'nullable|integer|min:0',
|
||||
'min_id' => 'nullable|integer|min:0'
|
||||
|
@ -3593,6 +3672,9 @@ class ApiV1Controller extends Controller
|
|||
$pe = $request->has('_pe');
|
||||
$pid = $request->user()->profile_id;
|
||||
$limit = $request->input('limit') ?? 20;
|
||||
if($limit > 40) {
|
||||
$limit = 40;
|
||||
}
|
||||
$max_id = $request->input('max_id');
|
||||
$since_id = $request->input('since_id');
|
||||
$min_id = $request->input('min_id');
|
||||
|
@ -3758,11 +3840,14 @@ class ApiV1Controller extends Controller
|
|||
abort_if(!$request->user(), 403);
|
||||
|
||||
$this->validate($request, [
|
||||
'limit' => 'int|min:1|max:10',
|
||||
'limit' => 'sometimes|integer|min:1',
|
||||
'sort' => 'in:all,newest,popular'
|
||||
]);
|
||||
|
||||
$limit = $request->input('limit', 3);
|
||||
if($limit > 10) {
|
||||
$limit = 10;
|
||||
}
|
||||
$pid = $request->user()->profile_id;
|
||||
$status = StatusService::getMastodon($id, false);
|
||||
|
||||
|
|
|
@ -71,72 +71,77 @@ class ApiV2Controller extends Controller
|
|||
->toArray() : [];
|
||||
});
|
||||
|
||||
$res = [
|
||||
'domain' => config('pixelfed.domain.app'),
|
||||
'title' => config_cache('app.name'),
|
||||
'version' => config('pixelfed.version'),
|
||||
'source_url' => 'https://github.com/pixelfed/pixelfed',
|
||||
'description' => config_cache('app.short_description'),
|
||||
'usage' => [
|
||||
'users' => [
|
||||
'active_month' => (int) Nodeinfo::activeUsersMonthly()
|
||||
]
|
||||
],
|
||||
'thumbnail' => [
|
||||
'url' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
|
||||
'blurhash' => InstanceService::headerBlurhash(),
|
||||
'versions' => [
|
||||
'@1x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
|
||||
'@2x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg'))
|
||||
]
|
||||
],
|
||||
'languages' => [config('app.locale')],
|
||||
'configuration' => [
|
||||
'urls' => [
|
||||
'streaming' => 'wss://' . config('pixelfed.domain.app'),
|
||||
'status' => null
|
||||
$res = Cache::remember('api:v2:instance-data-response-v2', 1800, function () use($contact, $rules) {
|
||||
return [
|
||||
'domain' => config('pixelfed.domain.app'),
|
||||
'title' => config_cache('app.name'),
|
||||
'version' => '3.5.3 (compatible; Pixelfed ' . config('pixelfed.version') .')',
|
||||
'source_url' => 'https://github.com/pixelfed/pixelfed',
|
||||
'description' => config_cache('app.short_description'),
|
||||
'usage' => [
|
||||
'users' => [
|
||||
'active_month' => (int) Nodeinfo::activeUsersMonthly()
|
||||
]
|
||||
],
|
||||
'vapid' => [
|
||||
'public_key' => config('webpush.vapid.public_key'),
|
||||
'thumbnail' => [
|
||||
'url' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
|
||||
'blurhash' => InstanceService::headerBlurhash(),
|
||||
'versions' => [
|
||||
'@1x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg')),
|
||||
'@2x' => config_cache('app.banner_image') ?? url(Storage::url('public/headers/default.jpg'))
|
||||
]
|
||||
],
|
||||
'accounts' => [
|
||||
'max_featured_tags' => 0,
|
||||
'languages' => [config('app.locale')],
|
||||
'configuration' => [
|
||||
'urls' => [
|
||||
'streaming' => null,
|
||||
'status' => null
|
||||
],
|
||||
'vapid' => [
|
||||
'public_key' => config('webpush.vapid.public_key'),
|
||||
],
|
||||
'accounts' => [
|
||||
'max_featured_tags' => 0,
|
||||
],
|
||||
'statuses' => [
|
||||
'max_characters' => (int) config('pixelfed.max_caption_length'),
|
||||
'max_media_attachments' => (int) config_cache('pixelfed.max_album_length'),
|
||||
'characters_reserved_per_url' => 23
|
||||
],
|
||||
'media_attachments' => [
|
||||
'supported_mime_types' => explode(',', config_cache('pixelfed.media_types')),
|
||||
'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
|
||||
'image_matrix_limit' => 3686400,
|
||||
'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
|
||||
'video_frame_rate_limit' => 240,
|
||||
'video_matrix_limit' => 3686400
|
||||
],
|
||||
'polls' => [
|
||||
'max_options' => 0,
|
||||
'max_characters_per_option' => 0,
|
||||
'min_expiration' => 0,
|
||||
'max_expiration' => 0,
|
||||
],
|
||||
'translation' => [
|
||||
'enabled' => false,
|
||||
],
|
||||
],
|
||||
'statuses' => [
|
||||
'max_characters' => (int) config('pixelfed.max_caption_length'),
|
||||
'max_media_attachments' => (int) config_cache('pixelfed.max_album_length'),
|
||||
'characters_reserved_per_url' => 23
|
||||
'registrations' => [
|
||||
'enabled' => null,
|
||||
'approval_required' => false,
|
||||
'message' => null,
|
||||
'url' => null,
|
||||
],
|
||||
'media_attachments' => [
|
||||
'supported_mime_types' => explode(',', config_cache('pixelfed.media_types')),
|
||||
'image_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
|
||||
'image_matrix_limit' => 3686400,
|
||||
'video_size_limit' => config_cache('pixelfed.max_photo_size') * 1024,
|
||||
'video_frame_rate_limit' => 240,
|
||||
'video_matrix_limit' => 3686400
|
||||
'contact' => [
|
||||
'email' => config('instance.email'),
|
||||
'account' => $contact
|
||||
],
|
||||
'polls' => [
|
||||
'max_options' => 4,
|
||||
'max_characters_per_option' => 50,
|
||||
'min_expiration' => 300,
|
||||
'max_expiration' => 2629746,
|
||||
],
|
||||
'translation' => [
|
||||
'enabled' => false,
|
||||
],
|
||||
],
|
||||
'registrations' => [
|
||||
'enabled' => (bool) config_cache('pixelfed.open_registration'),
|
||||
'approval_required' => false,
|
||||
'message' => null
|
||||
],
|
||||
'contact' => [
|
||||
'email' => config('instance.email'),
|
||||
'account' => $contact
|
||||
],
|
||||
'rules' => $rules
|
||||
];
|
||||
'rules' => $rules
|
||||
];
|
||||
});
|
||||
|
||||
$res['registrations']['enabled'] = (bool) config_cache('pixelfed.open_registration');
|
||||
$res['registrations']['approval_required'] = (bool) config_cache('instance.curated_registration.enabled');
|
||||
return response()->json($res, 200, [], JSON_UNESCAPED_SLASHES);
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ class RegisterController extends Controller
|
|||
*/
|
||||
public function showRegistrationForm()
|
||||
{
|
||||
if(config_cache('pixelfed.open_registration')) {
|
||||
if((bool) config_cache('pixelfed.open_registration')) {
|
||||
if(config('pixelfed.bouncer.cloud_ips.ban_signups')) {
|
||||
abort_if(BouncerService::checkIp(request()->ip()), 404);
|
||||
}
|
||||
|
@ -191,7 +191,11 @@ class RegisterController extends Controller
|
|||
return view('auth.register');
|
||||
}
|
||||
} else {
|
||||
abort(404);
|
||||
if((bool) config_cache('instance.curated_registration.enabled') && config('instance.curated_registration.state.fallback_on_closed_reg')) {
|
||||
return redirect('/auth/sign_up');
|
||||
} else {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
398
app/Http/Controllers/CuratedRegisterController.php
Normal file
398
app/Http/Controllers/CuratedRegisterController.php
Normal file
|
@ -0,0 +1,398 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Str;
|
||||
use App\User;
|
||||
use App\Models\CuratedRegister;
|
||||
use App\Models\CuratedRegisterActivity;
|
||||
use App\Services\EmailService;
|
||||
use App\Services\BouncerService;
|
||||
use App\Util\Lexer\RestrictedNames;
|
||||
use App\Mail\CuratedRegisterConfirmEmail;
|
||||
use App\Mail\CuratedRegisterNotifyAdmin;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use App\Jobs\CuratedOnboarding\CuratedOnboardingNotifyAdminNewApplicationPipeline;
|
||||
|
||||
class CuratedRegisterController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
abort_unless((bool) config_cache('instance.curated_registration.enabled'), 404);
|
||||
|
||||
if((bool) config_cache('pixelfed.open_registration')) {
|
||||
abort_if(config('instance.curated_registration.state.only_enabled_on_closed_reg'), 404);
|
||||
} else {
|
||||
abort_unless(config('instance.curated_registration.state.fallback_on_closed_reg'), 404);
|
||||
}
|
||||
}
|
||||
|
||||
public function index(Request $request)
|
||||
{
|
||||
abort_if($request->user(), 404);
|
||||
return view('auth.curated-register.index', ['step' => 1]);
|
||||
}
|
||||
|
||||
public function concierge(Request $request)
|
||||
{
|
||||
abort_if($request->user(), 404);
|
||||
$emailConfirmed = $request->session()->has('cur-reg-con.email-confirmed') &&
|
||||
$request->has('next') &&
|
||||
$request->session()->has('cur-reg-con.cr-id');
|
||||
return view('auth.curated-register.concierge', compact('emailConfirmed'));
|
||||
}
|
||||
|
||||
public function conciergeResponseSent(Request $request)
|
||||
{
|
||||
return view('auth.curated-register.user_response_sent');
|
||||
}
|
||||
|
||||
public function conciergeFormShow(Request $request)
|
||||
{
|
||||
abort_if($request->user(), 404);
|
||||
abort_unless(
|
||||
$request->session()->has('cur-reg-con.email-confirmed') &&
|
||||
$request->session()->has('cur-reg-con.cr-id') &&
|
||||
$request->session()->has('cur-reg-con.ac-id'), 404);
|
||||
$crid = $request->session()->get('cur-reg-con.cr-id');
|
||||
$arid = $request->session()->get('cur-reg-con.ac-id');
|
||||
$showCaptcha = config('instance.curated_registration.captcha_enabled');
|
||||
if($attempts = $request->session()->get('cur-reg-con-attempt')) {
|
||||
$showCaptcha = $attempts && $attempts >= 2;
|
||||
} else {
|
||||
$showCaptcha = false;
|
||||
}
|
||||
$activity = CuratedRegisterActivity::whereRegisterId($crid)->whereFromAdmin(true)->findOrFail($arid);
|
||||
return view('auth.curated-register.concierge_form', compact('activity', 'showCaptcha'));
|
||||
}
|
||||
|
||||
public function conciergeFormStore(Request $request)
|
||||
{
|
||||
abort_if($request->user(), 404);
|
||||
$request->session()->increment('cur-reg-con-attempt');
|
||||
abort_unless(
|
||||
$request->session()->has('cur-reg-con.email-confirmed') &&
|
||||
$request->session()->has('cur-reg-con.cr-id') &&
|
||||
$request->session()->has('cur-reg-con.ac-id'), 404);
|
||||
$attempts = $request->session()->get('cur-reg-con-attempt');
|
||||
$messages = [];
|
||||
$rules = [
|
||||
'response' => 'required|string|min:5|max:1000',
|
||||
'crid' => 'required|integer|min:1',
|
||||
'acid' => 'required|integer|min:1'
|
||||
];
|
||||
if(config('instance.curated_registration.captcha_enabled') && $attempts >= 3) {
|
||||
$rules['h-captcha-response'] = 'required|captcha';
|
||||
$messages['h-captcha-response.required'] = 'The captcha must be filled';
|
||||
}
|
||||
$this->validate($request, $rules, $messages);
|
||||
$crid = $request->session()->get('cur-reg-con.cr-id');
|
||||
$acid = $request->session()->get('cur-reg-con.ac-id');
|
||||
abort_if((string) $crid !== $request->input('crid'), 404);
|
||||
abort_if((string) $acid !== $request->input('acid'), 404);
|
||||
|
||||
if(CuratedRegisterActivity::whereRegisterId($crid)->whereReplyToId($acid)->exists()) {
|
||||
return redirect()->back()->withErrors(['code' => 'You already replied to this request.']);
|
||||
}
|
||||
|
||||
$act = CuratedRegisterActivity::create([
|
||||
'register_id' => $crid,
|
||||
'reply_to_id' => $acid,
|
||||
'type' => 'user_response',
|
||||
'message' => $request->input('response'),
|
||||
'from_user' => true,
|
||||
'action_required' => true,
|
||||
]);
|
||||
|
||||
$request->session()->pull('cur-reg-con');
|
||||
$request->session()->pull('cur-reg-con-attempt');
|
||||
|
||||
return view('auth.curated-register.user_response_sent');
|
||||
}
|
||||
|
||||
public function conciergeStore(Request $request)
|
||||
{
|
||||
abort_if($request->user(), 404);
|
||||
$rules = [
|
||||
'sid' => 'required_if:action,email|integer|min:1|max:20000000',
|
||||
'id' => 'required_if:action,email|integer|min:1|max:20000000',
|
||||
'code' => 'required_if:action,email',
|
||||
'action' => 'required|string|in:email,message',
|
||||
'email' => 'required_if:action,email|email',
|
||||
'response' => 'required_if:action,message|string|min:20|max:1000',
|
||||
];
|
||||
$messages = [];
|
||||
if(config('instance.curated_registration.captcha_enabled')) {
|
||||
$rules['h-captcha-response'] = 'required|captcha';
|
||||
$messages['h-captcha-response.required'] = 'The captcha must be filled';
|
||||
}
|
||||
$this->validate($request, $rules, $messages);
|
||||
|
||||
$action = $request->input('action');
|
||||
$sid = $request->input('sid');
|
||||
$id = $request->input('id');
|
||||
$code = $request->input('code');
|
||||
$email = $request->input('email');
|
||||
|
||||
$cr = CuratedRegister::whereIsClosed(false)->findOrFail($sid);
|
||||
$ac = CuratedRegisterActivity::whereRegisterId($cr->id)->whereFromAdmin(true)->findOrFail($id);
|
||||
|
||||
if(!hash_equals($ac->secret_code, $code)) {
|
||||
return redirect()->back()->withErrors(['code' => 'Invalid code']);
|
||||
}
|
||||
|
||||
if(!hash_equals($cr->email, $email)) {
|
||||
return redirect()->back()->withErrors(['email' => 'Invalid email']);
|
||||
}
|
||||
|
||||
$request->session()->put('cur-reg-con.email-confirmed', true);
|
||||
$request->session()->put('cur-reg-con.cr-id', $cr->id);
|
||||
$request->session()->put('cur-reg-con.ac-id', $ac->id);
|
||||
$emailConfirmed = true;
|
||||
return redirect('/auth/sign_up/concierge/form');
|
||||
}
|
||||
|
||||
public function confirmEmail(Request $request)
|
||||
{
|
||||
if($request->user()) {
|
||||
return redirect(route('help.email-confirmation-issues'));
|
||||
}
|
||||
return view('auth.curated-register.confirm_email');
|
||||
}
|
||||
|
||||
public function emailConfirmed(Request $request)
|
||||
{
|
||||
if($request->user()) {
|
||||
return redirect(route('help.email-confirmation-issues'));
|
||||
}
|
||||
return view('auth.curated-register.email_confirmed');
|
||||
}
|
||||
|
||||
public function resendConfirmation(Request $request)
|
||||
{
|
||||
return view('auth.curated-register.resend-confirmation');
|
||||
}
|
||||
|
||||
public function resendConfirmationProcess(Request $request)
|
||||
{
|
||||
$rules = [
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
|
||||
'exists:curated_registers',
|
||||
]
|
||||
];
|
||||
|
||||
$messages = [];
|
||||
|
||||
if(config('instance.curated_registration.captcha_enabled')) {
|
||||
$rules['h-captcha-response'] = 'required|captcha';
|
||||
$messages['h-captcha-response.required'] = 'The captcha must be filled';
|
||||
}
|
||||
|
||||
$this->validate($request, $rules, $messages);
|
||||
|
||||
$cur = CuratedRegister::whereEmail($request->input('email'))->whereIsClosed(false)->first();
|
||||
if(!$cur) {
|
||||
return redirect()->back()->withErrors(['email' => 'The selected email is invalid.']);
|
||||
}
|
||||
|
||||
$totalCount = CuratedRegisterActivity::whereRegisterId($cur->id)
|
||||
->whereType('user_resend_email_confirmation')
|
||||
->count();
|
||||
|
||||
if($totalCount && $totalCount >= config('instance.curated_registration.resend_confirmation_limit')) {
|
||||
return redirect()->back()->withErrors(['email' => 'You have re-attempted too many times. To proceed with your application, please <a href="/site/contact" class="text-white" style="text-decoration: underline;">contact the admin team</a>.']);
|
||||
}
|
||||
|
||||
$count = CuratedRegisterActivity::whereRegisterId($cur->id)
|
||||
->whereType('user_resend_email_confirmation')
|
||||
->where('created_at', '>', now()->subHours(12))
|
||||
->count();
|
||||
|
||||
if($count) {
|
||||
return redirect()->back()->withErrors(['email' => 'You can only re-send the confirmation email once per 12 hours. Try again later.']);
|
||||
}
|
||||
|
||||
CuratedRegisterActivity::create([
|
||||
'register_id' => $cur->id,
|
||||
'type' => 'user_resend_email_confirmation',
|
||||
'admin_only_view' => true,
|
||||
'from_admin' => false,
|
||||
'from_user' => false,
|
||||
'action_required' => false,
|
||||
]);
|
||||
|
||||
Mail::to($cur->email)->send(new CuratedRegisterConfirmEmail($cur));
|
||||
return view('auth.curated-register.resent-confirmation');
|
||||
return $request->all();
|
||||
}
|
||||
|
||||
public function confirmEmailHandle(Request $request)
|
||||
{
|
||||
$rules = [
|
||||
'sid' => 'required',
|
||||
'code' => 'required'
|
||||
];
|
||||
$messages = [];
|
||||
if(config('instance.curated_registration.captcha_enabled')) {
|
||||
$rules['h-captcha-response'] = 'required|captcha';
|
||||
$messages['h-captcha-response.required'] = 'The captcha must be filled';
|
||||
}
|
||||
$this->validate($request, $rules, $messages);
|
||||
|
||||
$cr = CuratedRegister::whereNull('email_verified_at')
|
||||
->where('created_at', '>', now()->subHours(24))
|
||||
->find($request->input('sid'));
|
||||
if(!$cr) {
|
||||
return redirect(route('help.email-confirmation-issues'));
|
||||
}
|
||||
if(!hash_equals($cr->verify_code, $request->input('code'))) {
|
||||
return redirect(route('help.email-confirmation-issues'));
|
||||
}
|
||||
$cr->email_verified_at = now();
|
||||
$cr->save();
|
||||
|
||||
if(config('instance.curated_registration.notify.admin.on_verify_email.enabled')) {
|
||||
CuratedOnboardingNotifyAdminNewApplicationPipeline::dispatch($cr);
|
||||
}
|
||||
return view('auth.curated-register.email_confirmed');
|
||||
}
|
||||
|
||||
public function proceed(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'step' => 'required|integer|in:1,2,3,4'
|
||||
]);
|
||||
$step = $request->input('step');
|
||||
|
||||
switch($step) {
|
||||
case 1:
|
||||
$step = 2;
|
||||
$request->session()->put('cur-step', 1);
|
||||
return view('auth.curated-register.index', compact('step'));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
$this->stepTwo($request);
|
||||
$step = 3;
|
||||
$request->session()->put('cur-step', 2);
|
||||
return view('auth.curated-register.index', compact('step'));
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$this->stepThree($request);
|
||||
$step = 3;
|
||||
$request->session()->put('cur-step', 3);
|
||||
$verifiedEmail = true;
|
||||
$request->session()->pull('cur-reg');
|
||||
return view('auth.curated-register.index', compact('step', 'verifiedEmail'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function stepTwo($request)
|
||||
{
|
||||
if($request->filled('reason')) {
|
||||
$request->session()->put('cur-reg.form-reason', $request->input('reason'));
|
||||
}
|
||||
if($request->filled('username')) {
|
||||
$request->session()->put('cur-reg.form-username', $request->input('username'));
|
||||
}
|
||||
if($request->filled('email')) {
|
||||
$request->session()->put('cur-reg.form-email', $request->input('email'));
|
||||
}
|
||||
$this->validate($request, [
|
||||
'username' => [
|
||||
'required',
|
||||
'min:2',
|
||||
'max:15',
|
||||
'unique:curated_registers',
|
||||
'unique:users',
|
||||
function ($attribute, $value, $fail) {
|
||||
$dash = substr_count($value, '-');
|
||||
$underscore = substr_count($value, '_');
|
||||
$period = substr_count($value, '.');
|
||||
|
||||
if(ends_with($value, ['.php', '.js', '.css'])) {
|
||||
return $fail('Username is invalid.');
|
||||
}
|
||||
|
||||
if(($dash + $underscore + $period) > 1) {
|
||||
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
|
||||
}
|
||||
|
||||
if (!ctype_alnum($value[0])) {
|
||||
return $fail('Username is invalid. Must start with a letter or number.');
|
||||
}
|
||||
|
||||
if (!ctype_alnum($value[strlen($value) - 1])) {
|
||||
return $fail('Username is invalid. Must end with a letter or number.');
|
||||
}
|
||||
|
||||
$val = str_replace(['_', '.', '-'], '', $value);
|
||||
if(!ctype_alnum($val)) {
|
||||
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
|
||||
}
|
||||
|
||||
$restricted = RestrictedNames::get();
|
||||
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
|
||||
return $fail('Username cannot be used.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
|
||||
'max:255',
|
||||
'unique:users',
|
||||
'unique:curated_registers',
|
||||
function ($attribute, $value, $fail) {
|
||||
$banned = EmailService::isBanned($value);
|
||||
if($banned) {
|
||||
return $fail('Email is invalid.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'password' => 'required|min:8',
|
||||
'password_confirmation' => 'required|same:password',
|
||||
'reason' => 'required|min:20|max:1000',
|
||||
'agree' => 'required|accepted'
|
||||
]);
|
||||
$request->session()->put('cur-reg.form-email', $request->input('email'));
|
||||
$request->session()->put('cur-reg.form-password', $request->input('password'));
|
||||
}
|
||||
|
||||
protected function stepThree($request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
app()->environment() === 'production' ? 'email:rfc,dns,spoof' : 'email',
|
||||
'max:255',
|
||||
'unique:users',
|
||||
'unique:curated_registers',
|
||||
function ($attribute, $value, $fail) {
|
||||
$banned = EmailService::isBanned($value);
|
||||
if($banned) {
|
||||
return $fail('Email is invalid.');
|
||||
}
|
||||
},
|
||||
]
|
||||
]);
|
||||
$cr = new CuratedRegister;
|
||||
$cr->email = $request->email;
|
||||
$cr->username = $request->session()->get('cur-reg.form-username');
|
||||
$cr->password = bcrypt($request->session()->get('cur-reg.form-password'));
|
||||
$cr->ip_address = $request->ip();
|
||||
$cr->reason_to_join = $request->session()->get('cur-reg.form-reason');
|
||||
$cr->verify_code = Str::random(40);
|
||||
$cr->save();
|
||||
|
||||
Mail::to($cr->email)->send(new CuratedRegisterConfirmEmail($cr));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs\CuratedOnboarding;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\CuratedRegister;
|
||||
use App\User;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Mail\CuratedRegisterNotifyAdmin;
|
||||
|
||||
class CuratedOnboardingNotifyAdminNewApplicationPipeline implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $cr;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(CuratedRegister $cr)
|
||||
{
|
||||
$this->cr = $cr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
if(!config('instance.curated_registration.notify.admin.on_verify_email.enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
config('instance.curated_registration.notify.admin.on_verify_email.bundle') ?
|
||||
$this->handleBundled() :
|
||||
$this->handleUnbundled();
|
||||
}
|
||||
|
||||
protected function handleBundled()
|
||||
{
|
||||
$cr = $this->cr;
|
||||
Storage::append('conanap.json', json_encode([
|
||||
'id' => $cr->id,
|
||||
'email' => $cr->email,
|
||||
'created_at' => $cr->created_at,
|
||||
'updated_at' => $cr->updated_at,
|
||||
]));
|
||||
}
|
||||
|
||||
protected function handleUnbundled()
|
||||
{
|
||||
$cr = $this->cr;
|
||||
if($aid = config_cache('instance.admin.pid')) {
|
||||
$admin = User::whereProfileId($aid)->first();
|
||||
if($admin && $admin->email) {
|
||||
Mail::to($admin->email)->send(new CuratedRegisterNotifyAdmin($cr));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
app/Mail/CuratedRegisterAcceptUser.php
Normal file
55
app/Mail/CuratedRegisterAcceptUser.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CuratedRegisterAcceptUser extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $verify;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($verify)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Your ' . config('pixelfed.domain.app') . ' Registration Update',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.request-accepted',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
55
app/Mail/CuratedRegisterConfirmEmail.php
Normal file
55
app/Mail/CuratedRegisterConfirmEmail.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\CuratedRegister;
|
||||
|
||||
class CuratedRegisterConfirmEmail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
public $verify;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(CuratedRegister $verify)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Welcome to Pixelfed! Please Confirm Your Email',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.confirm_email',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
55
app/Mail/CuratedRegisterNotifyAdmin.php
Normal file
55
app/Mail/CuratedRegisterNotifyAdmin.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\CuratedRegister;
|
||||
|
||||
class CuratedRegisterNotifyAdmin extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
public $verify;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(CuratedRegister $verify)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '[Requires Action]: New Curated Onboarding Application',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.admin_notify',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
55
app/Mail/CuratedRegisterNotifyAdminUserResponse.php
Normal file
55
app/Mail/CuratedRegisterNotifyAdminUserResponse.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CuratedRegisterNotifyAdminUserResponse extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $activity;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($activity)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Curated Register Notify Admin User Response',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.admin_notify_user_response',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
55
app/Mail/CuratedRegisterRejectUser.php
Normal file
55
app/Mail/CuratedRegisterRejectUser.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CuratedRegisterRejectUser extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $verify;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($verify)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Your ' . config('pixelfed.domain.app') . ' Registration Update',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.request-rejected',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
58
app/Mail/CuratedRegisterRequestDetailsFromUser.php
Normal file
58
app/Mail/CuratedRegisterRequestDetailsFromUser.php
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use App\Models\CuratedRegister;
|
||||
use App\Models\CuratedRegisterActivity;
|
||||
|
||||
class CuratedRegisterRequestDetailsFromUser extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
public $verify;
|
||||
public $activity;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(CuratedRegister $verify, CuratedRegisterActivity $activity)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
$this->activity = $activity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: '[Action Needed]: Additional information requested',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.request-details-from-user',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
55
app/Mail/CuratedRegisterSendMessage.php
Normal file
55
app/Mail/CuratedRegisterSendMessage.php
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class CuratedRegisterSendMessage extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $verify;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct($verify)
|
||||
{
|
||||
$this->verify = $verify;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: 'Your ' . config('pixelfed.domain.app') . ' Registration Update',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
markdown: 'emails.curated-register.message-from-admin',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
49
app/Models/CuratedRegister.php
Normal file
49
app/Models/CuratedRegister.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CuratedRegister extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $casts = [
|
||||
'autofollow_account_ids' => 'array',
|
||||
'admin_notes' => 'array',
|
||||
'email_verified_at' => 'datetime',
|
||||
'admin_notified_at' => 'datetime',
|
||||
'action_taken_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function adminStatusLabel()
|
||||
{
|
||||
if(!$this->email_verified_at) {
|
||||
return '<span class="border border-danger px-3 py-1 rounded text-white font-weight-bold">Unverified email</span>';
|
||||
}
|
||||
if($this->is_accepted) { return 'Approved'; }
|
||||
if($this->is_rejected) { return 'Rejected'; }
|
||||
if($this->is_awaiting_more_info ) {
|
||||
return '<span class="border border-info px-3 py-1 rounded text-white font-weight-bold">Awaiting Details</span>';
|
||||
}
|
||||
if($this->is_closed ) { return 'Closed'; }
|
||||
|
||||
return '<span class="border border-success px-3 py-1 rounded text-white font-weight-bold">Open</span>';
|
||||
}
|
||||
|
||||
public function emailConfirmUrl()
|
||||
{
|
||||
return url('/auth/sign_up/confirm?sid=' . $this->id . '&code=' . $this->verify_code);
|
||||
}
|
||||
|
||||
public function emailReplyUrl()
|
||||
{
|
||||
return url('/auth/sign_up/concierge?sid=' . $this->id . '&code=' . $this->verify_code . '&sc=' . str_random(8));
|
||||
}
|
||||
|
||||
public function adminReviewUrl()
|
||||
{
|
||||
return url('/i/admin/curated-onboarding/show/' . $this->id);
|
||||
}
|
||||
}
|
38
app/Models/CuratedRegisterActivity.php
Normal file
38
app/Models/CuratedRegisterActivity.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CuratedRegisterActivity extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $guarded = [];
|
||||
|
||||
protected $casts = [
|
||||
'metadata' => 'array',
|
||||
'admin_notified_at' => 'datetime',
|
||||
'action_taken_at' => 'datetime',
|
||||
];
|
||||
|
||||
public function application()
|
||||
{
|
||||
return $this->belongsTo(CuratedRegister::class, 'register_id');
|
||||
}
|
||||
|
||||
public function emailReplyUrl()
|
||||
{
|
||||
return url('/auth/sign_up/concierge?sid='.$this->register_id . '&id=' . $this->id . '&code=' . $this->secret_code);
|
||||
}
|
||||
|
||||
public function adminReviewUrl()
|
||||
{
|
||||
$url = '/i/admin/curated-onboarding/show/' . $this->register_id . '/?ah=' . $this->id;
|
||||
if($this->reply_to_id) {
|
||||
$url .= '&rtid=' . $this->reply_to_id;
|
||||
}
|
||||
return url($url);
|
||||
}
|
||||
}
|
|
@ -72,6 +72,8 @@ class ConfigCacheService
|
|||
'instance.banner.blurhash',
|
||||
|
||||
'autospam.nlp.enabled',
|
||||
|
||||
'instance.curated_registration.enabled',
|
||||
// 'system.user_mode'
|
||||
];
|
||||
|
||||
|
|
|
@ -48,13 +48,16 @@ class LandingService
|
|||
->toArray() : [];
|
||||
});
|
||||
|
||||
$openReg = (bool) config_cache('pixelfed.open_registration');
|
||||
|
||||
$res = [
|
||||
'name' => config_cache('app.name'),
|
||||
'url' => config_cache('app.url'),
|
||||
'domain' => config('pixelfed.domain.app'),
|
||||
'show_directory' => config_cache('instance.landing.show_directory'),
|
||||
'show_explore_feed' => config_cache('instance.landing.show_explore'),
|
||||
'open_registration' => config_cache('pixelfed.open_registration') == 1,
|
||||
'open_registration' => (bool) $openReg,
|
||||
'curated_onboarding' => (bool) config_cache('instance.curated_registration.enabled'),
|
||||
'version' => config('pixelfed.version'),
|
||||
'about' => [
|
||||
'banner_image' => config_cache('app.banner_image') ?? url('/storage/headers/default.jpg'),
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"license": "AGPL-3.0-only",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": "^8.1|^8.2",
|
||||
"php": "^8.1|^8.2|^8.3",
|
||||
"ext-bcmath": "*",
|
||||
"ext-ctype": "*",
|
||||
"ext-curl": "*",
|
||||
|
|
1078
composer.lock
generated
1078
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -74,7 +74,7 @@ return [
|
|||
'redis' => [
|
||||
'driver' => 'redis',
|
||||
'lock_connection' => 'default',
|
||||
'client' => env('REDIS_CLIENT', 'phpredis'),
|
||||
'client' => env('REDIS_CLIENT', 'predis'),
|
||||
|
||||
'default' => [
|
||||
'scheme' => env('REDIS_SCHEME', 'tcp'),
|
||||
|
|
|
@ -49,7 +49,7 @@ return [
|
|||
],
|
||||
|
||||
'network_timeline' => env('PF_NETWORK_TIMELINE', true),
|
||||
'network_timeline_days_falloff' => env('PF_NETWORK_TIMELINE_DAYS_FALLOFF', 2),
|
||||
'network_timeline_days_falloff' => env('PF_NETWORK_TIMELINE_DAYS_FALLOFF', 90),
|
||||
|
||||
'custom_emoji' => [
|
||||
'enabled' => env('CUSTOM_EMOJI', false),
|
||||
|
|
|
@ -145,4 +145,35 @@ return [
|
|||
'software-update' => [
|
||||
'disable_failed_warning' => env('INSTANCE_SOFTWARE_UPDATE_DISABLE_FAILED_WARNING', false)
|
||||
],
|
||||
|
||||
'notifications' => [
|
||||
'gc' => [
|
||||
'enabled' => env('INSTANCE_NOTIFY_AUTO_GC', false),
|
||||
'delete_after_days' => env('INSTANCE_NOTIFY_AUTO_GC_DEL_AFTER_DAYS', 365)
|
||||
]
|
||||
],
|
||||
|
||||
'curated_registration' => [
|
||||
'enabled' => env('INSTANCE_CUR_REG', false),
|
||||
|
||||
'resend_confirmation_limit' => env('INSTANCE_CUR_REG_RESEND_LIMIT', 5),
|
||||
|
||||
'captcha_enabled' => env('INSTANCE_CUR_REG_CAPTCHA', env('CAPTCHA_ENABLED', false)),
|
||||
|
||||
'state' => [
|
||||
'fallback_on_closed_reg' => true,
|
||||
'only_enabled_on_closed_reg' => env('INSTANCE_CUR_REG_STATE_ONLY_ON_CLOSED', true),
|
||||
],
|
||||
|
||||
'notify' => [
|
||||
'admin' => [
|
||||
'on_verify_email' => [
|
||||
'enabled' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY', false),
|
||||
'bundle' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_BUNDLE', false),
|
||||
'max_per_day' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_VERIFY_MPD', 10),
|
||||
],
|
||||
'on_user_response' => env('INSTANCE_CUR_REG_NOTIFY_ADMIN_ON_USER_RESPONSE', false),
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('curated_registers', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('email')->unique()->nullable()->index();
|
||||
$table->string('username')->unique()->nullable()->index();
|
||||
$table->string('password')->nullable();
|
||||
$table->string('ip_address')->nullable();
|
||||
$table->string('verify_code')->nullable();
|
||||
$table->text('reason_to_join')->nullable();
|
||||
$table->unsignedBigInteger('invited_by')->nullable()->index();
|
||||
$table->boolean('is_approved')->default(0)->index();
|
||||
$table->boolean('is_rejected')->default(0)->index();
|
||||
$table->boolean('is_awaiting_more_info')->default(0)->index();
|
||||
$table->boolean('is_closed')->default(0)->index();
|
||||
$table->json('autofollow_account_ids')->nullable();
|
||||
$table->json('admin_notes')->nullable();
|
||||
$table->unsignedInteger('approved_by_admin_id')->nullable();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->timestamp('admin_notified_at')->nullable();
|
||||
$table->timestamp('action_taken_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('curated_registers');
|
||||
}
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('curated_register_activities', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('register_id')->nullable()->index();
|
||||
$table->unsignedInteger('admin_id')->nullable();
|
||||
$table->unsignedInteger('reply_to_id')->nullable()->index();
|
||||
$table->string('secret_code')->nullable();
|
||||
$table->string('type')->nullable()->index();
|
||||
$table->string('title')->nullable();
|
||||
$table->string('link')->nullable();
|
||||
$table->text('message')->nullable();
|
||||
$table->json('metadata')->nullable();
|
||||
$table->boolean('from_admin')->default(false)->index();
|
||||
$table->boolean('from_user')->default(false)->index();
|
||||
$table->boolean('admin_only_view')->default(true);
|
||||
$table->boolean('action_required')->default(false);
|
||||
$table->timestamp('admin_notified_at')->nullable();
|
||||
$table->timestamp('action_taken_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('curated_register_activities');
|
||||
}
|
||||
};
|
1543
package-lock.json
generated
1543
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"acorn": "^8.7.1",
|
||||
"axios": "^0.21.1",
|
||||
"axios": ">=1.6.0",
|
||||
"bootstrap": "^4.5.2",
|
||||
"cross-env": "^5.2.1",
|
||||
"jquery": "^3.6.0",
|
||||
|
|
BIN
public/js/account-import.js
vendored
BIN
public/js/account-import.js
vendored
Binary file not shown.
BIN
public/js/activity.js
vendored
BIN
public/js/activity.js
vendored
Binary file not shown.
BIN
public/js/admin.js
vendored
BIN
public/js/admin.js
vendored
Binary file not shown.
BIN
public/js/admin_invite.js
vendored
BIN
public/js/admin_invite.js
vendored
Binary file not shown.
BIN
public/js/app.js
vendored
BIN
public/js/app.js
vendored
Binary file not shown.
BIN
public/js/changelog.bundle.742a06ba0a547120.js
vendored
BIN
public/js/changelog.bundle.742a06ba0a547120.js
vendored
Binary file not shown.
BIN
public/js/changelog.bundle.bf44edbbfa14bd53.js
vendored
Normal file
BIN
public/js/changelog.bundle.bf44edbbfa14bd53.js
vendored
Normal file
Binary file not shown.
BIN
public/js/collectioncompose.js
vendored
BIN
public/js/collectioncompose.js
vendored
Binary file not shown.
BIN
public/js/collections.js
vendored
BIN
public/js/collections.js
vendored
Binary file not shown.
BIN
public/js/components.js
vendored
BIN
public/js/components.js
vendored
Binary file not shown.
BIN
public/js/compose-classic.js
vendored
BIN
public/js/compose-classic.js
vendored
Binary file not shown.
BIN
public/js/compose.chunk.1ac292c93b524406.js
vendored
BIN
public/js/compose.chunk.1ac292c93b524406.js
vendored
Binary file not shown.
BIN
public/js/compose.chunk.ffae318db42f1072.js
vendored
Normal file
BIN
public/js/compose.chunk.ffae318db42f1072.js
vendored
Normal file
Binary file not shown.
BIN
public/js/compose.js
vendored
BIN
public/js/compose.js
vendored
Binary file not shown.
BIN
public/js/daci.chunk.34dc7bad3a0792cc.js
vendored
Normal file
BIN
public/js/daci.chunk.34dc7bad3a0792cc.js
vendored
Normal file
Binary file not shown.
BIN
public/js/daci.chunk.8d4acc1db3f27a51.js
vendored
BIN
public/js/daci.chunk.8d4acc1db3f27a51.js
vendored
Binary file not shown.
BIN
public/js/developers.js
vendored
BIN
public/js/developers.js
vendored
Binary file not shown.
BIN
public/js/direct.js
vendored
BIN
public/js/direct.js
vendored
Binary file not shown.
BIN
public/js/discover.chunk.b1846efb6bd1e43c.js
vendored
BIN
public/js/discover.chunk.b1846efb6bd1e43c.js
vendored
Binary file not shown.
BIN
public/js/discover.chunk.c2229e1d15bd3ada.js
vendored
Normal file
BIN
public/js/discover.chunk.c2229e1d15bd3ada.js
vendored
Normal file
Binary file not shown.
BIN
public/js/discover.js
vendored
BIN
public/js/discover.js
vendored
Binary file not shown.
Binary file not shown.
BIN
public/js/discover~findfriends.chunk.b1858bea66d9723b.js
vendored
Normal file
BIN
public/js/discover~findfriends.chunk.b1858bea66d9723b.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/discover~hashtag.bundle.a0f00fc7df1f313c.js
vendored
Normal file
BIN
public/js/discover~hashtag.bundle.a0f00fc7df1f313c.js
vendored
Normal file
Binary file not shown.
BIN
public/js/discover~memories.chunk.37e0c325f900e163.js
vendored
Normal file
BIN
public/js/discover~memories.chunk.37e0c325f900e163.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/discover~myhashtags.chunk.8886fc0d4736d819.js
vendored
Normal file
BIN
public/js/discover~myhashtags.chunk.8886fc0d4736d819.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/discover~serverfeed.chunk.262bf7e3bce843c3.js
vendored
Normal file
BIN
public/js/discover~serverfeed.chunk.262bf7e3bce843c3.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/discover~settings.chunk.65d6f3cbe5323ed4.js
vendored
Normal file
BIN
public/js/discover~settings.chunk.65d6f3cbe5323ed4.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/dms.chunk.2b55effc0e8ba89f.js
vendored
Normal file
BIN
public/js/dms.chunk.2b55effc0e8ba89f.js
vendored
Normal file
Binary file not shown.
BIN
public/js/dms.chunk.53a951c5de2d95ac.js
vendored
BIN
public/js/dms.chunk.53a951c5de2d95ac.js
vendored
Binary file not shown.
BIN
public/js/dms~message.chunk.76edeafda3d92320.js
vendored
BIN
public/js/dms~message.chunk.76edeafda3d92320.js
vendored
Binary file not shown.
BIN
public/js/dms~message.chunk.976f7edaa6f71137.js
vendored
Normal file
BIN
public/js/dms~message.chunk.976f7edaa6f71137.js
vendored
Normal file
Binary file not shown.
BIN
public/js/error404.bundle.3bbc118159460db6.js
vendored
BIN
public/js/error404.bundle.3bbc118159460db6.js
vendored
Binary file not shown.
BIN
public/js/error404.bundle.b397483e3991ab20.js
vendored
Normal file
BIN
public/js/error404.bundle.b397483e3991ab20.js
vendored
Normal file
Binary file not shown.
BIN
public/js/hashtag.js
vendored
BIN
public/js/hashtag.js
vendored
Binary file not shown.
BIN
public/js/home.chunk.264eeb47bfac56c1.js
vendored
Normal file
BIN
public/js/home.chunk.264eeb47bfac56c1.js
vendored
Normal file
Binary file not shown.
BIN
public/js/home.chunk.88eeebf6c53d4dca.js
vendored
BIN
public/js/home.chunk.88eeebf6c53d4dca.js
vendored
Binary file not shown.
BIN
public/js/i18n.bundle.47cbf9f04d955267.js
vendored
BIN
public/js/i18n.bundle.47cbf9f04d955267.js
vendored
Binary file not shown.
BIN
public/js/i18n.bundle.93a02e275ac1a708.js
vendored
Normal file
BIN
public/js/i18n.bundle.93a02e275ac1a708.js
vendored
Normal file
Binary file not shown.
BIN
public/js/landing.js
vendored
BIN
public/js/landing.js
vendored
Binary file not shown.
BIN
public/js/manifest.js
vendored
BIN
public/js/manifest.js
vendored
Binary file not shown.
BIN
public/js/notifications.chunk.0c5151643e4534aa.js
vendored
Normal file
BIN
public/js/notifications.chunk.0c5151643e4534aa.js
vendored
Normal file
Binary file not shown.
BIN
public/js/notifications.chunk.3b92cf46da469de1.js
vendored
BIN
public/js/notifications.chunk.3b92cf46da469de1.js
vendored
Binary file not shown.
BIN
public/js/portfolio.js
vendored
BIN
public/js/portfolio.js
vendored
Binary file not shown.
BIN
public/js/post.chunk.5ff16664f9adb901.js
vendored
Normal file
BIN
public/js/post.chunk.5ff16664f9adb901.js
vendored
Normal file
Binary file not shown.
BIN
public/js/post.chunk.eb9804ff282909ae.js
vendored
BIN
public/js/post.chunk.eb9804ff282909ae.js
vendored
Binary file not shown.
BIN
public/js/profile-directory.js
vendored
BIN
public/js/profile-directory.js
vendored
Binary file not shown.
BIN
public/js/profile.chunk.7a6c846c4cb3cfd4.js
vendored
Normal file
BIN
public/js/profile.chunk.7a6c846c4cb3cfd4.js
vendored
Normal file
Binary file not shown.
BIN
public/js/profile.chunk.d52916cb68c9a146.js
vendored
BIN
public/js/profile.chunk.d52916cb68c9a146.js
vendored
Binary file not shown.
BIN
public/js/profile.js
vendored
BIN
public/js/profile.js
vendored
Binary file not shown.
BIN
public/js/profile~followers.bundle.5d796e79f32d066c.js
vendored
Normal file
BIN
public/js/profile~followers.bundle.5d796e79f32d066c.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/profile~following.bundle.7ca7cfa5aaae75e2.js
vendored
Normal file
BIN
public/js/profile~following.bundle.7ca7cfa5aaae75e2.js
vendored
Normal file
Binary file not shown.
Binary file not shown.
BIN
public/js/remote_auth.js
vendored
BIN
public/js/remote_auth.js
vendored
Binary file not shown.
BIN
public/js/search.js
vendored
BIN
public/js/search.js
vendored
Binary file not shown.
BIN
public/js/spa.js
vendored
BIN
public/js/spa.js
vendored
Binary file not shown.
BIN
public/js/status.js
vendored
BIN
public/js/status.js
vendored
Binary file not shown.
BIN
public/js/stories.js
vendored
BIN
public/js/stories.js
vendored
Binary file not shown.
BIN
public/js/story-compose.js
vendored
BIN
public/js/story-compose.js
vendored
Binary file not shown.
BIN
public/js/timeline.js
vendored
BIN
public/js/timeline.js
vendored
Binary file not shown.
BIN
public/js/vendor.js
vendored
BIN
public/js/vendor.js
vendored
Binary file not shown.
|
@ -71,8 +71,8 @@
|
|||
*/
|
||||
|
||||
/*!
|
||||
* Vue.js v2.7.14
|
||||
* (c) 2014-2022 Evan You
|
||||
* Vue.js v2.7.16
|
||||
* (c) 2014-2023 Evan You
|
||||
* Released under the MIT License.
|
||||
*/
|
||||
|
||||
|
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue