make required fields configurable

This commit is contained in:
xuwhite 2023-11-03 15:15:44 +01:00 committed by GitHub
parent 1397fe90ce
commit 27323bfba5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 37 deletions

View File

@ -233,6 +233,16 @@ return [
// Users are able to sign up // Users are able to sign up
'registration_enabled' => (bool) env('REGISTRATION_ENABLED', true), 'registration_enabled' => (bool) env('REGISTRATION_ENABLED', true),
// Required fields on sign-up page
'signup_required_fields' => [
'pronoun' => (bool) env('PRONOUN_REQUIRED', false),
'firstname' => (bool) env('FIRSTNAME_REQUIRED', false),
'lastname' => (bool) env('LASTNAME_REQUIRED', false),
'tshirt_size' => (bool) env('TSHIRT_SIZE_REQUIRED', true),
'mobile' => (bool) env('MOBILE_REQUIRED', false),
'dect' => (bool) env('DECT_REQUIRED', false),
],
// Only arrived angels can sign up for shifts // Only arrived angels can sign up for shifts
'signup_requires_arrival' => (bool) env('SIGNUP_REQUIRES_ARRIVAL', false), 'signup_requires_arrival' => (bool) env('SIGNUP_REQUIRES_ARRIVAL', false),

View File

@ -952,7 +952,11 @@ function render_user_tshirt_hint()
{ {
$goodie = GoodieType::from(config('goodie_type')); $goodie = GoodieType::from(config('goodie_type'));
$goodie_tshirt = $goodie === GoodieType::Tshirt; $goodie_tshirt = $goodie === GoodieType::Tshirt;
if ($goodie_tshirt && !auth()->user()->personalData->shirt_size) { if (
$goodie_tshirt
&& config('signup_required_fields')['tshirt_size']
&& !auth()->user()->personalData->shirt_size
) {
$text = __('You need to specify a tshirt size in your settings!'); $text = __('You need to specify a tshirt size in your settings!');
return render_profile_link($text); return render_profile_link($text);
} }

View File

@ -23,20 +23,20 @@ msgstr "Das angegebene Passwort ist zu kurz."
msgid "validation.login.required" msgid "validation.login.required"
msgstr "Bitte gib einen Loginnamen an." msgstr "Bitte gib einen Loginnamen an."
msgid "validation.pronoun.optional" msgid "validation.pronoun.required"
msgstr "Das Pronomen, das Du eingegeben hast, ist zu lang. Verwende maximal 15 Zeichen." msgstr "Bitte gebe dein Pronomen an."
msgid "validation.firstname.optional" msgid "validation.firstname.required"
msgstr "Der von dir eingegebene Vorname ist zu lang. Verwende maximal 64 Zeichen." msgstr "Bitte gebe deinen Vornamen an."
msgid "validation.lastname.optional" msgid "validation.lastname.required"
msgstr "Der von dir eingegebene Vorname ist zu lang. Verwende maximal 64 Zeichen." msgstr "Bitte gebe deinen Nachnamen an."
msgid "validation.mobile.optional" msgid "validation.mobile.required"
msgstr "Der von dir eingegebene Handynummer ist zu lang. Verwende maximal 40 Zeichen." msgstr "Bitte gebe deine Handynummer an."
msgid "validation.dect.optional" msgid "validation.dect.required"
msgstr "Der von dir eingegebene DECT-Nummer ist zu lang. Verwende maximal 40 Zeichen." msgstr "Bitte gebe deine DECT-Nummer an."
msgid "validation.username.required" msgid "validation.username.required"
msgstr "Bitte gebe deinen Nick an." msgstr "Bitte gebe deinen Nick an."

View File

@ -21,20 +21,20 @@ msgstr "The password entered is too short."
msgid "validation.login.required" msgid "validation.login.required"
msgstr "The login name is required." msgstr "The login name is required."
msgid "validation.pronoun.length" msgid "validation.pronoun.required"
msgstr "The pronoun you have entered is too long. Use a maximum of 15 characters." msgstr "Please enter your pronoun."
msgid "validation.firstname.length" msgid "validation.firstname.required"
msgstr "The first name you have entered is too long. Use a maximum of 64 characters." msgstr "Please enter your first name."
msgid "validation.lastname.length" msgid "validation.lastname.required"
msgstr "The last name you have entered is too long. Use a maximum of 64 characters." msgstr "Please enter your last name."
msgid "validation.mobile.optional" msgid "validation.mobile.required"
msgstr "The mobile number you have entered is too long. Use a maximum of 40 characters." msgstr "Please enter your mobile number."
msgid "validation.dect.optional" msgid "validation.dect.required"
msgstr "The DECT number you have entered is too long. Use a maximum of 40 characters." msgstr "Please enter your DECT number."
msgid "validation.username.required" msgid "validation.username.required"
msgstr "Please enter your nick." msgstr "Please enter your nick."

View File

@ -28,6 +28,8 @@
{{ f.input('pronoun', __('settings.profile.pronoun'), { {{ f.input('pronoun', __('settings.profile.pronoun'), {
'value': user.personalData.pronoun, 'value': user.personalData.pronoun,
'max_length': 15, 'max_length': 15,
'required': isPronounRequired,
'required_icon': isPronounRequired,
}) }} }) }}
{{ m.info(__('settings.profile.pronoun.info')) }} {{ m.info(__('settings.profile.pronoun.info')) }}
</div> </div>
@ -40,12 +42,16 @@
{{ f.input('first_name', __('settings.profile.firstname'), { {{ f.input('first_name', __('settings.profile.firstname'), {
'value': user.personalData.first_name, 'value': user.personalData.first_name,
'max_length': 64, 'max_length': 64,
'required': isFirstnameRequired,
'required_icon': isFirstnameRequired,
}) }} }) }}
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
{{ f.input('last_name', __('settings.profile.lastname'), { {{ f.input('last_name', __('settings.profile.lastname'), {
'value': user.personalData.last_name, 'value': user.personalData.last_name,
'max_length': 64, 'max_length': 64,
'required': isLastnameRequired,
'required_icon': isLastnameRequired,
}) }} }) }}
</div> </div>
</div> </div>
@ -82,6 +88,8 @@
{{ f.input('dect', __('general.dect'), { {{ f.input('dect', __('general.dect'), {
'value': user.contact.dect, 'value': user.contact.dect,
'max_length': 40, 'max_length': 40,
'required': isDectRequired,
'required_icon': isDectRequired,
}) }} }) }}
</div> </div>
{% endif %} {% endif %}
@ -89,6 +97,8 @@
{{ f.input('mobile', __('settings.profile.mobile'), { {{ f.input('mobile', __('settings.profile.mobile'), {
'value': user.contact.mobile, 'value': user.contact.mobile,
'max_length': 40, 'max_length': 40,
'required': isMobileRequired,
'required_icon': isMobileRequired,
}) }} }) }}
{% if config('enable_mobile_show') %} {% if config('enable_mobile_show') %}
{{ f.checkbox('mobile_show', __('settings.profile.mobile_show'), { {{ f.checkbox('mobile_show', __('settings.profile.mobile_show'), {
@ -141,8 +151,8 @@
<div class="col-12"> <div class="col-12">
{{ f.select('shirt_size', __('settings.profile.shirt_size'), config('tshirt_sizes'), { {{ f.select('shirt_size', __('settings.profile.shirt_size'), config('tshirt_sizes'), {
'selected': user.personalData.shirt_size, 'selected': user.personalData.shirt_size,
'required': true, 'required': isTShirtSizeRequired,
'required_icon': true, 'required_icon': isTShirtSizeRequired,
'default_option': __('form.select_placeholder'), 'default_option': __('form.select_placeholder'),
}) }} }) }}
</div> </div>

View File

@ -31,6 +31,8 @@
__('settings.profile.pronoun'), __('settings.profile.pronoun'),
{ {
'max_length': 15, 'max_length': 15,
'required': isPronounRequired,
'required_icon': isPronounRequired,
'value': f.formData('pronoun', ''), 'value': f.formData('pronoun', ''),
} }
) }} ) }}
@ -162,6 +164,8 @@
{ {
'autocomplete': 'given-name', 'autocomplete': 'given-name',
'max_length': 64, 'max_length': 64,
'required': isFirstnameRequired,
'required_icon': isFirstnameRequired,
'value': f.formData('firstname', ''), 'value': f.formData('firstname', ''),
} }
) }} ) }}
@ -173,6 +177,8 @@
{ {
'autocomplete': 'family-name', 'autocomplete': 'family-name',
'max_length': 64, 'max_length': 64,
'required': isLastnameRequired,
'required_icon': isLastnameRequired,
'value': f.formData('lastname', ''), 'value': f.formData('lastname', ''),
} }
) }} ) }}
@ -227,8 +233,8 @@
tShirtSizes, tShirtSizes,
{ {
'default_option': __('form.select_placeholder'), 'default_option': __('form.select_placeholder'),
'required': true, 'required': isTShirtSizeRequired,
'required_icon': true, 'required_icon': isTShirtSizeRequired,
'selected': f.formData('tshirt_size', ''), 'selected': f.formData('tshirt_size', ''),
} }
) }} ) }}
@ -242,6 +248,8 @@
{ {
'type': 'tel-national', 'type': 'tel-national',
'max_length': 40, 'max_length': 40,
'required': isMobileRequired,
'required_icon': isMobileRequired,
'value': f.formData('mobile', ''), 'value': f.formData('mobile', ''),
} }
) }} ) }}
@ -265,6 +273,8 @@
{ {
'type': 'tel-local', 'type': 'tel-local',
'max_length': 40, 'max_length': 40,
'required': isDectRequired,
'required_icon': isDectRequired,
'value': f.formData('dect', ''), 'value': f.formData('dect', ''),
} }
) }} ) }}

