'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!') . '
' . __('Our team has been notified. Please try again later.') . '
' . __('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,
]);
}
}