diff --git a/resources/views/layouts/parts/messages.twig b/resources/views/layouts/parts/messages.twig new file mode 100644 index 00000000..b6f03aa4 --- /dev/null +++ b/resources/views/layouts/parts/messages.twig @@ -0,0 +1,19 @@ +{% import 'macros/base.twig' as m %} + +{{ msg() }} + +{% for message in errors|default([]) %} + {{ m.alert(__(message), 'danger') }} +{% endfor %} + +{% for message in warnings|default([]) %} + {{ m.alert(__(message), 'warning') }} +{% endfor %} + +{% for message in information|default([]) %} + {{ m.alert(__(message), 'info') }} +{% endfor %} + +{% for message in messages|default([]) %} + {{ m.alert(__(message), 'success') }} +{% endfor %} diff --git a/resources/views/pages/login.twig b/resources/views/pages/login.twig index fdb5b116..a6fb0934 100644 --- a/resources/views/pages/login.twig +++ b/resources/views/pages/login.twig @@ -32,10 +32,7 @@
- {{ msg() }} - {% for message in errors|default([]) %} - {{ m.alert(__(message), 'danger') }} - {% endfor %} + {% include 'layouts/parts/messages.twig' %}
{{ csrf() }} diff --git a/resources/views/pages/password/reset.twig b/resources/views/pages/password/reset.twig index b1de4eae..b3fba53e 100644 --- a/resources/views/pages/password/reset.twig +++ b/resources/views/pages/password/reset.twig @@ -8,9 +8,7 @@

{{ __('Password recovery') }}

