diff --git a/config/routes.php b/config/routes.php
index a0a843a1..e88f9581 100644
--- a/config/routes.php
+++ b/config/routes.php
@@ -20,6 +20,8 @@ $route->post('/oauth/{provider}/connect', 'OAuthController@connect');
$route->post('/oauth/{provider}/disconnect', 'OAuthController@disconnect');
// User settings
+$route->get('/settings/profile', 'SettingsController@profile');
+$route->post('/settings/profile', 'SettingsController@saveProfile');
$route->get('/settings/password', 'SettingsController@password');
$route->post('/settings/password', 'SettingsController@savePassword');
$route->get('/settings/theme', 'SettingsController@theme');
diff --git a/resources/views/macros/base.twig b/resources/views/macros/base.twig
index b6dfb19e..dd8feff6 100644
--- a/resources/views/macros/base.twig
+++ b/resources/views/macros/base.twig
@@ -40,6 +40,10 @@
{{ _self.icon('info-circle') }}{{ text }}
{%- endmacro %}
+{% macro entry_required(text) %}
+
{% if label -%}
-
+
{%- endif %}
+ {{ csrf() }}
+
+
+
+ {{ m.info(__('settings.profile.user_details.info')) }}
+ {{ m.entry_required(' = ' ~ __('settings.profile.entry_required')) }}
+ {{ f.input(
+ 'nick',
+ __('settings.profile.nick'),
+ 'text',
+ {'value': user.name, 'disabled': true}
+ ) }}
+ {% if config('enable_pronoun') %}
+ {{ f.input(
+ 'pronoun',
+ __('settings.profile.pronoun'),
+ 'text',
+ {'value': user.personalData.pronoun ,'max': 15}
+ ) }}
+ {{ m.info(__('settings.profile.pronoun.info')) }}
+ {% endif %}
+ {% if config('enable_user_name') %}
+ {{ f.input(
+ 'first_name',
+ __('settings.profile.firstname'),
+ 'text',
+ {'value': user.personalData.first_name, 'max': 64}
+ ) }}
+ {{ f.input(
+ 'last_name',
+ __('settings.profile.lastname'),
+ 'text',
+ {'value': user.personalData.last_name, 'max': 64}
+ ) }}
+ {% endif %}
+
+
+ {% if config('enable_planned_arrival') %}
+
+ {{ f.input(
+ 'planned_arrival_date',
+ __('settings.profile.planned_arrival_date'),
+ 'date',
+ {
+ 'value': user.personalData.planned_arrival_date.format(__('Y-m-d')),
+ 'required': true,
+ 'entry_required_icon': true
+ }
+ ) }}
+ {{ f.input(
+ 'planned_departure_date',
+ __('settings.profile.planned_departure_date'),
+ 'text',
+ {'value': user.personalData.planned_departure_date.format(__('Y-m-d'))}
+ ) }}
+
+ {% endif %}
+
+
+ {% if config('enable_dect') %}
+ {{ f.input(
+ 'dect',
+ __('settings.profile.dect'),
+ 'text',
+ {'value': user.contact.dect, 'max': 40}
+ ) }}
+ {% endif %}
+ {{ f.input(
+ 'mobile',
+ __('settings.profile.mobile'),
+ 'text',
+ {'value': user.contact.mobile, 'max': 40}
+ ) }}
+ {{ f.input(
+ 'email',
+ __('settings.profile.email'),
+ 'email',
+ {'value': user.email, 'max': 254, 'required': true, 'entry_required_icon': true}
+ ) }}
+
+
+
+ {{ f.checkbox(
+ 'email_shiftinfo',
+ __('settings.profile.email_shiftinfo'),
+ user.settings.email_shiftinfo
+ ) }}
+ {{ f.checkbox(
+ 'email_news',
+ __('settings.profile.email_news'),
+ user.settings.email_news
+ ) }}
+ {{ f.checkbox(
+ 'email_human',
+ __('settings.profile.email_by_human_allowed'),
+ user.settings.email_human
+ ) }}
+ {% if config('enable_goody') %}
+ {{ f.checkbox(
+ 'email_goody',
+ __('settings.profile.email_goody'),
+ user.settings.email_goody
+ ) }}
+ {% endif %}
+
+
+
+ {{ f.select(
+ 'shirt_size',
+ config('tshirt_sizes'),
+ __('settings.profile.shirt_size'),
+ user.personalData.shirt_size
+ ) }}
+
+
+
+ {{ m.info(__('settings.profile.user_details.info')) }}
+ {{ f.submit() }}
+
+
+
+{% endblock %}
diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php
index a3359126..59bcee46 100644
--- a/src/Controllers/SettingsController.php
+++ b/src/Controllers/SettingsController.php
@@ -52,6 +52,82 @@ class SettingsController extends BaseController
$this->response = $response;
}
+ public function profile(): Response
+ {
+ $user = $this->auth->user();
+
+ return $this->response->withView(
+ 'pages/settings/profile',
+ [
+ 'settings_menu' => $this->settingsMenu(),
+ 'user' => $user,
+ ] + $this->getNotifications()
+ );
+ }
+
+ /**
+ * @param Request $request
+ * @return Response
+ */
+ public function saveProfile(Request $request): Response
+ {
+ $user = $this->auth->user();
+ $data = $this->validate($request, [
+ 'pronoun' => 'optional|max:15',
+ 'first_name' => 'optional|max:64',
+ 'last_name' => 'optional|max:64',
+ 'planned_arrival_date' => 'required|date:Y-m-d',
+ 'planned_departure_date' => 'optional|date:Y-m-d',
+ 'dect' => 'optional|length:0:40', // dect/mobile can be purely numbers. "max" would have
+ 'mobile' => 'optional|length:0:40', // checked their values, not their character length.
+ 'email' => 'required|email|max:254',
+ 'email_shiftinfo' => 'optional|checked',
+ 'email_news' => 'optional|checked',
+ 'email_human' => 'optional|checked',
+ 'email_goody' => 'optional|checked',
+ 'shirt_size' => 'required',
+ ]);
+
+ if (config('enable_pronoun')) {
+ $user->personalData->pronoun = $data['pronoun'];
+ }
+
+ if (config('enable_user_name')) {
+ $user->personalData->first_name = $data['first_name'];
+ $user->personalData->last_name = $data['last_name'];
+ }
+
+ if (config('enable_planned_arrival')) {
+ $user->personalData->planned_arrival_date = $data['planned_arrival_date'];
+ $user->personalData->planned_departure_date = $data['planned_departure_date'];
+ }
+
+ if (config('enable_dect')) {
+ $user->contact->dect = $data['dect'];
+ }
+
+ $user->contact->mobile = $data['mobile'];
+ $user->email = $data['email'];
+ $user->settings->email_shiftinfo = $data['email_shiftinfo'];
+ $user->settings->email_news = $data['email_news'];
+ $user->settings->email_human = $data['email_human'];
+
+ if (config('enable_goody')) {
+ $user->settings->email_goody = $data['email_goody'];
+ }
+
+ $user->personalData->shirt_size = $data['shirt_size'];
+
+ $user->personalData->save();
+ $user->contact->save();
+ $user->settings->save();
+ $user->save();
+
+ $this->addNotification('settings.profile.success');
+
+ return $this->redirect->to('/settings/profile');
+ }
+
/**
* @return Response
*/
@@ -62,7 +138,6 @@ class SettingsController extends BaseController
[
'settings_menu' => $this->settingsMenu(),
'min_length' => config('min_password_length')
-
] + $this->getNotifications()
);
}
diff --git a/tests/Unit/Controllers/SettingsControllerTest.php b/tests/Unit/Controllers/SettingsControllerTest.php
index fddf8936..0563e3b9 100644
--- a/tests/Unit/Controllers/SettingsControllerTest.php
+++ b/tests/Unit/Controllers/SettingsControllerTest.php
@@ -3,10 +3,15 @@
namespace Engelsystem\Test\Unit\Controllers;
use Engelsystem\Config\Config;
+use Engelsystem\Controllers\Admin\UserShirtController;
use Engelsystem\Controllers\SettingsController;
use Engelsystem\Http\Exceptions\HttpNotFound;
+use Engelsystem\Http\Redirector;
use Engelsystem\Http\Response;
+use Engelsystem\Models\User\Contact;
+use Engelsystem\Models\User\PersonalData;
use Engelsystem\Models\User\Settings;
+use Engelsystem\Models\User\State;
use Engelsystem\Test\Unit\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\HttpFoundation\Session\Session;
@@ -48,6 +53,150 @@ class SettingsControllerTest extends TestCase
/** @var Session */
protected $session;
+ /** @var SettingsController */
+ protected $controller;
+
+ protected function setUpProfileTest() {
+ $body = [
+ 'pronoun' => 'Herr',
+ 'first_name' => 'John',
+ 'last_name' => 'Doe',
+ 'planned_arrival_date' => '2022-01-01',
+ 'planned_departure_date' => '2022-01-02',
+ 'dect' => '1234',
+ 'mobile' => '0123456789',
+ 'email' => 'a@bc.de',
+ 'email_shiftinfo' => true,
+ 'email_news' => true,
+ 'email_human' => true,
+ 'email_goody' => true,
+ 'shirt_size' => 'S',
+ ];
+ $this->request = $this->request->withParsedBody($body);
+ $this->setExpects(
+ $this->response,
+ 'redirectTo',
+ ['http://localhost/settings/profile'],
+ $this->response,
+ $this->atLeastOnce()
+ );
+
+ config([
+ 'enable_pronoun' => true,
+ 'enable_user_name' => true,
+ 'enable_planned_arrival' => true,
+ 'enable_dect' => true,
+ 'enable_goody' => true,
+ ]);
+
+ $this->setExpects($this->auth, 'user', null, $this->user, $this->atLeastOnce());
+
+ $this->controller = $this->app->make(SettingsController::class);
+ $this->controller->setValidator(new Validator());
+
+ return $body;
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::profile
+ */
+ public function testProfile()
+ {
+ $this->setExpects($this->auth, 'user', null, $this->user, $this->once());
+ /** @var Response|MockObject $response */
+ $this->response->expects($this->once())
+ ->method('withView')
+ ->willReturnCallback(function ($view, $data) {
+ $this->assertEquals('pages/settings/profile', $view);
+ $this->assertArrayHasKey('user', $data);
+ $this->assertEquals($this->user, $data['user']);
+ return $this->response;
+ });
+
+ $this->controller = $this->app->make(SettingsController::class);
+ $this->controller->profile();
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::saveProfile
+ */
+ public function testSaveProfile()
+ {
+ $body = $this->setUpProfileTest();
+ $this->controller->saveProfile($this->request);
+
+ $this->assertEquals($body['pronoun'], $this->user->personalData->pronoun);
+ $this->assertEquals($body['first_name'], $this->user->personalData->first_name);
+ $this->assertEquals($body['last_name'], $this->user->personalData->last_name);
+ $this->assertEquals($body['planned_arrival_date'], $this->user->personalData->planned_arrival_date->format('Y-m-d'));
+ $this->assertEquals($body['planned_departure_date'], $this->user->personalData->planned_departure_date->format('Y-m-d'));
+ $this->assertEquals($body['dect'], $this->user->contact->dect);
+ $this->assertEquals($body['mobile'], $this->user->contact->mobile);
+ $this->assertEquals($body['email'], $this->user->email);
+ $this->assertEquals($body['email_shiftinfo'], $this->user->settings->email_shiftinfo);
+ $this->assertEquals($body['email_news'], $this->user->settings->email_news);
+ $this->assertEquals($body['email_human'], $this->user->settings->email_human);
+ $this->assertEquals($body['email_goody'], $this->user->settings->email_goody);
+ $this->assertEquals($body['shirt_size'], $this->user->personalData->shirt_size);
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::saveProfile
+ */
+ public function testSaveProfileIgnoresPronounIfDisabled()
+ {
+ $this->setUpProfileTest();
+ config(['enable_pronoun' => false]);
+ $this->controller->saveProfile($this->request);
+ $this->assertEquals('', $this->user->personalData->pronoun);
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::saveProfile
+ */
+ public function testSaveProfileIgnoresFirstAndLastnameIfDisabled()
+ {
+ $this->setUpProfileTest();
+ config(['enable_user_name' => false]);
+ $this->controller->saveProfile($this->request);
+ $this->assertEquals('', $this->user->personalData->first_name);
+ $this->assertEquals('', $this->user->personalData->last_name);
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::saveProfile
+ */
+ public function testSaveProfileIgnoresArrivalDatesIfDisabled()
+ {
+ $this->setUpProfileTest();
+ config(['enable_planned_arrival' => false]);
+ $this->controller->saveProfile($this->request);
+ $this->assertEquals('', $this->user->personalData->planned_arrival_date);
+ $this->assertEquals('', $this->user->personalData->planned_departure_date);
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::saveProfile
+ */
+ public function testSaveProfileIgnoresDectIfDisabled()
+ {
+ $this->setUpProfileTest();
+ config(['enable_dect' => false]);
+ $this->controller->saveProfile($this->request);
+ $this->assertEquals('', $this->user->contact->dect);
+ }
+
+ /**
+ * @covers \Engelsystem\Controllers\SettingsController::saveProfile
+ */
+ public function testSaveProfileIgnoresEmailGoodyIfDisabled()
+ {
+ $this->setUpProfileTest();
+ config(['enable_goody' => false]);
+ $this->controller->saveProfile($this->request);
+ $this->assertFalse($this->user->settings->email_goody);
+ }
+
/**
* @covers \Engelsystem\Controllers\SettingsController::password
*/
@@ -62,9 +211,7 @@ class SettingsControllerTest extends TestCase
return $this->response;
});
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->password();
+ $this->controller->password();
}
/**
@@ -90,10 +237,7 @@ class SettingsControllerTest extends TestCase
$this->once()
);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->savePassword($this->request);
+ $this->controller->savePassword($this->request);
$this->assertTrue($this->log->hasInfoThatContains('User set new password.'));
@@ -127,10 +271,7 @@ class SettingsControllerTest extends TestCase
$this->once()
);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->savePassword($this->request);
+ $this->controller->savePassword($this->request);
}
/**
@@ -156,10 +297,7 @@ class SettingsControllerTest extends TestCase
$this->once()
);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->savePassword($this->request);
+ $this->controller->savePassword($this->request);
/** @var Session $session */
$session = $this->app->get('session');
@@ -190,10 +328,7 @@ class SettingsControllerTest extends TestCase
$this->once()
);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->savePassword($this->request);
+ $this->controller->savePassword($this->request);
/** @var Session $session */
$session = $this->app->get('session');
@@ -238,10 +373,7 @@ class SettingsControllerTest extends TestCase
$this->expectException(ValidationException::class);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->savePassword($this->request);
+ $this->controller->savePassword($this->request);
}
/**
@@ -266,9 +398,7 @@ class SettingsControllerTest extends TestCase
return $this->response;
});
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->theme();
+ $this->controller->theme();
}
/**
@@ -280,10 +410,7 @@ class SettingsControllerTest extends TestCase
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
$this->expectException(ValidationException::class);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->saveTheme($this->request);
+ $this->controller->saveTheme($this->request);
}
/**
@@ -297,10 +424,7 @@ class SettingsControllerTest extends TestCase
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
$this->expectException(HttpNotFound::class);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->saveTheme($this->request);
+ $this->controller->saveTheme($this->request);
}
/**
@@ -318,10 +442,7 @@ class SettingsControllerTest extends TestCase
$this->request = $this->request->withParsedBody(['select_theme' => 0]);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->saveTheme($this->request);
+ $this->controller->saveTheme($this->request);
$this->assertEquals(0, $this->user->settings->theme);
}
@@ -348,9 +469,7 @@ class SettingsControllerTest extends TestCase
return $this->response;
});
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->language();
+ $this->controller->language();
}
/**
@@ -362,10 +481,7 @@ class SettingsControllerTest extends TestCase
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
$this->expectException(ValidationException::class);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->saveLanguage($this->request);
+ $this->controller->saveLanguage($this->request);
}
/**
@@ -379,10 +495,7 @@ class SettingsControllerTest extends TestCase
$this->setExpects($this->auth, 'user', null, $this->user, $this->once());
$this->expectException(HttpNotFound::class);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->saveLanguage($this->request);
+ $this->controller->saveLanguage($this->request);
}
/**
@@ -402,10 +515,7 @@ class SettingsControllerTest extends TestCase
$this->request = $this->request->withParsedBody(['select_language' => 'de_DE']);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->setValidator(new Validator());
- $controller->saveLanguage($this->request);
+ $this->controller->saveLanguage($this->request);
$this->assertEquals('de_DE', $this->user->settings->language);
$this->assertEquals('de_DE', $this->session->get('locale'));
@@ -430,9 +540,7 @@ class SettingsControllerTest extends TestCase
return $this->response;
});
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
- $controller->oauth();
+ $this->controller->oauth();
}
/**
@@ -442,11 +550,8 @@ class SettingsControllerTest extends TestCase
{
config(['oauth' => []]);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
-
$this->expectException(HttpNotFound::class);
- $controller->oauth();
+ $this->controller->oauth();
}
/**
@@ -459,16 +564,13 @@ class SettingsControllerTest extends TestCase
$providersHidden = ['foo' => ['lorem' => 'ipsum', 'hidden' => true]];
config(['oauth' => $providers]);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
-
$this->assertEquals([
'http://localhost/user-settings' => 'settings.profile',
'http://localhost/settings/password' => 'settings.password',
'http://localhost/settings/language' => 'settings.language',
'http://localhost/settings/theme' => 'settings.theme',
'http://localhost/settings/oauth' => ['title' => 'settings.oauth', 'hidden' => false]
- ], $controller->settingsMenu());
+ ], $this->controller->settingsMenu());
config(['oauth' => $providersHidden]);
$this->assertEquals([
@@ -477,7 +579,7 @@ class SettingsControllerTest extends TestCase
'http://localhost/settings/language' => 'settings.language',
'http://localhost/settings/theme' => 'settings.theme',
'http://localhost/settings/oauth' => ['title' => 'settings.oauth', 'hidden' => true]
- ], $controller->settingsMenu());
+ ], $this->controller->settingsMenu());
}
/**
@@ -487,15 +589,12 @@ class SettingsControllerTest extends TestCase
{
config(['oauth' => []]);
- /** @var SettingsController $controller */
- $controller = $this->app->make(SettingsController::class);
-
$this->assertEquals([
'http://localhost/user-settings' => 'settings.profile',
'http://localhost/settings/password' => 'settings.password',
'http://localhost/settings/language' => 'settings.language',
'http://localhost/settings/theme' => 'settings.theme'
- ], $controller->settingsMenu());
+ ], $this->controller->settingsMenu());
}
/**
@@ -539,7 +638,10 @@ class SettingsControllerTest extends TestCase
$this->app->instance(Authenticator::class, $this->auth);
$this->user = User::factory()
- ->has(Settings::factory(['theme' => 1, 'language' => 'en_US']))
+ ->has(Settings::factory(['theme' => 1, 'language' => 'en_US', 'email_goody' => false]))
->create();
+
+ $this->controller = $this->app->make(SettingsController::class);
+ $this->controller->setValidator(new Validator());
}
}
diff --git a/tests/Unit/Http/Validation/ValidatorTest.php b/tests/Unit/Http/Validation/ValidatorTest.php
index 124673df..35a9716d 100644
--- a/tests/Unit/Http/Validation/ValidatorTest.php
+++ b/tests/Unit/Http/Validation/ValidatorTest.php
@@ -60,6 +60,27 @@ class ValidatorTest extends TestCase
));
}
+ /**
+ * @covers \Engelsystem\Http\Validation\Validator::validate
+ */
+ public function testValidateMultipleParameters()
+ {
+ $val = new Validator();
+
+ $this->assertFalse($val->validate(
+ ['lorem' => 'h'],
+ ['lorem' => 'length:2:3']
+ ));
+ $this->assertTrue($val->validate(
+ ['lorem' => 'hey'],
+ ['lorem' => 'length:2:3']
+ ));
+ $this->assertFalse($val->validate(
+ ['lorem' => 'heyy'],
+ ['lorem' => 'length:2:3']
+ ));
+ }
+
/**
* @covers \Engelsystem\Http\Validation\Validator::validate
*/