View File

@ -35,6 +35,7 @@ class SettingsController extends BaseController
public function profile(): Response public function profile(): Response
{ {
$user = $this->auth->user(); $user = $this->auth->user();
$requiredFields = $this->config->get('signup_required_fields');
return $this->response->withView( return $this->response->withView(
'pages/settings/profile', 'pages/settings/profile',
@ -43,6 +44,12 @@ class SettingsController extends BaseController
'user' => $user, 'user' => $user,
'goodie_tshirt' => $this->config->get('goodie_type') === GoodieType::Tshirt->value, 'goodie_tshirt' => $this->config->get('goodie_type') === GoodieType::Tshirt->value,
'goodie_enabled' => $this->config->get('goodie_type') !== GoodieType::None->value, 'goodie_enabled' => $this->config->get('goodie_type') !== GoodieType::None->value,
'isPronounRequired' => $requiredFields['pronoun'],
'isFirstnameRequired' => $requiredFields['firstname'],
'isLastnameRequired' => $requiredFields['lastname'],
'isTShirtSizeRequired' => $requiredFields['tshirt_size'],
'isMobileRequired' => $requiredFields['mobile'],
'isDectRequired' => $requiredFields['dect'],
] ]
); );
} }
@ -359,6 +366,12 @@ class SettingsController extends BaseController
return true; return true;
} }
private function isRequired(string $key): string
{
$requiredFields = $this->config->get('signup_required_fields');
return $requiredFields[$key] ? 'required' : 'optional';
}
/** /**
* @return string[] * @return string[]
*/ */
@ -366,11 +379,12 @@ class SettingsController extends BaseController
{ {
$goodie_tshirt = $this->config->get('goodie_type') === GoodieType::Tshirt->value; $goodie_tshirt = $this->config->get('goodie_type') === GoodieType::Tshirt->value;
$rules = [ $rules = [
'pronoun' => 'optional|max:15', 'pronoun' => $this->isRequired('pronoun') . '|max:15',
'first_name' => 'optional|max:64', 'first_name' => $this->isRequired('firstname') . '|max:64',
'last_name' => 'optional|max:64', 'last_name' => $this->isRequired('lastname') . '|max:64',
'dect' => 'optional|length:0:40', // dect/mobile can be purely numbers. "max" would have 'dect' => $this->isRequired('dect') . '|length:0:40',
'mobile' => 'optional|length:0:40', // checked their values, not their character length. // dect/mobile can be purely numbers. "max" would have checked their values, not their character length.
'mobile' => $this->isRequired('mobile') . '|length:0:40',
'mobile_show' => 'optional|checked', 'mobile_show' => 'optional|checked',
'email' => 'required|email|max:254', 'email' => 'required|email|max:254',
'email_shiftinfo' => 'optional|checked', 'email_shiftinfo' => 'optional|checked',
@ -384,7 +398,7 @@ class SettingsController extends BaseController
$rules['planned_departure_date'] = 'optional|date:Y-m-d'; $rules['planned_departure_date'] = 'optional|date:Y-m-d';
} }
if ($goodie_tshirt) { if ($goodie_tshirt) {
$rules['shirt_size'] = 'required|shirt_size'; $rules['shirt_size'] = $this->isRequired('tshirt_size') . '|shirt_size';
} }
return $rules; return $rules;
} }

