Added shirt edit view
This commit is contained in:
parent
5667fc2326
commit
5c90a1ef37
|
@ -96,6 +96,16 @@ $route->addGroup(
|
|||
}
|
||||
);
|
||||
|
||||
// User
|
||||
$route->addGroup(
|
||||
'/user/{id:\d+}',
|
||||
// Shirts
|
||||
function (RouteCollector $route) {
|
||||
$route->get('/shirt', 'Admin\\UserShirtController@editShirt');
|
||||
$route->post('/shirt', 'Admin\\UserShirtController@saveShirt');
|
||||
}
|
||||
);
|
||||
|
||||
// News
|
||||
$route->addGroup(
|
||||
'/news',
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Migrations;
|
||||
|
||||
use Engelsystem\Database\Migration\Migration;
|
||||
|
||||
class AddShirtEditPermissions extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migration
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
if (!$this->schema->hasTable('GroupPrivileges')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->schema->getConnection();
|
||||
$db->table('Privileges')
|
||||
->insert(['name' => 'user.edit.shirt', 'desc' => 'Edit user shirts']);
|
||||
|
||||
$shiftCoordinator = -40;
|
||||
$shirtManager = -30;
|
||||
|
||||
$userEditShirt = $db->table('Privileges')
|
||||
->where('name', 'user.edit.shirt')
|
||||
->get(['id'])->first();
|
||||
$adminArrive = $db->table('Privileges')
|
||||
->where('name', 'admin_arrive')
|
||||
->get(['id'])->first();
|
||||
|
||||
$db->table('GroupPrivileges')
|
||||
->insertOrIgnore([
|
||||
['group_id' => $shiftCoordinator, 'privilege_id' => $userEditShirt->id],
|
||||
['group_id' => $shirtManager, 'privilege_id' => $userEditShirt->id],
|
||||
['group_id' => $shirtManager, 'privilege_id' => $adminArrive->id],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migration
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
if (!$this->schema->hasTable('GroupPrivileges')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$db = $this->schema->getConnection();
|
||||
$db->table('Privileges')
|
||||
->where(['name' => 'user.edit.shirt'])
|
||||
->delete();
|
||||
|
||||
$shirtManager = -30;
|
||||
$adminArrive = $db->table('Privileges')
|
||||
->where('name', 'admin_arrive')
|
||||
->get(['id'])->first();
|
||||
|
||||
$db->table('GroupPrivileges')
|
||||
->where(['group_id' => $shirtManager, 'privilege_id' => $adminArrive->id])
|
||||
->delete();
|
||||
}
|
||||
}
|
|
@ -272,6 +272,8 @@ function admin_active()
|
|||
);
|
||||
}
|
||||
|
||||
$actions[] = button(url('/admin/user/' . $usr->id . '/shirt'), __('form.edit'), 'btn-primary btn-sm');
|
||||
|
||||
$userData['actions'] = buttons($actions);
|
||||
|
||||
$matched_users[] = $userData;
|
||||
|
|
|
@ -618,6 +618,10 @@ function User_view(
|
|||
div('row', [
|
||||
div('col-md-12', [
|
||||
buttons([
|
||||
$auth->can('user.edit.shirt') ? button(
|
||||
url('/admin/user/' . $user_source->id . '/shirt'),
|
||||
icon('person') . __('Shirt')
|
||||
) : '',
|
||||
$admin_user_privilege ? button(
|
||||
page_link_to('admin_user', ['id' => $user_source->id]),
|
||||
icon('pencil-square') . __('edit')
|
||||
|
|
|
@ -138,3 +138,6 @@ msgstr "Es gibt eine neue News: %1$s"
|
|||
|
||||
msgid "notification.news.new.text"
|
||||
msgstr "Du kannst sie dir unter %3$s anschauen."
|
||||
|
||||
msgid "user.edit.success"
|
||||
msgstr "Benutzer erfolgreich bearbeitet."
|
||||
|
|
|
@ -2909,7 +2909,6 @@ msgstr "Level"
|
|||
msgid "log.message"
|
||||
msgstr "Nachricht"
|
||||
|
||||
|
||||
msgid "settings.settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
|
@ -2978,3 +2977,24 @@ msgstr "Frage"
|
|||
|
||||
msgid "question.answer"
|
||||
msgstr "Antwort"
|
||||
|
||||
msgid "user.edit.shirt"
|
||||
msgstr "Shirt bearbeiten"
|
||||
|
||||
msgid "form.shirt"
|
||||
msgstr "Shirt"
|
||||
|
||||
msgid "user.shirt_size"
|
||||
msgstr "Shirt größe"
|
||||
|
||||
msgid "user.active"
|
||||
msgstr "Aktiv"
|
||||
|
||||
msgid "user.force_active"
|
||||
msgstr "Aktiv (erzwungen)"
|
||||
|
||||
msgid "user.arrived"
|
||||
msgstr "Angekommen"
|
||||
|
||||
msgid "user.got_shirt"
|
||||
msgstr "Shirt bekommen"
|
||||
|
|
|
@ -134,3 +134,6 @@ msgstr "A new news is available: %1$s"
|
|||
|
||||
msgid "notification.news.new.text"
|
||||
msgstr "You can watch it at %3$s"
|
||||
|
||||
msgid "user.edit.success"
|
||||
msgstr "User edited successfully."
|
||||
|
|
|
@ -260,3 +260,24 @@ msgstr "Question"
|
|||
|
||||
msgid "question.answer"
|
||||
msgstr "Answer"
|
||||
|
||||
msgid "user.edit.shirt"
|
||||
msgstr "Edit shirt"
|
||||
|
||||
msgid "form.shirt"
|
||||
msgstr "Shirt"
|
||||
|
||||
msgid "user.shirt_size"
|
||||
msgstr "Shirt size"
|
||||
|
||||
msgid "user.active"
|
||||
msgstr "Active"
|
||||
|
||||
msgid "user.force_active"
|
||||
msgstr "Active (forced)"
|
||||
|
||||
msgid "user.arrived"
|
||||
msgstr "Arrived"
|
||||
|
||||
msgid "user.got_shirt"
|
||||
msgstr "Got shirt"
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{% extends "layouts/app.twig" %}
|
||||
{% import 'macros/base.twig' as m %}
|
||||
{% import 'macros/form.twig' as f %}
|
||||
|
||||
{% block title %}{{ __('user.edit.shirt') }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{{ block('title') }}</h1>
|
||||
|
||||
{% include 'layouts/parts/messages.twig' %}
|
||||
|
||||
<form method="post">
|
||||
{{ csrf() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
{{ f.select('shirt_size', config('tshirt_sizes'), __('user.shirt_size'), userdata.personalData.shirt_size) }}
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
{% if has_permission_to('admin_arrive') %}
|
||||
{{ f.switch('arrived', __('user.arrived'), userdata.state.arrived) }}
|
||||
{% endif %}
|
||||
|
||||
{% if userdata.state.force_active %}
|
||||
{{ f.switch('force_active', __('user.force_active'), true, {'disabled': true}) }}
|
||||
{% endif %}
|
||||
|
||||
{{ f.switch('active', __('user.active'), userdata.state.active) }}
|
||||
|
||||
{{ f.switch('got_shirt', __('user.got_shirt'), userdata.state.got_shirt) }}
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
{{ f.submit(__('form.save')) }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -44,17 +44,18 @@
|
|||
{% endif %}
|
||||
<select id="{{ name }}" name="{{ name }}" class="form-control">
|
||||
{% for value,decription in data -%}
|
||||
<option value="{{ value }}" {% if value == selected %} selected{% endif %}>{{ decription }}</option>
|
||||
<option value="{{ value }}"{% if value == selected %} selected{% endif %}>{{ decription }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro checkbox(name, label, checked, value) %}
|
||||
<div class="checkbox">
|
||||
{% macro checkbox(name, label, checked, value, disabled) %}
|
||||
<div class="form-check">
|
||||
<label>
|
||||
<input type="checkbox" id="{{ name }}" name="{{ name }}" value="{{ value|default('1') }}"
|
||||
<input type="checkbox" id="{{ name }}" name="{{ name }}" value="{{ value|default('1') }}" class="form-check-input"
|
||||
{%- if checked|default(false) %} checked{% endif %}
|
||||
{%- if disabled|default(false) %} disabled{% endif %}
|
||||
>
|
||||
{{ label }}
|
||||
</label>
|
||||
|
@ -81,3 +82,13 @@
|
|||
{% macro submit(label, opt) %}
|
||||
{{ _self.button(label|default(__('form.submit')), {'type': 'submit', 'btn_type': 'primary'}|merge(opt|default({}))) }}
|
||||
{%- endmacro %}
|
||||
|
||||
{% macro switch(name, label, checked, opt) %}
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="{{ name }}" name="{{ name }}" value="{{ opt.value|default('1') }}"
|
||||
{%- if checked|default(false) %} checked{% endif %}
|
||||
{%- if opt.disabled|default(false) %} disabled{% endif %}
|
||||
>
|
||||
<label class="form-check-label" for="{{ name }}">{{ label }}</label>
|
||||
</div>
|
||||
{%- endmacro %}
|
||||
|
|
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Controllers\Admin;
|
||||
|
||||
use Engelsystem\Config\Config;
|
||||
use Engelsystem\Controllers\BaseController;
|
||||
use Engelsystem\Controllers\HasUserNotifications;
|
||||
use Engelsystem\Helpers\Authenticator;
|
||||
use Engelsystem\Http\Redirector;
|
||||
use Engelsystem\Http\Request;
|
||||
use Engelsystem\Http\Response;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class UserShirtController extends BaseController
|
||||
{
|
||||
use HasUserNotifications;
|
||||
|
||||
/** @var Authenticator */
|
||||
protected $auth;
|
||||
|
||||
/** @var Config */
|
||||
protected $config;
|
||||
|
||||
/** @var LoggerInterface */
|
||||
protected $log;
|
||||
|
||||
/** @var Redirector */
|
||||
protected $redirect;
|
||||
|
||||
/** @var Response */
|
||||
protected $response;
|
||||
|
||||
/** @var User */
|
||||
protected $user;
|
||||
|
||||
/** @var array */
|
||||
protected $permissions = [
|
||||
'editShirt' => 'user.edit.shirt',
|
||||
'saveShirt' => 'user.edit.shirt',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param Authenticator $auth
|
||||
* @param Config $config
|
||||
* @param LoggerInterface $log
|
||||
* @param Redirector $redirector
|
||||
* @param Response $response
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(
|
||||
Authenticator $auth,
|
||||
Config $config,
|
||||
LoggerInterface $log,
|
||||
Redirector $redirector,
|
||||
Response $response,
|
||||
User $user
|
||||
) {
|
||||
$this->auth = $auth;
|
||||
$this->config = $config;
|
||||
$this->log = $log;
|
||||
$this->redirect = $redirector;
|
||||
$this->response = $response;
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function editShirt(Request $request): Response
|
||||
{
|
||||
$id = $request->getAttribute('id');
|
||||
$user = $this->user->findOrFail($id);
|
||||
|
||||
return $this->response->withView(
|
||||
'admin/user/edit-shirt.twig',
|
||||
['userdata' => $user] + $this->getNotifications()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function saveShirt(Request $request): Response
|
||||
{
|
||||
$id = $request->getAttribute('id');
|
||||
/** @var User $user */
|
||||
$user = $this->user->findOrFail($id);
|
||||
|
||||
$data = $this->validate($request, [
|
||||
'shirt_size' => 'required',
|
||||
'arrived' => 'optional|checked',
|
||||
'active' => 'optional|checked',
|
||||
'got_shirt' => 'optional|checked',
|
||||
]);
|
||||
|
||||
if (isset($this->config->get('tshirt_sizes')[$data['shirt_size']])) {
|
||||
$user->personalData->shirt_size = $data['shirt_size'];
|
||||
$user->personalData->save();
|
||||
}
|
||||
|
||||
if ($this->auth->can('admin_arrive')) {
|
||||
$user->state->arrived = (bool)$data['arrived'];
|
||||
}
|
||||
|
||||
$user->state->active = (bool)$data['active'];
|
||||
$user->state->got_shirt = (bool)$data['got_shirt'];
|
||||
$user->state->save();
|
||||
|
||||
$this->log->info(
|
||||
'Updated user shirt state "{user}" ({id}): {size}, arrived: {arrived}, got shirt: {got_shirt}',
|
||||
[
|
||||
'id' => $user->id,
|
||||
'user' => $user->name,
|
||||
'size' => $user->personalData->shirt_size,
|
||||
'arrived' => $user->state->arrived,
|
||||
'got_shirt' => $user->state->got_shirt
|
||||
]
|
||||
);
|
||||
|
||||
$this->addNotification('user.edit.success');
|
||||
|
||||
return $this->redirect->back();
|
||||
}
|
||||
}
|
|
@ -33,6 +33,15 @@ class State extends HasUserModel
|
|||
'arrival_date',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
protected $casts = [
|
||||
'arrived' => 'boolean',
|
||||
'active' => 'boolean',
|
||||
'force_active' => 'boolean',
|
||||
'got_shirt' => 'boolean',
|
||||
'got_voucher' => 'integer',
|
||||
];
|
||||
|
||||
/** The attributes that are mass assignable */
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
<?php
|
||||
|
||||
namespace Engelsystem\Test\Unit\Controllers\Admin;
|
||||
|
||||
use Engelsystem\Controllers\Admin\UserShirtController;
|
||||
use Engelsystem\Helpers\Authenticator;
|
||||
use Engelsystem\Http\Redirector;
|
||||
use Engelsystem\Http\Validation\Validator;
|
||||
use Engelsystem\Models\User\PersonalData;
|
||||
use Engelsystem\Models\User\State;
|
||||
use Engelsystem\Models\User\User;
|
||||
use Engelsystem\Test\Unit\Controllers\ControllerTest;
|
||||
use Engelsystem\Test\Unit\HasDatabase;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
class UserShirtControllerTest extends ControllerTest
|
||||
{
|
||||
use HasDatabase;
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Admin\UserShirtController::editShirt
|
||||
* @covers \Engelsystem\Controllers\Admin\UserShirtController::__construct
|
||||
*/
|
||||
public function testIndex()
|
||||
{
|
||||
$request = $this->request->withAttribute('id', 1);
|
||||
/** @var Authenticator|MockObject $auth */
|
||||
$auth = $this->createMock(Authenticator::class);
|
||||
/** @var Redirector|MockObject $redirector */
|
||||
$redirector = $this->createMock(Redirector::class);
|
||||
$user = new User();
|
||||
User::factory()->create();
|
||||
|
||||
$this->setExpects($this->response, 'withView', ['admin/user/edit-shirt.twig'], $this->response);
|
||||
|
||||
$controller = new UserShirtController($auth, $this->config, $this->log, $redirector, $this->response, $user);
|
||||
|
||||
$controller->editShirt($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Admin\UserShirtController::editShirt
|
||||
*/
|
||||
public function testIndexUserNotFound()
|
||||
{
|
||||
/** @var Authenticator|MockObject $auth */
|
||||
$auth = $this->createMock(Authenticator::class);
|
||||
/** @var Redirector|MockObject $redirector */
|
||||
$redirector = $this->createMock(Redirector::class);
|
||||
$user = new User();
|
||||
|
||||
$controller = new UserShirtController($auth, $this->config, $this->log, $redirector, $this->response, $user);
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
$controller->editShirt($this->request);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Admin\UserShirtController::saveShirt
|
||||
*/
|
||||
public function testSaveShirt()
|
||||
{
|
||||
$request = $this->request
|
||||
->withAttribute('id', 1)
|
||||
->withParsedBody([
|
||||
'shirt_size' => 'S',
|
||||
]);
|
||||
/** @var Authenticator|MockObject $auth */
|
||||
$auth = $this->createMock(Authenticator::class);
|
||||
$this->config->set('tshirt_sizes', ['S' => 'Small']);
|
||||
/** @var Redirector|MockObject $redirector */
|
||||
$redirector = $this->createMock(Redirector::class);
|
||||
User::factory()
|
||||
->has(State::factory())
|
||||
->has(PersonalData::factory())
|
||||
->create();
|
||||
|
||||
$auth
|
||||
->expects($this->exactly(4))
|
||||
->method('can')
|
||||
->with('admin_arrive')
|
||||
->willReturnOnConsecutiveCalls(true, true, true, false);
|
||||
$this->setExpects($redirector, 'back', null, $this->response, $this->exactly(4));
|
||||
|
||||
$controller = new UserShirtController(
|
||||
$auth,
|
||||
$this->config,
|
||||
$this->log,
|
||||
$redirector,
|
||||
$this->response,
|
||||
new User()
|
||||
);
|
||||
$controller->setValidator(new Validator());
|
||||
|
||||
// Set shirt size
|
||||
$controller->saveShirt($request);
|
||||
|
||||
$this->assertHasNotification('user.edit.success');
|
||||
$this->assertTrue($this->log->hasInfoThatContains('Updated user shirt state'));
|
||||
|
||||
$user = User::find(1);
|
||||
$this->assertEquals('S', $user->personalData->shirt_size);
|
||||
$this->assertFalse($user->state->arrived);
|
||||
$this->assertFalse($user->state->active);
|
||||
$this->assertFalse($user->state->got_shirt);
|
||||
|
||||
// Set active, arrived and got_shirt
|
||||
$request = $request
|
||||
->withParsedBody([
|
||||
'shirt_size' => 'S',
|
||||
'arrived' => '1',
|
||||
'active' => '1',
|
||||
'got_shirt' => '1',
|
||||
]);
|
||||
|
||||
$controller->saveShirt($request);
|
||||
|
||||
$user = User::find(1);
|
||||
$this->assertTrue($user->state->active);
|
||||
$this->assertTrue($user->state->arrived);
|
||||
$this->assertTrue($user->state->got_shirt);
|
||||
|
||||
// Shirt size not available
|
||||
$request = $request
|
||||
->withParsedBody([
|
||||
'shirt_size' => 'L',
|
||||
]);
|
||||
|
||||
$controller->saveShirt($request);
|
||||
$user = User::find(1);
|
||||
$this->assertEquals('S', $user->personalData->shirt_size);
|
||||
|
||||
// Not allowed changing arrived
|
||||
$request = $request
|
||||
->withParsedBody([
|
||||
'shirt_size' => 'S',
|
||||
'arrived' => '1',
|
||||
]);
|
||||
|
||||
$this->assertFalse($user->state->arrived);
|
||||
$controller->saveShirt($request);
|
||||
$user = User::find(1);
|
||||
$this->assertFalse($user->state->arrived);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers \Engelsystem\Controllers\Admin\UserShirtController::saveShirt
|
||||
*/
|
||||
public function testSaveShirtUserNotFound()
|
||||
{
|
||||
/** @var Authenticator|MockObject $auth */
|
||||
$auth = $this->createMock(Authenticator::class);
|
||||
/** @var Redirector|MockObject $redirector */
|
||||
$redirector = $this->createMock(Redirector::class);
|
||||
$user = new User();
|
||||
|
||||
$controller = new UserShirtController($auth, $this->config, $this->log, $redirector, $this->response, $user);
|
||||
|
||||
$this->expectException(ModelNotFoundException::class);
|
||||
$controller->editShirt($this->request);
|
||||
}
|
||||
}
|
|
@ -53,6 +53,7 @@ trait HasDatabase
|
|||
['migration' => '2020_04_07_000000_change_mysql_database_encoding_to_utf8mb4'],
|
||||
['migration' => '2020_09_12_000000_create_welcome_angel_permissions_group'],
|
||||
['migration' => '2020_12_28_000000_oauth_set_identifier_binary'],
|
||||
['migration' => '2021_08_26_000000_add_shirt_edit_permissions'],
|
||||
]
|
||||
);
|
||||
|
||||
|
|
Loading…
Reference in New Issue