diff --git a/config/routes.php b/config/routes.php index 2f4a903d..a0a843a1 100644 --- a/config/routes.php +++ b/config/routes.php @@ -24,6 +24,8 @@ $route->get('/settings/password', 'SettingsController@password'); $route->post('/settings/password', 'SettingsController@savePassword'); $route->get('/settings/theme', 'SettingsController@theme'); $route->post('/settings/theme', 'SettingsController@saveTheme'); +$route->get('/settings/language', 'SettingsController@language'); +$route->post('/settings/language', 'SettingsController@saveLanguage'); $route->get('/settings/oauth', 'SettingsController@oauth'); // Password recovery diff --git a/includes/pages/user_settings.php b/includes/pages/user_settings.php index 8b4f9872..3998715a 100644 --- a/includes/pages/user_settings.php +++ b/includes/pages/user_settings.php @@ -105,36 +105,6 @@ function user_settings_main($user_source, $enable_tshirt_size, $tshirt_sizes) return $user_source; } -/** - * Change use locale - * - * @param User $user_source The user - * @param array $locales List of available locales - * @return User - */ -function user_settings_locale($user_source, $locales) -{ - $valid = true; - $request = request(); - $session = session(); - - if ($request->has('language') && isset($locales[$request->input('language')])) { - $user_source->settings->language = $request->input('language'); - } else { - $valid = false; - } - - if ($valid) { - $user_source->settings->save(); - $session->set('locale', $user_source->settings->language); - - success('Language changed.'); - throw_redirect(page_link_to('user_settings')); - } - - return $user_source; -} - /** * Main user settings page/controller * @@ -147,7 +117,6 @@ function user_settings() $enable_tshirt_size = config('enable_tshirt_size'); $tshirt_sizes = config('tshirt_sizes'); - $locales = config('locales'); $buildup_start_date = null; $teardown_end_date = null; @@ -165,13 +134,10 @@ function user_settings() $user_source = auth()->user(); if ($request->hasPostData('submit')) { $user_source = user_settings_main($user_source, $enable_tshirt_size, $tshirt_sizes); - } elseif ($request->hasPostData('submit_language')) { - $user_source = user_settings_locale($user_source, $locales); } return User_settings_view( $user_source, - $locales, $buildup_start_date, $teardown_end_date, $enable_tshirt_size, diff --git a/includes/view/User_view.php b/includes/view/User_view.php index 4fe5f43f..393806c9 100644 --- a/includes/view/User_view.php +++ b/includes/view/User_view.php @@ -13,7 +13,6 @@ use Engelsystem\Controllers\SettingsController; * Renders user settings page * * @param User $user_source The user - * @param array $locales Available languages * @param int $buildup_start_date Unix timestamp * @param int $teardown_end_date Unix timestamp * @param bool $enable_tshirt_size @@ -23,7 +22,6 @@ use Engelsystem\Controllers\SettingsController; */ function User_settings_view( $user_source, - $locales, $buildup_start_date, $teardown_end_date, $enable_tshirt_size, @@ -116,11 +114,6 @@ function User_settings_view( form_info('', __('Please visit the angeltypes page to manage your angeltypes.')), form_submit('submit', __('Save')) ]), - form([ - form_info(__('Here you can choose your language:')), - form_select('language', __('Language:'), $locales, $user_source->settings->language), - form_submit('submit_language', __('Save')) - ]), ]) ]) ] diff --git a/resources/lang/de_DE/default.po b/resources/lang/de_DE/default.po index 6fd3d658..5d05ada0 100644 --- a/resources/lang/de_DE/default.po +++ b/resources/lang/de_DE/default.po @@ -2521,14 +2521,6 @@ msgstr "Geplanter Abreisetag" msgid "Please visit the angeltypes page to manage your angeltypes." msgstr "Bitte benutze die Engeltypen-Seite um deine Engeltypen zu verwalten." -#: includes/view/User_view.php:101 -msgid "Here you can choose your language:" -msgstr "Hier kannst Du Deine Sprache auswählen:" - -#: includes/view/User_view.php:102 -msgid "Language:" -msgstr "Sprache:" - #: includes/view/User_view.php:124 msgid "" "Do you really want to delete the user including all his shifts and every " @@ -2965,6 +2957,15 @@ msgstr "Hier kannst Du Dein Theme ändern." msgid "settings.theme.success" msgstr "Theme wurde erfolgreich geändert." +msgid "settings.language" +msgstr "Sprache" + +msgid "settings.language.info" +msgstr "Hier kannst Du Deine Sprache ändern." + +msgid "settings.language.success" +msgstr "Sprache wurde erfolgreich geändert." + msgid "settings.oauth" msgstr "Single Sign-On" diff --git a/resources/lang/en_US/default.po b/resources/lang/en_US/default.po index bda63109..be0f6a95 100644 --- a/resources/lang/en_US/default.po +++ b/resources/lang/en_US/default.po @@ -235,6 +235,15 @@ msgstr "Here you can change your theme." msgid "settings.theme.success" msgstr "Theme was changed successfully." +msgid "settings.language" +msgstr "Language" + +msgid "settings.language.info" +msgstr "Here you can change your language." + +msgid "settings.language.success" +msgstr "Language was changed successfully." + msgid "settings.oauth" msgstr "Single Sign-On" diff --git a/resources/lang/pt_BR/default.po b/resources/lang/pt_BR/default.po index e6cd3345..56baa9a2 100644 --- a/resources/lang/pt_BR/default.po +++ b/resources/lang/pt_BR/default.po @@ -2207,14 +2207,6 @@ msgstr "Nova senha:" msgid "Password confirmation:" msgstr "Confirmação de senha:" -#: includes/view/User_view.php:73 -msgid "Here you can choose your language:" -msgstr "Aqui você pode selecionar seu idioma:" - -#: includes/view/User_view.php:74 -msgid "Language:" -msgstr "Idioma:" - #: includes/view/User_view.php:88 msgid "Registration successful" msgstr "Registrado com sucesso" diff --git a/resources/views/pages/settings/language.twig b/resources/views/pages/settings/language.twig new file mode 100644 index 00000000..888dc0c7 --- /dev/null +++ b/resources/views/pages/settings/language.twig @@ -0,0 +1,19 @@ +{% extends 'pages/settings/settings.twig' %} +{% import 'macros/form.twig' as f %} +{% import 'macros/base.twig' as m %} + +{% block title %}{{ __('settings.language') }}{% endblock %} + +{% block row_content %} +
+ {{ csrf() }} + +
+
+ {{ m.info(__('settings.language.info')) }} + {{ f.select('select_language', languages, __('settings.language'), current_language) }} + {{ f.submit() }} +
+
+
+{% endblock %} diff --git a/src/Controllers/SettingsController.php b/src/Controllers/SettingsController.php index 59d510db..a3359126 100644 --- a/src/Controllers/SettingsController.php +++ b/src/Controllers/SettingsController.php @@ -139,6 +139,49 @@ class SettingsController extends BaseController return $this->redirect->to('/settings/theme'); } + /** + * @return Response + */ + public function language(): Response + { + $languages = config('locales'); + + $currentLanguage = $this->auth->user()->settings->language; + + return $this->response->withView( + 'pages/settings/language', + [ + 'settings_menu' => $this->settingsMenu(), + 'languages' => $languages, + 'current_language' => $currentLanguage + ] + $this->getNotifications() + ); + } + + /** + * @param Request $request + * @return Response + */ + public function saveLanguage(Request $request): Response + { + $user = $this->auth->user(); + $data = $this->validate($request, ['select_language' => 'required']); + $selectLanguage = $data['select_language']; + + if (!isset(config('locales')[$selectLanguage])) { + throw new HttpNotFound('Language ' . $selectLanguage . ' does not exist.'); + } + + $user->settings->language = $selectLanguage; + $user->settings->save(); + + session()->set('locale', $selectLanguage); + + $this->addNotification('settings.language.success'); + + return $this->redirect->to('/settings/language'); + } + /** * @return Response */ @@ -166,6 +209,7 @@ class SettingsController extends BaseController $menu = [ url('/user-settings') => 'settings.profile', url('/settings/password') => 'settings.password', + url('/settings/language') => 'settings.language', url('/settings/theme') => 'settings.theme' ]; diff --git a/tests/Unit/Controllers/SettingsControllerTest.php b/tests/Unit/Controllers/SettingsControllerTest.php index 8942ef0b..fddf8936 100644 --- a/tests/Unit/Controllers/SettingsControllerTest.php +++ b/tests/Unit/Controllers/SettingsControllerTest.php @@ -45,6 +45,9 @@ class SettingsControllerTest extends TestCase /** @var User */ protected $user; + /** @var Session */ + protected $session; + /** * @covers \Engelsystem\Controllers\SettingsController::password */ @@ -323,6 +326,91 @@ class SettingsControllerTest extends TestCase $this->assertEquals(0, $this->user->settings->theme); } + /** + * @testdox language: underNormalConditions -> returnsCorrectViewAndData + * @covers \Engelsystem\Controllers\SettingsController::language + */ + public function testLanguageUnderNormalConditionReturnsCorrectViewAndData() + { + $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/language', $view); + $this->assertArrayHasKey('settings_menu', $data); + $this->assertArrayHasKey('languages', $data); + $this->assertArrayHasKey('current_language', $data); + $this->assertEquals(['en_US' => 'English', 'de_DE' => 'Deutsch'], $data['languages']); + $this->assertEquals('en_US', $data['current_language']); + + return $this->response; + }); + + /** @var SettingsController $controller */ + $controller = $this->app->make(SettingsController::class); + $controller->language(); + } + + /** + * @testdox saveLanguage: withNoSelectedLanguageGiven -> throwsException + * @covers \Engelsystem\Controllers\SettingsController::saveLanguage + */ + public function testSaveLanguageWithNoSelectedLanguageGivenThrowsException() + { + $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); + } + + /** + * @testdox saveLanguage: withUnknownSelectedLanguageGiven -> throwsException + * @covers \Engelsystem\Controllers\SettingsController::saveLanguage + */ + public function testSaveLanguageWithUnknownSelectedLanguageGivenThrowsException() + { + $this->request = $this->request->withParsedBody(['select_language' => 'unknown']); + + $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); + } + + /** + * @testdox saveLanguage: withKnownSelectedLanguageGiven -> savesLanguageAndUpdatesSessionAndRedirect + * @covers \Engelsystem\Controllers\SettingsController::saveLanguage + */ + public function testSaveLanguageWithKnownSelectedLanguageGivenSavesLanguageAndUpdatesSessionAndRedirect() + { + $this->assertEquals('en_US', $this->user->settings->language); + $this->session->set('locale', 'en_US'); + + $this->setExpects($this->auth, 'user', null, $this->user, $this->once()); + $this->response->expects($this->once()) + ->method('redirectTo') + ->with('http://localhost/settings/language') + ->willReturn($this->response); + + $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->assertEquals('de_DE', $this->user->settings->language); + $this->assertEquals('de_DE', $this->session->get('locale')); + } + /** * @covers \Engelsystem\Controllers\SettingsController::__construct * @covers \Engelsystem\Controllers\SettingsController::oauth @@ -377,6 +465,7 @@ class SettingsControllerTest extends TestCase $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()); @@ -385,6 +474,7 @@ class SettingsControllerTest extends TestCase $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' => true] ], $controller->settingsMenu()); @@ -403,6 +493,7 @@ class SettingsControllerTest extends TestCase $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()); } @@ -419,7 +510,11 @@ class SettingsControllerTest extends TestCase 0 => ['name' => 'Engelsystem light'], 1 => ['name' => 'Engelsystem dark'] ]; - $this->config = new Config(['min_password_length' => 6, 'themes' => $themes]); + $languages = [ + 'en_US' => 'English', + 'de_DE' => 'Deutsch' + ]; + $this->config = new Config(['min_password_length' => 6, 'themes' => $themes, 'locales' => $languages]); $this->app->instance('config', $this->config); $this->app->instance(Config::class, $this->config); @@ -437,13 +532,14 @@ class SettingsControllerTest extends TestCase $this->log = new TestLogger(); $this->app->instance(LoggerInterface::class, $this->log); - $this->app->instance('session', new Session(new MockArraySessionStorage())); + $this->session = new Session(new MockArraySessionStorage()); + $this->app->instance('session', $this->session); $this->auth = $this->createMock(Authenticator::class); $this->app->instance(Authenticator::class, $this->auth); $this->user = User::factory() - ->has(Settings::factory(['theme' => 1])) + ->has(Settings::factory(['theme' => 1, 'language' => 'en_US'])) ->create(); } }