View File

@ -79,6 +79,7 @@ class SignUpController extends BaseController
{ {
$goodieType = GoodieType::from($this->config->get('goodie_type')); $goodieType = GoodieType::from($this->config->get('goodie_type'));
$preselectedAngelTypes = $this->determinePreselectedAngelTypes(); $preselectedAngelTypes = $this->determinePreselectedAngelTypes();
$requiredFields = $this->config->get('signup_required_fields');
// form-data-register-submit is a marker, that the form was submitted. // form-data-register-submit is a marker, that the form was submitted.
// It will be used for instance to use the default angel types or the user selected ones. // It will be used for instance to use the default angel types or the user selected ones.
@ -101,6 +102,12 @@ class SignUpController extends BaseController
'isPronounEnabled' => $this->config->get('enable_pronoun'), 'isPronounEnabled' => $this->config->get('enable_pronoun'),
'isFullNameEnabled' => $this->config->get('enable_user_name'), 'isFullNameEnabled' => $this->config->get('enable_user_name'),
'isPlannedArrivalDateEnabled' => $this->config->get('enable_planned_arrival'), 'isPlannedArrivalDateEnabled' => $this->config->get('enable_planned_arrival'),
'isPronounRequired' => $requiredFields['pronoun'],
'isFirstnameRequired' => $requiredFields['firstname'],
'isLastnameRequired' => $requiredFields['lastname'],
'isTShirtSizeRequired' => $requiredFields['tshirt_size'],
'isMobileRequired' => $requiredFields['mobile'],
'isDectRequired' => $requiredFields['dect'],
], ],
); );
} }