- {% for message in errors|default([]) %} - {{ m.alert(__(message), 'danger') }} - {% endfor %} + {% include 'layouts/parts/messages.twig' %}
{% block row_content %} diff --git a/src/Controllers/AuthController.php b/src/Controllers/AuthController.php index 9fc46f2e..dc4d1594 100644 --- a/src/Controllers/AuthController.php +++ b/src/Controllers/AuthController.php @@ -9,12 +9,12 @@ use Engelsystem\Http\Request; use Engelsystem\Http\Response; use Engelsystem\Http\UrlGeneratorInterface; use Engelsystem\Models\User\User; -use Illuminate\Support\Arr; -use Illuminate\Support\Collection; use Symfony\Component\HttpFoundation\Session\SessionInterface; class AuthController extends BaseController { + use HasUserNotifications; + /** @var Response */ protected $response; @@ -70,12 +70,9 @@ class AuthController extends BaseController */ protected function showLogin(): Response { - $errors = Collection::make(Arr::flatten($this->session->get('errors', []))); - $this->session->remove('errors'); - return $this->response->withView( 'pages/login', - ['errors' => $errors] + $this->getNotifications() ); } @@ -95,7 +92,7 @@ class AuthController extends BaseController $user = $this->auth->authenticate($data['login'], $data['password']); if (!$user instanceof User) { - $this->session->set('errors', array_merge($this->session->get('errors', []), ['auth.not-found'])); + $this->addNotification('auth.not-found', 'errors'); return $this->showLogin(); } diff --git a/src/Controllers/HasUserNotifications.php b/src/Controllers/HasUserNotifications.php new file mode 100644 index 00000000..c5a7d97d --- /dev/null +++ b/src/Controllers/HasUserNotifications.php @@ -0,0 +1,35 @@ +set( + $type, + array_merge(session()->get($type, []), [$value]) + ); + } + + /** + * @return array + */ + protected function getNotifications(): array + { + $return = []; + foreach (['errors', 'warnings', 'information', 'messages'] as $type) { + $return[$type] = Collection::make(Arr::flatten(session()->get($type, []))); + session()->remove($type); + } + + return $return; + } +} diff --git a/src/Controllers/PasswordResetController.php b/src/Controllers/PasswordResetController.php index 6ceadec1..7d1349e1 100644 --- a/src/Controllers/PasswordResetController.php +++ b/src/Controllers/PasswordResetController.php @@ -8,13 +8,13 @@ use Engelsystem\Http\Response; use Engelsystem\Mail\EngelsystemMailer; use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\User; -use Illuminate\Support\Arr; -use Illuminate\Support\Collection; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; class PasswordResetController extends BaseController { + use HasUserNotifications; + /** @var LoggerInterface */ protected $log; @@ -120,10 +120,7 @@ class PasswordResetController extends BaseController ]); if ($data['password'] !== $data['password_confirmation']) { - $this->session->set( - 'errors', - array_merge($this->session->get('errors', []), ['validation.password.confirmed']) - ); + $this->addNotification('validation.password.confirmed', 'errors'); return $this->showView('pages/password/reset-form'); } @@ -141,12 +138,9 @@ class PasswordResetController extends BaseController */ protected function showView($view = 'pages/password/reset', $data = []): Response { - $errors = Collection::make(Arr::flatten($this->session->get('errors', []))); - $this->session->remove('errors'); - return $this->response->withView( $view, - array_merge_recursive(['errors' => $errors], $data) + array_merge_recursive($this->getNotifications(), $data) ); } diff --git a/tests/Unit/Controllers/AuthControllerTest.php b/tests/Unit/Controllers/AuthControllerTest.php index aa1194a2..8dfb0a64 100644 --- a/tests/Unit/Controllers/AuthControllerTest.php +++ b/tests/Unit/Controllers/AuthControllerTest.php @@ -2,6 +2,7 @@ namespace Engelsystem\Test\Unit\Controllers; +use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Engelsystem\Config\Config; use Engelsystem\Controllers\AuthController; use Engelsystem\Helpers\Authenticator; @@ -21,6 +22,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; class AuthControllerTest extends TestCase { + use ArraySubsetAsserts; use HasDatabase; /** @@ -38,10 +40,11 @@ class AuthControllerTest extends TestCase /** @var Authenticator|MockObject $auth */ list(, $session, $url, $config, $auth) = $this->getMocks(); - $session->expects($this->once()) + $session->expects($this->atLeastOnce()) ->method('get') - ->with('errors', []) - ->willReturn(['foo' => 'bar']); + ->willReturnCallback(function ($type) { + return $type == 'errors' ? ['foo' => 'bar'] : []; + }); $response->expects($this->once()) ->method('withView') ->with('pages/login') @@ -69,6 +72,7 @@ class AuthControllerTest extends TestCase /** @var Validator|MockObject $validator */ $validator = new Validator(); $session->set('errors', [['bar' => 'some.bar.error']]); + $this->app->instance('session', $session); $user = new User([ 'name' => 'foo', @@ -92,8 +96,11 @@ class AuthControllerTest extends TestCase $response->expects($this->once()) ->method('withView') - ->with('pages/login', ['errors' => collect(['some.bar.error', 'auth.not-found'])]) - ->willReturn($response); + ->willReturnCallback(function ($view, $data = []) use ($response) { + $this->assertEquals('pages/login', $view); + $this->assertArraySubset(['errors' => collect(['some.bar.error', 'auth.not-found'])], $data); + return $response; + }); $response->expects($this->once()) ->method('redirectTo') ->with('news') @@ -168,6 +175,8 @@ class AuthControllerTest extends TestCase /** @var Authenticator|MockObject $auth */ $auth = $this->createMock(Authenticator::class); + $this->app->instance('session', $session); + return [$response, $session, $url, $config, $auth]; } } diff --git a/tests/Unit/Controllers/HasUserNotificationsTest.php b/tests/Unit/Controllers/HasUserNotificationsTest.php new file mode 100644 index 00000000..9f9e9d25 --- /dev/null +++ b/tests/Unit/Controllers/HasUserNotificationsTest.php @@ -0,0 +1,35 @@ +app->instance('session', $session); + + $notify = new HasUserNotificationsImplementation(); + $notify->add('Foo', 'errors'); + $notify->add('Bar', 'warnings'); + $notify->add(['Baz', 'Lorem'], 'information'); + $notify->add(['Hm', ['Uff', 'sum']], 'messages'); + + $this->assertEquals([ + 'errors' => new Collection(['Foo']), + 'warnings' => new Collection(['Bar']), + 'information' => new Collection(['Baz', 'Lorem']), + 'messages' => new Collection(['Hm', 'Uff', 'sum']), + ], $notify->get()); + } +} diff --git a/tests/Unit/Controllers/PasswordResetControllerTest.php b/tests/Unit/Controllers/PasswordResetControllerTest.php index c83b06b9..f237ff66 100644 --- a/tests/Unit/Controllers/PasswordResetControllerTest.php +++ b/tests/Unit/Controllers/PasswordResetControllerTest.php @@ -2,6 +2,7 @@ namespace Engelsystem\Test\Unit\Controllers; +use DMS\PHPUnitExtensions\ArraySubset\ArraySubsetAsserts; use Engelsystem\Config\Config; use Engelsystem\Controllers\PasswordResetController; use Engelsystem\Helpers\Authenticator; @@ -23,6 +24,7 @@ use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage; class PasswordResetControllerTest extends TestCase { + use ArraySubsetAsserts; use HasDatabase; /** @var array */ @@ -199,6 +201,8 @@ class PasswordResetControllerTest extends TestCase $renderer = $this->createMock(Renderer::class); $response->setRenderer($renderer); + $this->app->instance('session', $session); + return $this->args = [ 'response' => $response, 'session' => $session, @@ -230,7 +234,16 @@ class PasswordResetControllerTest extends TestCase $args[] = $data; } - $this->setExpects($renderer, 'render', $args, 'Foo'); + $renderer->expects($this->atLeastOnce()) + ->method('render') + ->willReturnCallback(function ($template, $data = []) use ($args) { + $this->assertEquals($args[0], $template); + if (isset($args[1])) { + $this->assertArraySubset($args[1], $data); + } + + return 'Foo'; + }); } return $controller; diff --git a/tests/Unit/Controllers/Stub/HasUserNotificationsImplementation.php b/tests/Unit/Controllers/Stub/HasUserNotificationsImplementation.php new file mode 100644 index 00000000..1e71b9db --- /dev/null +++ b/tests/Unit/Controllers/Stub/HasUserNotificationsImplementation.php @@ -0,0 +1,27 @@ +addNotification($value, $type); + } + + /** + * @return array + */ + public function get(): array + { + return $this->getNotifications(); + } +}