Initial commit
This commit is contained in:
405
app/Http/Livewire/Registration.php
Normal file
405
app/Http/Livewire/Registration.php
Normal file
@@ -0,0 +1,405 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use App\Models\Account;
|
||||
use App\Models\Locations\Country;
|
||||
use App\Models\Locations\Location;
|
||||
use App\Models\Post;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Laravel\Fortify\Contracts\CreatesNewUsers;
|
||||
use Livewire\Component;
|
||||
use Stevebauman\Location\Facades\Location as IpLocation;
|
||||
use Throwable;
|
||||
use WireUi\Traits\WireUiActions;
|
||||
|
||||
class Registration extends Component implements CreatesNewUsers
|
||||
{
|
||||
use WireUiActions;
|
||||
|
||||
|
||||
public $full_name;
|
||||
public $name;
|
||||
public $email;
|
||||
public $password;
|
||||
public $password_confirmation;
|
||||
public $country;
|
||||
public $division;
|
||||
public $city;
|
||||
public $district;
|
||||
public $validateCountry = true;
|
||||
public $validateDivision = true;
|
||||
public $validateCity = true;
|
||||
public $waitMessage = false;
|
||||
public $captcha;
|
||||
|
||||
// Principles acceptance
|
||||
public bool $showPrinciplesModal = true;
|
||||
public bool $principlesAccepted = false;
|
||||
public bool $principlesAgreed = false;
|
||||
public $principlesData = null;
|
||||
|
||||
// GDPR age verification
|
||||
public bool $ageConfirmed = false;
|
||||
|
||||
protected $listeners = ['countryToParent', 'divisionToParent', 'cityToParent', 'districtToParent'];
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'full_name' => 'required|string|max:255',
|
||||
'name' => timebank_config('rules.profile_user.name'),
|
||||
'email' => timebank_config('rules.profile_user.email'),
|
||||
'password' => timebank_config('rules.profile_user.password'),
|
||||
'country' => 'required_if:validateCountry,true|integer',
|
||||
'division' => 'required_if:validateDivision,true',
|
||||
'city' => 'required_if:validateCity,true',
|
||||
'district' => 'sometimes',
|
||||
'ageConfirmed' => 'accepted',
|
||||
// 'captcha' => 'hiddencaptcha:3,300', // min, max time in sec for submitting form without captcha validation error
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'captcha.hiddencaptcha' => __('Automatic form completion detected. Try again filling in the form manually. Robots are not allowed to register.'),
|
||||
'ageConfirmed.accepted' => __('You must confirm that you meet the minimum age requirement to register.'),
|
||||
];
|
||||
}
|
||||
|
||||
public function mount(Request $request)
|
||||
{
|
||||
if (App::environment(['local'])) {
|
||||
// $ip = '103.75.231.255'; // Static IP address Brussels for testing
|
||||
$ip = '31.20.250.12'; // Static IP address The Hague for testing
|
||||
//$ip = '101.33.29.255'; // Static IP address in Amsterdam for testing
|
||||
//$ip = '102.129.156.0'; // Static IP address Berlin for testing
|
||||
} else {
|
||||
// TODO: Test ip lookup in production
|
||||
$ip = $request->ip(); // Dynamic IP address
|
||||
}
|
||||
$IpLocationInfo = IpLocation::get($ip);
|
||||
if ($IpLocationInfo) {
|
||||
|
||||
$country = Country::select('id')->where('code', $IpLocationInfo->countryCode)->first();
|
||||
if ($country) {
|
||||
$this->country = $country->id;
|
||||
}
|
||||
|
||||
$division = DB::table('division_locales')->select('division_id')->where('name', 'LIKE', $IpLocationInfo->regionName)->where('locale', app()->getLocale())->first(); //We only need the city_id, therefore we use the default app locale in the where query.
|
||||
if ($division) {
|
||||
$this->division = $division->division_id;
|
||||
}
|
||||
|
||||
$city = DB::table('city_locales')->select('city_id')->where('name', $IpLocationInfo->cityName)->where('locale', app()->getLocale())->first(); //We only need the city_id, therefore we use the default app locale in the where query.
|
||||
if ($city) {
|
||||
$this->city = $city->city_id;
|
||||
};
|
||||
}
|
||||
|
||||
$this->setValidationOptions();
|
||||
|
||||
// Failsafe: If no published principles post exists, bypass the principles modal
|
||||
$principlesPost = $this->getPrinciplesPost();
|
||||
if (!$principlesPost) {
|
||||
$this->showPrinciplesModal = false;
|
||||
$this->principlesAccepted = true;
|
||||
// principlesData remains null, user will be registered without principles acceptance
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function emitLocationToChildren()
|
||||
{
|
||||
$this->dispatch('countryToChildren', $this->country);
|
||||
$this->dispatch('divisionToChildren', $this->division);
|
||||
$this->dispatch('cityToChildren', $this->city);
|
||||
$this->dispatch('districtToChildren', $this->district);
|
||||
}
|
||||
|
||||
|
||||
public function countryToParent($value)
|
||||
{
|
||||
$this->country = $value;
|
||||
$this->setValidationOptions();
|
||||
}
|
||||
|
||||
|
||||
public function divisionToParent($value)
|
||||
{
|
||||
$this->division = $value;
|
||||
$this->setValidationOptions();
|
||||
}
|
||||
|
||||
|
||||
public function cityToParent($value)
|
||||
{
|
||||
$this->city = $value;
|
||||
$this->setValidationOptions();
|
||||
}
|
||||
|
||||
|
||||
public function districtToParent($value)
|
||||
{
|
||||
$this->district = $value;
|
||||
$this->setValidationOptions();
|
||||
}
|
||||
|
||||
|
||||
public function updated($field)
|
||||
{
|
||||
$this->validateOnly($field);
|
||||
}
|
||||
|
||||
|
||||
public function setValidationOptions()
|
||||
{
|
||||
$this->validateCountry = $this->validateDivision = $this->validateCity = true;
|
||||
|
||||
// In case no cities or divisions for selected country are seeded in database
|
||||
if ($this->country) {
|
||||
$countDivisions = Country::find($this->country)->divisions->count();
|
||||
$countCities = Country::find($this->country)->cities->count();
|
||||
|
||||
if ($countDivisions > 0 && $countCities < 1) {
|
||||
$this->validateDivision = true;
|
||||
$this->validateCity = false;
|
||||
} elseif ($countDivisions < 1 && $countCities > 1) {
|
||||
$this->validateDivision = false;
|
||||
$this->validateCity = true;
|
||||
} elseif ($countDivisions < 1 && $countCities < 1) {
|
||||
$this->validateDivision = false;
|
||||
$this->validateCity = false;
|
||||
} elseif ($countDivisions > 0 && $countCities > 0) {
|
||||
$this->validateDivision = false;
|
||||
$this->validateCity = true;
|
||||
}
|
||||
|
||||
}
|
||||
// In case no country is selected, no need to show other validation errors
|
||||
if (!$this->country) {
|
||||
$this->validateCountry = true;
|
||||
$this->validateDivision = $this->validateCity = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the current principles post for the current locale
|
||||
*
|
||||
* @return Post|null
|
||||
*/
|
||||
protected function getPrinciplesPost()
|
||||
{
|
||||
$locale = app()->getLocale();
|
||||
|
||||
return Post::with(['translations' => function ($query) use ($locale) {
|
||||
$query->where('locale', 'like', $locale . '%')
|
||||
->whereDate('from', '<=', now())
|
||||
->where(function ($query) {
|
||||
$query->whereDate('till', '>', now())->orWhereNull('till');
|
||||
})
|
||||
->orderBy('updated_at', 'desc')
|
||||
->limit(1);
|
||||
}])
|
||||
->whereHas('category', function ($query) {
|
||||
$query->where('type', 'SiteContents\\Static\\Principles');
|
||||
})
|
||||
->whereHas('translations', function ($query) use ($locale) {
|
||||
$query->where('locale', 'like', $locale . '%')
|
||||
->whereDate('from', '<=', now())
|
||||
->where(function ($query) {
|
||||
$query->whereDate('till', '>', now())->orWhereNull('till');
|
||||
});
|
||||
})
|
||||
->first();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accept the principles and store metadata for later user creation
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function acceptPrinciples()
|
||||
{
|
||||
// Get the current principles post
|
||||
$principlesPost = $this->getPrinciplesPost();
|
||||
|
||||
if (!$principlesPost || !$principlesPost->translations->first()) {
|
||||
$this->notification()->error(
|
||||
$title = __('Error'),
|
||||
$description = __('Unable to find the current principles document.')
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$translation = $principlesPost->translations->first();
|
||||
|
||||
// Store acceptance metadata for later user creation
|
||||
$this->principlesData = [
|
||||
'post_id' => $principlesPost->id,
|
||||
'post_translation_id' => $translation->id,
|
||||
'locale' => $translation->locale,
|
||||
'from' => $translation->from,
|
||||
'updated_at' => $translation->updated_at->toDateTimeString(),
|
||||
'accepted_at' => now()->toDateTimeString(),
|
||||
];
|
||||
|
||||
$this->principlesAccepted = true;
|
||||
$this->showPrinciplesModal = false;
|
||||
|
||||
$this->notification()->success(
|
||||
$title = __('Thank you'),
|
||||
$description = __('You can now proceed with registration.')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function create($input = null)
|
||||
{
|
||||
$this->waitMessage = true;
|
||||
$valid = $this->validate();
|
||||
|
||||
try {
|
||||
// Use a transaction for creating the new user
|
||||
DB::transaction(function () use ($valid): void {
|
||||
|
||||
$user = User::create([
|
||||
'full_name' => $valid['full_name'],
|
||||
'name' => $valid['name'],
|
||||
'email' => $valid['email'],
|
||||
'password' => Hash::make($valid['password']),
|
||||
'profile_photo_path' => timebank_config('profiles.user.profile_photo_path_new'),
|
||||
'lang_preference' => app()->getLocale(), // App locale is set by mcamara/laravel-localization package: set app locale according to browser language
|
||||
'limit_min' => timebank_config('profiles.user.limit_min'),
|
||||
'limit_max' => timebank_config('profiles.user.limit_max'),
|
||||
'principles_terms_accepted' => $this->principlesData,
|
||||
]);
|
||||
|
||||
$location = new Location();
|
||||
$location->name = __('Default location');
|
||||
$location->country_id = $valid['country'];
|
||||
$location->division_id = $valid['division'];
|
||||
$location->city_id = $valid['city'];
|
||||
$location->district_id = $valid['district'];
|
||||
$user->locations()->save($location); // save the new location for the user
|
||||
|
||||
$account = new Account();
|
||||
$account->name = __(timebank_config('accounts.user.name'));
|
||||
$account->limit_min = timebank_config('accounts.user.limit_min');
|
||||
$account->limit_max = timebank_config('accounts.user.limit_max');
|
||||
|
||||
// TODO: remove testing comment for production
|
||||
// Uncomment to test a failed transaction
|
||||
// Simulate an error by throwing an exception
|
||||
// throw new \Exception('Simulated error before saving account');
|
||||
|
||||
$user->accounts()->save($account); // create the new account for the user
|
||||
|
||||
|
||||
// TODO: Replace commented rtippin messenger logic with wirechat logic
|
||||
// Attach user to Messenger as a provider
|
||||
// Messenger::getProviderMessenger($user);
|
||||
|
||||
// WireUI notification
|
||||
$this->notification()->success(
|
||||
$title = __('Your registration is saved!'),
|
||||
);
|
||||
|
||||
$this->reset();
|
||||
Auth::guard('web')->login($user);
|
||||
event(new Registered($user));
|
||||
});
|
||||
// End of transaction
|
||||
|
||||
$this->waitMessage = false;
|
||||
|
||||
return redirect()->route('verification.notice');
|
||||
|
||||
} catch (Throwable $e) {
|
||||
$this->waitMessage = false;
|
||||
|
||||
// WireUI notification
|
||||
$this->notification()->send([
|
||||
'title' => __('Registration failed') . '! ',
|
||||
'description' => __('Sorry, your data could not be saved!') . '<br /><br />' . __('Our team has been notified. Please try again later.') . '<br /><br />' . __('Error') . ': ' . $e->getMessage(),
|
||||
'icon' => 'error',
|
||||
'timeout' => 100000
|
||||
]);
|
||||
|
||||
$warningMessage = 'User registration failed';
|
||||
$error = $e;
|
||||
$eventTime = now()->toDateTimeString();
|
||||
$ip = request()->ip();
|
||||
$ipLocationInfo = IpLocation::get($ip);
|
||||
// Escape ipLocation errors when not in production
|
||||
if (!$ipLocationInfo || App::environment(['local', 'development', 'staging'])) {
|
||||
$ipLocationInfo = (object) [
|
||||
'cityName' => 'local City',
|
||||
'regionName' => 'local Region',
|
||||
'countryName' => 'local Country',
|
||||
];
|
||||
}
|
||||
$lang_preference = app()->getLocale();
|
||||
$country = DB::table('country_locales')->where('country_id', $valid['country'])->where('locale', timebank_config('base_language'))->value('name');
|
||||
$division = DB::table('division_locales')->where('division_id', $valid['division'])->where('locale', timebank_config('base_language'))->value('name');
|
||||
$city = DB::table('city_locales')->where('city_id', $valid['city'])->where('locale', timebank_config('base_language'))->value('name');
|
||||
$district = DB::table('district_locales')->where('district_id', $valid['district'])->where('locale', timebank_config('base_language'))->value('name');
|
||||
|
||||
// Log this event and mail to admin
|
||||
Log::warning($warningMessage, [
|
||||
'full_name' => $valid['full_name'],
|
||||
'name' => $valid['name'],
|
||||
'email' => $valid['email'],
|
||||
'lang_preference' => $lang_preference,
|
||||
'country' => $country,
|
||||
'division' => $division,
|
||||
'city' => $city,
|
||||
'district' => $district,
|
||||
'IP address' => $ip,
|
||||
'IP location' => $ipLocationInfo->cityName . ', ' . $ipLocationInfo->regionName . ', ' . $ipLocationInfo->countryName,
|
||||
'Event Time' => $eventTime,
|
||||
'Message' => $error,
|
||||
]);
|
||||
Mail::raw(
|
||||
$warningMessage . '.' . "\n\n" .
|
||||
'Full name: ' . $valid['full_name'] . "\n" .
|
||||
'Name: ' . $valid['name'] . "\n" .
|
||||
'Email: ' . $valid['email'] . "\n" .
|
||||
'Language preference: ' . $lang_preference . "\n" .
|
||||
'Country: ' . $country . "\n" .
|
||||
'Division: ' . $division . "\n" .
|
||||
'City: ' . $city . "\n" .
|
||||
'District: ' . $district . "\n" .
|
||||
'IP address: ' . $ip . "\n" .
|
||||
'IP location: ' . $ipLocationInfo->cityName . ', ' . $ipLocationInfo->regionName . ', ' . $ipLocationInfo->countryName . "\n" .
|
||||
'Event Time: ' . $eventTime . "\n\n" .
|
||||
$error,
|
||||
function ($message) use ($warningMessage) {
|
||||
$message->to(timebank_config('mail.system_admin.email'))->subject($warningMessage);
|
||||
},
|
||||
);
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$principlesPost = $this->getPrinciplesPost();
|
||||
|
||||
return view('livewire.registration', [
|
||||
'principlesPost' => $principlesPost,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user