View File

@ -67,6 +67,12 @@ class User
return $this->config->get('buildup_start') ?? CarbonImmutable::now(); return $this->config->get('buildup_start') ?? CarbonImmutable::now();
} }
private function isRequired(string $key): string
{
$requiredFields = $this->config->get('signup_required_fields');
return $requiredFields[$key] ? 'required' : 'optional';
}
/** /**
* @param Array<string, mixed> $rawData * @param Array<string, mixed> $rawData
* @throws ValidationException * @throws ValidationException
@ -82,7 +88,7 @@ class User
'email_news' => 'optional|checked', 'email_news' => 'optional|checked',
'email_goody' => 'optional|checked', 'email_goody' => 'optional|checked',
// Using length here, because min/max would validate dect/mobile as numbers. // Using length here, because min/max would validate dect/mobile as numbers.
'mobile' => 'optional|length:0:40', 'mobile' => $this->isRequired('mobile') . '|length:0:40',
]; ];
$isPasswordEnabled = $this->determineIsPasswordEnabled(); $isPasswordEnabled = $this->determineIsPasswordEnabled();
@ -96,14 +102,14 @@ class User
$isFullNameEnabled = $this->config->get('enable_user_name'); $isFullNameEnabled = $this->config->get('enable_user_name');
if ($isFullNameEnabled) { if ($isFullNameEnabled) {
$validationRules['firstname'] = 'optional|length:0:64'; $validationRules['firstname'] = $this->isRequired('firstname') . '|length:0:64';
$validationRules['lastname'] = 'optional|length:0:64'; $validationRules['lastname'] = $this->isRequired('lastname') . '|length:0:64';
} }
$isPronounEnabled = $this->config->get('enable_pronoun'); $isPronounEnabled = $this->config->get('enable_pronoun');
if ($isPronounEnabled) { if ($isPronounEnabled) {
$validationRules['pronoun'] = 'optional|max:15'; $validationRules['pronoun'] = $this->isRequired('pronoun') . '|max:15';
} }
$isShowMobileEnabled = $this->config->get('enable_mobile_show'); $isShowMobileEnabled = $this->config->get('enable_mobile_show');
@ -137,14 +143,14 @@ class User
if ($isDECTEnabled) { if ($isDECTEnabled) {
// Using length here, because min/max would validate dect/mobile as numbers. // Using length here, because min/max would validate dect/mobile as numbers.
$validationRules['dect'] = 'optional|length:0:40'; $validationRules['dect'] = $this->isRequired('dect') . '|length:0:40';
} }
$goodieType = GoodieType::from($this->config->get('goodie_type')); $goodieType = GoodieType::from($this->config->get('goodie_type'));
$isGoodieTShirt = $goodieType === GoodieType::Tshirt; $isGoodieTShirt = $goodieType === GoodieType::Tshirt;
if ($isGoodieTShirt) { if ($isGoodieTShirt) {
$validationRules['tshirt_size'] = 'required|shirt-size'; $validationRules['tshirt_size'] = $this->isRequired('tshirt_size') . '|shirt-size';
} }
$data = $this->validate($rawData, $validationRules); $data = $this->validate($rawData, $validationRules);

View File

@ -106,6 +106,7 @@ class SettingsControllerTest extends ControllerTest
/** /**
* @covers \Engelsystem\Controllers\SettingsController::saveProfile * @covers \Engelsystem\Controllers\SettingsController::saveProfile
* @covers \Engelsystem\Controllers\SettingsController::getSaveProfileRules * @covers \Engelsystem\Controllers\SettingsController::getSaveProfileRules
* @covers \Engelsystem\Controllers\SettingsController::isRequired
*/ */
public function testSaveProfile(): void public function testSaveProfile(): void
{ {
@ -934,12 +935,21 @@ class SettingsControllerTest extends ControllerTest
'de_DE' => 'Deutsch', 'de_DE' => 'Deutsch',
]; ];
$tshirt_sizes = ['S' => 'Small']; $tshirt_sizes = ['S' => 'Small'];
$requiredFields = [
'pronoun' => false,
'firstname' => false,
'lastname' => false,
'tshirt_size' => true,
'mobile' => false,
'dect' => false,
];
$this->config = new Config([ $this->config = new Config([
'min_password_length' => 6, 'min_password_length' => 6,
'themes' => $themes, 'themes' => $themes,
'locales' => $languages, 'locales' => $languages,
'tshirt_sizes' => $tshirt_sizes, 'tshirt_sizes' => $tshirt_sizes,
'goodie_type' => GoodieType::Goodie->value, 'goodie_type' => GoodieType::Goodie->value,
'signup_required_fields' => $requiredFields,
]); ]);
$this->app->instance('config', $this->config); $this->app->instance('config', $this->config);
$this->app->instance(Config::class, $this->config); $this->app->instance(Config::class, $this->config);

