diff --git a/resources/lang/de_DE/additional.po b/resources/lang/de_DE/additional.po
index 7b7df794..3045379d 100644
--- a/resources/lang/de_DE/additional.po
+++ b/resources/lang/de_DE/additional.po
@@ -110,6 +110,17 @@ msgstr "OAuth-Provider nicht gefunden"
msgid "settings.profile"
msgstr "Profil"
+msgid "settings.profile.planned_arrival_date.invalid"
+msgstr "Bitte gib Dein geplantes Ankunftsdatum an. "
+"Es sollte nach dem Aufbaubeginn und vor dem Abbauende liegen."
+
+msgid "settings.profile.planned_departure_date.invalid"
+msgstr "Bitte gib Dein geplantes Abreisedatum an, damit wir ein Gefühl für die Abbauplanung bekommen. "
+"Es sollte nach dem Aufbaubeginn und vor dem Abbauende liegen."
+
+msgid "settings.profile.success"
+msgstr "Einstellungen gespeichert."
+
msgid "faq.delete.success"
msgstr "FAQ Eintrag erfolgreich gelöscht."
diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po
index d677d1e6..0b37716e 100644
--- a/resources/lang/de_DE/default.po
+++ b/resources/lang/de_DE/default.po
@@ -2942,6 +2942,64 @@ msgstr "Nachricht"
msgid "settings.settings"
msgstr "Einstellungen"
+msgid "settings.profile.user_details.info"
+msgstr "Hier kannst Du Deine Details ändern."
+
+msgid "settings.profile.entry_required"
+msgstr "Pflichtfeld!"
+
+msgid "settings.profile.nick"
+msgstr "Nick"
+
+msgid "settings.profile.pronoun"
+msgstr "Pronomen"
+
+msgid "settings.profile.pronoun.info"
+msgstr "Wird auf deiner Profilseite und in Engellisten angezeigt."
+
+msgid "settings.profile.firstname"
+msgstr "Vorname"
+
+msgid "settings.profile.lastname"
+msgstr "Nachname"
+
+msgid "settings.profile.planned_arrival_date"
+msgstr "Geplanter Ankunftstag"
+
+msgid "settings.profile.planned_departure_date"
+msgstr "Geplanter Abreisetag"
+
+msgid "settings.profile.dect"
+msgstr "DECT"
+
+msgid "settings.profile.mobile"
+msgstr "Handy"
+
+msgid "settings.profile.email"
+msgstr "E-Mail"
+
+msgid "settings.profile.email_shiftinfo"
+msgstr "Das %s darf mir E-Mails senden (z.B. wenn sich meine Schichten ändern)."
+
+msgid "settings.profile.email_news"
+msgstr "Benachrichtige mich bei neuen News."
+
+msgid "settings.profile.email_by_human_allowed"
+msgstr "Erlaube Himmel-Engeln dich per Mail zu kontaktieren."
+
+msgid "settings.profile.email_goody"
+msgstr "Um Voucher zu erhalten, stimme zu, dass Nick, E-Mail-Adresse, geleistete Arbeit und Shirtgröße "
+"bis zum nächsten gleichartigen Event gespeichert werden."
+
+msgid "settings.profile.privacy"
+msgstr "Dies kann jederzeit durch eine E-Mail an %1$s widerrufen werden."
+
+msgid "settings.profile.shirt_size"
+msgstr "T-Shirt-Größe"
+
+msgid "settings.profile.angeltypes.info"
+msgstr "Du kannst deine Engeltypen auf der Engeltypen-Seite verwalten."
+
msgid "settings.password"
msgstr "Passwort"
diff --git a/resources/lang/en_US/additional.po b/resources/lang/en_US/additional.po
index ce25afc9..daab8b32 100644
--- a/resources/lang/en_US/additional.po
+++ b/resources/lang/en_US/additional.po
@@ -108,6 +108,17 @@ msgstr "Unable to find OAuth provider"
msgid "settings.profile"
msgstr "Profile"
+msgid "settings.profile.planned_arrival_date.invalid"
+msgstr "Please enter your planned date of arrival. "
+"It should be after the buildup start date and before teardown end date."
+
+msgid "settings.profile.planned_departure_date.invalid"
+msgstr "Please enter your planned date of departure. "
+"It should be after your planned arrival date and after buildup start date and before teardown end date."
+
+msgid "settings.profile.success"
+msgstr "Settings saved."
+
msgid "faq.delete.success"
msgstr "FAQ entry successfully deleted."
diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po
index be0f6a95..125f203b 100644
--- a/resources/lang/en_US/default.po
+++ b/resources/lang/en_US/default.po
@@ -208,6 +208,64 @@ msgstr "Message"
msgid "settings.settings"
msgstr "Settings"
+msgid "settings.profile.user_details.info"
+msgstr "Here you can change your user details."
+
+msgid "settings.profile.entry_required"
+msgstr "Entry required!"
+
+msgid "settings.profile.nick"
+msgstr "Nick"
+
+msgid "settings.profile.pronoun"
+msgstr "Pronoun"
+
+msgid "settings.profile.pronoun.info"
+msgstr "Will be shown on your profile page and in angel lists."
+
+msgid "settings.profile.firstname"
+msgstr "First name"
+
+msgid "settings.profile.lastname"
+msgstr "Last name"
+
+msgid "settings.profile.planned_arrival_date"
+msgstr "Planned date of arrival"
+
+msgid "settings.profile.planned_departure_date"
+msgstr "Planned date of departure"
+
+msgid "settings.profile.dect"
+msgstr "DECT"
+
+msgid "settings.profile.mobile"
+msgstr "Mobile"
+
+msgid "settings.profile.email"
+msgstr "E-Mail"
+
+msgid "settings.profile.email_shiftinfo"
+msgstr "The %s is allowed to send me an e-mail (e.g. when my shifts change)."
+
+msgid "settings.profile.email_news"
+msgstr "Notify me of new news."
+
+msgid "settings.profile.email_by_human_allowed"
+msgstr "Allow heaven angels to contact you by e-mail."
+
+msgid "settings.profile.email_goody"
+msgstr "To receive vouchers, give consent that nick, e-mail address, worked hours and shirt size will be stored until "
+"the next similar event."
+
+msgid "settings.profile.privacy"
+msgstr "To withdraw your approval, send an e-mail to %1$s."
+
+msgid "settings.profile.shirt_size"
+msgstr "Shirt size"
+
+msgid "settings.profile.angeltypes.info"
+msgstr "You can manage your Angeltypes on the Angeltypes page."
+
msgid "settings.password"
msgstr "Password"
diff --git a/resources/views/macros/base.twig b/resources/views/macros/base.twig
index dd8feff6..2ab1908d 100644
--- a/resources/views/macros/base.twig
+++ b/resources/views/macros/base.twig
@@ -36,8 +36,15 @@
{% endmacro %}
-{% macro info(text) %}
- {{ _self.icon('info-circle') }}{{ text }}
+{% macro info(text, raw) %}
+
+ {{ _self.icon('info-circle') }}
+ {%- if raw|default(false) -%}
+ {{ text|raw }}
+ {%- else -%}
+ {{ text }}
+ {%- endif -%}
+
{%- endmacro %}
{% macro entry_required(text) %}
diff --git a/resources/views/macros/form.twig b/resources/views/macros/form.twig
index 346ebea3..66a2e244 100644
--- a/resources/views/macros/form.twig
+++ b/resources/views/macros/form.twig
@@ -12,8 +12,10 @@
type="{{ type|default('text') }}" class="form-control"
id="{{ name }}" name="{{ name }}"
value="{{ opt.value|default('')|escape('html_attr') }}"
- {%- if opt.min is defined %} minlength="{{ opt.min }}"{% endif %}
- {%- if opt.max is defined %} maxlength="{{ opt.max }}"{% endif %}
+ {%- if opt.min_length is defined %} minlength="{{ opt.min_length }}"{% endif %}
+ {%- if opt.max_length is defined %} maxlength="{{ opt.max_length }}"{% endif %}
+ {%- if opt.min is defined %} min="{{ opt.min }}"{% endif %}
+ {%- if opt.max is defined %} max="{{ opt.max }}"{% endif %}
{%- if opt.required|default(false) %}
required
{%- endif -%}
@@ -61,14 +63,18 @@
{%- endmacro %}
-{% macro checkbox(name, label, checked, value, disabled) %}
+{% macro checkbox(name, label, checked, value, disabled, raw_label) %}
{%- endmacro %}
diff --git a/resources/views/pages/password/reset-form.twig b/resources/views/pages/password/reset-form.twig
index e078ab0e..76ab2582 100644
--- a/resources/views/pages/password/reset-form.twig
+++ b/resources/views/pages/password/reset-form.twig
@@ -7,8 +7,8 @@
diff --git a/resources/views/pages/settings/password.twig b/resources/views/pages/settings/password.twig
index ba7a864f..bb148f46 100644
--- a/resources/views/pages/settings/password.twig
+++ b/resources/views/pages/settings/password.twig
@@ -24,13 +24,13 @@
'new_password',
__('settings.password.new_password'),
'password',
- {'min': min_length, 'required': true}
+ {'min_length': min_length, 'required': true}
) }}
{{ f.input(
'new_password2',
__('settings.password.new_password2'),
'password',
- {'min': min_length, 'required': true}
+ {'min_length': min_length, 'required': true}
) }}
{{ f.submit() }}
diff --git a/resources/views/pages/settings/profile.twig b/resources/views/pages/settings/profile.twig
index f3d964ba..ee56eda0 100644
--- a/resources/views/pages/settings/profile.twig
+++ b/resources/views/pages/settings/profile.twig
@@ -23,7 +23,7 @@
'pronoun',
__('settings.profile.pronoun'),
'text',
- {'value': user.personalData.pronoun ,'max': 15}
+ {'value': user.personalData.pronoun ,'max_length': 15}
) }}
{{ m.info(__('settings.profile.pronoun.info')) }}
{% endif %}
@@ -32,13 +32,13 @@
'first_name',
__('settings.profile.firstname'),
'text',
- {'value': user.personalData.first_name, 'max': 64}
+ {'value': user.personalData.first_name, 'max_length': 64}
) }}
{{ f.input(
'last_name',
__('settings.profile.lastname'),
'text',
- {'value': user.personalData.last_name, 'max': 64}
+ {'value': user.personalData.last_name, 'max_length': 64}
) }}
{% endif %}
@@ -50,16 +50,22 @@
__('settings.profile.planned_arrival_date'),
'date',
{
- 'value': user.personalData.planned_arrival_date.format(__('Y-m-d')),
+ 'value': user.personalData.planned_arrival_date.format('Y-m-d'),
'required': true,
- 'entry_required_icon': true
+ 'entry_required_icon': true,
+ 'min': config('buildup_start') ? config('buildup_start').format('Y-m-d') : '',
+ 'max': config('teardown_end') ? config('teardown_end').format('Y-m-d') : '',
}
) }}
{{ f.input(
'planned_departure_date',
__('settings.profile.planned_departure_date'),
- 'text',
- {'value': user.personalData.planned_departure_date.format(__('Y-m-d'))}
+ 'date',
+ {
+ 'value': user.personalData.planned_departure_date.format('Y-m-d'),
+ 'min': config('buildup_start') ? config('buildup_start').format('Y-m-d') : '',
+ 'max': config('teardown_end') ? config('teardown_end').format('Y-m-d') : '',
+ }
) }}
{% endif %}
@@ -67,30 +73,30 @@
{% if config('enable_dect') %}
{{ f.input(
- 'dect',
- __('settings.profile.dect'),
- 'text',
- {'value': user.contact.dect, 'max': 40}
+ 'dect',
+ __('settings.profile.dect'),
+ 'text',
+ {'value': user.contact.dect, 'max_length': 40}
) }}
{% endif %}
{{ f.input(
'mobile',
__('settings.profile.mobile'),
'text',
- {'value': user.contact.mobile, 'max': 40}
+ {'value': user.contact.mobile, 'max_length': 40}
) }}
{{ f.input(
'email',
__('settings.profile.email'),
'email',
- {'value': user.email, 'max': 254, 'required': true, 'entry_required_icon': true}
+ {'value': user.email, 'max_length': 254, 'required': true, 'entry_required_icon': true}
) }}
{{ f.checkbox(
'email_shiftinfo',
- __('settings.profile.email_shiftinfo'),
+ __('settings.profile.email_shiftinfo', [config('app_name')]),
user.settings.email_shiftinfo
) }}
{{ f.checkbox(
@@ -104,10 +110,17 @@
user.settings.email_human
) }}
{% if config('enable_goody') %}
+ {% set privacy_email = config('privacy_email') %}
+ {% set email_goody_label = __('settings.profile.email_goody') ~
+ (privacy_email ? ' ' ~ __('settings.profile.privacy', [privacy_email]) : '')
+ %}
{{ f.checkbox(
'email_goody',
- __('settings.profile.email_goody'),
- user.settings.email_goody
+ email_goody_label,
+ user.settings.email_goody,
+ user.settings.email_goody,
+ false,
+ true
) }}
{% endif %}
@@ -122,7 +135,7 @@
- {{ m.info(__('settings.profile.user_details.info')) }}
+ {{ m.info(__('settings.profile.angeltypes.info', [url('/angeltypes')]), true) }}
{{ f.submit() }}
diff --git a/src/Controllers/ChecksArrivalsAndDepartures.php b/src/Controllers/ChecksArrivalsAndDepartures.php
new file mode 100644
index 00000000..a1f211e4
--- /dev/null
+++ b/src/Controllers/ChecksArrivalsAndDepartures.php
@@ -0,0 +1,52 @@
+toCarbon($arrival);
+
+ if (!is_null($departure) && $arrival_carbon->greaterThan($this->toCarbon($departure))) {
+ return false;
+ }
+
+ return !$this->isBeforeBuildup($arrival_carbon) && !$this->isAfterTeardown($arrival_carbon);
+ }
+
+ protected function isDepartureDateValid(?string $arrival, ?string $departure): bool
+ {
+ if (is_null($departure)) {
+ return true; // since optional value
+ }
+ $departure_carbon = $this->toCarbon($departure);
+
+ return $departure_carbon->greaterThanOrEqualTo($this->toCarbon($arrival)) &&
+ !$this->isBeforeBuildup($departure_carbon) && !$this->isAfterTeardown($departure_carbon);
+ }
+
+ private function toCarbon(string $date_string): Carbon
+ {
+ return new Carbon(DateTime::createFromFormat('Y-m-d', $date_string));
+ }
+
+ private function isBeforeBuildup(Carbon $date): bool
+ {
+ $buildup = config('buildup_start');
+ return !empty($buildup) && $date->lessThan($buildup->setTime(0,0));
+ }
+
+ private function isAfterTeardown(Carbon $date): bool
+ {
+ $teardown = config('teardown_end');
+ return !empty($teardown) && $date->greaterThanOrEqualTo($teardown->addDay()->setTime(0,0));
+ }
+}
diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php
index 59bcee46..777c8a6c 100644
--- a/src/Controllers/SettingsController.php
+++ b/src/Controllers/SettingsController.php
@@ -2,6 +2,7 @@
namespace Engelsystem\Controllers;
+use Carbon\Carbon;
use Engelsystem\Config\Config;
use Engelsystem\Http\Exceptions\HttpNotFound;
use Engelsystem\Http\Response;
@@ -13,6 +14,7 @@ use Psr\Log\LoggerInterface;
class SettingsController extends BaseController
{
use HasUserNotifications;
+ use ChecksArrivalsAndDepartures;
/** @var Authenticator */
protected $auth;
@@ -72,6 +74,10 @@ class SettingsController extends BaseController
public function saveProfile(Request $request): Response
{
$user = $this->auth->user();
+
+ config('buildup_start');
+ config('teardown_end');
+
$data = $this->validate($request, [
'pronoun' => 'optional|max:15',
'first_name' => 'optional|max:64',
@@ -98,8 +104,18 @@ class SettingsController extends BaseController
}
if (config('enable_planned_arrival')) {
- $user->personalData->planned_arrival_date = $data['planned_arrival_date'];
- $user->personalData->planned_departure_date = $data['planned_departure_date'];
+ if (!$this->isArrivalDateValid($data['planned_arrival_date'], $data['planned_departure_date'])) {
+ $this->addNotification('settings.profile.planned_arrival_date.invalid', 'errors');
+ return $this->redirect->to('/settings/profile');
+
+ } else if (!$this->isDepartureDateValid($data['planned_arrival_date'], $data['planned_departure_date'])) {
+ $this->addNotification('settings.profile.planned_departure_date.invalid', 'errors');
+ return $this->redirect->to('/settings/profile');
+
+ } else {
+ $user->personalData->planned_arrival_date = $data['planned_arrival_date'];
+ $user->personalData->planned_departure_date = $data['planned_departure_date'];
+ }
}
if (config('enable_dect')) {
@@ -116,7 +132,10 @@ class SettingsController extends BaseController
$user->settings->email_goody = $data['email_goody'];
}
- $user->personalData->shirt_size = $data['shirt_size'];
+ if (isset(config('tshirt_sizes')[$data['shirt_size']])) {
+ $user->personalData->shirt_size = $data['shirt_size'];
+ $user->personalData->save();
+ }
$user->personalData->save();
$user->contact->save();
diff --git a/tests/Unit/Controllers/ChecksArrivalsAndDeparturesTest.php b/tests/Unit/Controllers/ChecksArrivalsAndDeparturesTest.php
new file mode 100644
index 00000000..085b92bb
--- /dev/null
+++ b/tests/Unit/Controllers/ChecksArrivalsAndDeparturesTest.php
@@ -0,0 +1,92 @@
+ is_null($buildup) ? null: new Carbon($buildup)]);
+ config(['teardown_end' => is_null($teardown) ? null: new Carbon($teardown)]);
+
+ $check = new ChecksArrivalsAndDeparturesImplementation();
+ $this->assertFalse($check->checkArrival($arrival, $departure));
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\ChecksArrivalsAndDepartures::isDepartureDateValid
+ * @dataProvider invalidDateCombinations
+ */
+ public function testCheckInvalidDatesForDeparture($buildup, $teardown, $arrival, $departure)
+ {
+ config(['buildup_start' => is_null($buildup) ? null: new Carbon($buildup)]);
+ config(['teardown_end' => is_null($teardown) ? null: new Carbon($teardown)]);
+
+ $check = new ChecksArrivalsAndDeparturesImplementation();
+ $this->assertFalse($check->checkDeparture($arrival, $departure));
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\ChecksArrivalsAndDepartures::isArrivalDateValid
+ * @dataProvider validDateCombinations
+ */
+ public function testCheckValidDatesForArrival($buildup, $teardown, $arrival, $departure)
+ {
+ config(['buildup_start' => is_null($buildup) ? null: new Carbon($buildup)]);
+ config(['teardown_end' => is_null($teardown) ? null: new Carbon($teardown)]);
+
+ $check = new ChecksArrivalsAndDeparturesImplementation();
+ $this->assertTrue($check->checkArrival($arrival, $departure));
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\ChecksArrivalsAndDepartures::isDepartureDateValid
+ * @dataProvider validDateCombinations
+ */
+ public function testCheckValidDatesForDeparture($buildup, $teardown, $arrival, $departure)
+ {
+ config(['buildup_start' => is_null($buildup) ? null: new Carbon($buildup)]);
+ config(['teardown_end' => is_null($teardown) ? null: new Carbon($teardown)]);
+
+ $check = new ChecksArrivalsAndDeparturesImplementation();
+ $this->assertTrue($check->checkDeparture($arrival, $departure));
+ }
+
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->config = new Config();
+ $this->app->instance('config', $this->config);
+ $this->app->instance(Config::class, $this->config);
+ }
+}
diff --git a/tests/Unit/Controllers/SettingsControllerTest.php b/tests/Unit/Controllers/SettingsControllerTest.php
index 0563e3b9..b3ff8e30 100644
--- a/tests/Unit/Controllers/SettingsControllerTest.php
+++ b/tests/Unit/Controllers/SettingsControllerTest.php
@@ -613,7 +613,13 @@ class SettingsControllerTest extends TestCase
'en_US' => 'English',
'de_DE' => 'Deutsch'
];
- $this->config = new Config(['min_password_length' => 6, 'themes' => $themes, 'locales' => $languages]);
+ $tshirt_sizes = ['S' => 'Small'];
+ $this->config = new Config([
+ 'min_password_length' => 6,
+ 'themes' => $themes,
+ 'locales' => $languages,
+ 'tshirt_sizes' => $tshirt_sizes
+ ]);
$this->app->instance('config', $this->config);
$this->app->instance(Config::class, $this->config);
diff --git a/tests/Unit/Controllers/Stub/ChecksArrivalsAndDeparturesImplementation.php b/tests/Unit/Controllers/Stub/ChecksArrivalsAndDeparturesImplementation.php
new file mode 100644
index 00000000..06465d7a
--- /dev/null
+++ b/tests/Unit/Controllers/Stub/ChecksArrivalsAndDeparturesImplementation.php
@@ -0,0 +1,20 @@
+isArrivalDateValid($arrival, $departure);
+ }
+
+ public function checkDeparture(string $arrival, string $departure): bool
+ {
+ return $this->isDepartureDateValid($arrival, $departure);
+ }
+}