View File

@ -11,6 +11,14 @@ final class SignUpConfig
{ {
public static function setMaximumConfig(Config $config): void public static function setMaximumConfig(Config $config): void
{ {
$requiredFields = [
'pronoun' => false,
'firstname' => false,
'lastname' => false,
'tshirt_size' => true,
'mobile' => false,
'dect' => false,
];
$config->set('registration_enabled', true); $config->set('registration_enabled', true);
$config->set('enable_password', true); $config->set('enable_password', true);
$config->set('enable_pronoun', true); $config->set('enable_pronoun', true);
@ -27,10 +35,25 @@ final class SignUpConfig
$config->set('enable_user_name', true); $config->set('enable_user_name', true);
$config->set('enable_mobile_show', true); $config->set('enable_mobile_show', true);
$config->set('enable_dect', true); $config->set('enable_dect', true);
$config->set('signup_required_fields', $requiredFields);
} }
public static function setMinimumConfig(Config $config): void public static function setMinimumConfig(Config $config): void
{ {
$requiredFields = [
'pronoun' => false,
'firstname' => false,
'lastname' => false,
'tshirt_size' => true,
'mobile' => false,
'dect' => false,
];
$requiredFields['pronoun'] = false;
$requiredFields['firstname'] = false;
$requiredFields['lastname'] = false;
$requiredFields['tshirt_size'] = true;
$requiredFields['mobile'] = false;
$requiredFields['dect'] = false;
$config->set('registration_enabled', true); $config->set('registration_enabled', true);
$config->set('enable_password', true); $config->set('enable_password', true);
$config->set('enable_pronoun', false); $config->set('enable_pronoun', false);
@ -43,5 +66,6 @@ final class SignUpConfig
$config->set('enable_user_name', false); $config->set('enable_user_name', false);
$config->set('enable_mobile_show', false); $config->set('enable_mobile_show', false);
$config->set('enable_dect', false); $config->set('enable_dect', false);
$config->set('signup_required_fields', $requiredFields);
} }
} }