added "create user" functionality (#37)
* added "create user" functionality #5 * improved user admin views * added Role constraint and validator
This commit is contained in:
@@ -1,35 +1,12 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
||||||
<file source-language="en" target-language="de" datatype="plaintext" original="file.ext">
|
<file source-language="en" target-language="de" datatype="plaintext" original="not.available">
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="post.blank_summary">
|
<trans-unit id="This value is not a valid role.">
|
||||||
<source>post.blank_summary</source>
|
<source>This value is not a valid role.</source>
|
||||||
<target>Gib deinem Beitrag eine Zusammenfassung!</target>
|
<target>Dieser Wert ist keine gültige Rolle.</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="post.blank_content">
|
|
||||||
<source>post.blank_content</source>
|
|
||||||
<target>Dein Beitrag sollte einen Inhalt haben!</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="post.too_short_content">
|
|
||||||
<source>post.too_short_content</source>
|
|
||||||
<target>Der Beitragsinhalt ist zu kurz (mindestens {{ limit }} Zeichen)</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="comment.blank">
|
|
||||||
<source>comment.blank</source>
|
|
||||||
<target>Bitte gib einen Kommentar ein!</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="comment.too_short">
|
|
||||||
<source>comment.too_short</source>
|
|
||||||
<target>Der Kommentar ist zu kurz (mindestens {{ limit }} Zeichen)</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="comment.too_long">
|
|
||||||
<source>comment.too_long</source>
|
|
||||||
<target>Der Kommentar ist zu lang (maximal {{ limit }} Zeichen)</target>
|
|
||||||
</trans-unit>
|
|
||||||
<trans-unit id="comment.is_spam">
|
|
||||||
<source>comment.is_spam</source>
|
|
||||||
<target>Der Inhalt des Kommentars wird als Spam eingestuft.</target>
|
|
||||||
</trans-unit>
|
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|||||||
@@ -17,17 +17,21 @@
|
|||||||
'label.active': '',
|
'label.active': '',
|
||||||
'label.roles': '',
|
'label.roles': '',
|
||||||
'label.actions': '',
|
'label.actions': '',
|
||||||
}) }}
|
}, null, {'plus-square': path('admin_user_create')}) }}
|
||||||
|
|
||||||
{% for entry in entries %}
|
{% for entry in entries %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="hidden-xs">{{ entry.id }}</td>
|
<td class="hidden-xs">{{ entry.id }}</td>
|
||||||
<td>{{ entry.alias }}</td>
|
<td>{{ entry.alias|default(entry.username) }}</td>
|
||||||
<td class="hidden-xs">{{ entry.username }}</td>
|
<td class="hidden-xs">{{ entry.username }}</td>
|
||||||
<td class="hidden-xs hidden-sm">{{ entry.email }}</td>
|
<td class="hidden-xs hidden-sm">{{ entry.email }}</td>
|
||||||
<td class="hidden-xs">{{ entry.title }}</td>
|
<td class="hidden-xs">{{ entry.title }}</td>
|
||||||
<td>{{ widgets.label_visible(entry.active) }}</td>
|
<td>{{ widgets.label_visible(entry.active) }}</td>
|
||||||
<td>{{ entry.roles|join(',')|trans }}</td>
|
<td>
|
||||||
|
{% for role in entry.roles %}
|
||||||
|
{{ widgets.label_role(role) }}
|
||||||
|
{% endfor %}
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{{ widgets.button_group({
|
{{ widgets.button_group({
|
||||||
'edit': path('user_profile', {'username' : entry.username}),
|
'edit': path('user_profile', {'username' : entry.username}),
|
||||||
|
|||||||
12
app/Resources/views/admin/user_edit.html.twig
Normal file
12
app/Resources/views/admin/user_edit.html.twig
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block page_title %}{{ 'admin_user.title'|trans }}{% endblock %}
|
||||||
|
{% block page_subtitle %}{{ 'admin_user.subtitle'|trans }}{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}
|
||||||
|
{{ include('default/_form.html.twig', {
|
||||||
|
'title': 'create'|trans,
|
||||||
|
'form': form,
|
||||||
|
'back': path('admin_user')
|
||||||
|
}) }}
|
||||||
|
{% endblock %}
|
||||||
@@ -12,6 +12,11 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro label_role(role) %}
|
||||||
|
{% import _self as macro %}
|
||||||
|
{{ macro.label(role, 'primary') }}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro label_project(project) %}
|
{% macro label_project(project) %}
|
||||||
{% import _self as macro %}
|
{% import _self as macro %}
|
||||||
{{ macro.label(project.name, 'primary') }}
|
{{ macro.label(project.name, 'primary') }}
|
||||||
|
|||||||
@@ -11,13 +11,15 @@
|
|||||||
|
|
||||||
namespace AppBundle\Controller\Admin;
|
namespace AppBundle\Controller\Admin;
|
||||||
|
|
||||||
|
use AppBundle\Controller\AbstractController;
|
||||||
use AppBundle\Entity\User;
|
use AppBundle\Entity\User;
|
||||||
|
use AppBundle\Form\UserCreateType;
|
||||||
use Pagerfanta\Pagerfanta;
|
use Pagerfanta\Pagerfanta;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
|
||||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller used to manage users in the admin part of the site.
|
* Controller used to manage users in the admin part of the site.
|
||||||
@@ -27,7 +29,7 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
|
|||||||
*
|
*
|
||||||
* @author Kevin Papst <kevin@kevinpapst.de>
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
*/
|
*/
|
||||||
class UserController extends Controller
|
class UserController extends AbstractController
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @Route("/", defaults={"page": 1}, name="admin_user")
|
* @Route("/", defaults={"page": 1}, name="admin_user")
|
||||||
@@ -42,4 +44,58 @@ class UserController extends Controller
|
|||||||
|
|
||||||
return $this->render('admin/user.html.twig', ['entries' => $entries]);
|
return $this->render('admin/user.html.twig', ['entries' => $entries]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/create", name="admin_user_create")
|
||||||
|
* @Method({"GET", "POST"})
|
||||||
|
*/
|
||||||
|
public function createAction(Request $request)
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$editForm = $this->createEditForm($user);
|
||||||
|
|
||||||
|
$editForm->handleRequest($request);
|
||||||
|
|
||||||
|
if ($editForm->isSubmitted() && $editForm->isValid()) {
|
||||||
|
$password = $this->get('security.password_encoder')
|
||||||
|
->encodePassword($user, $user->getPlainPassword());
|
||||||
|
$user->setPassword($password);
|
||||||
|
$user->setRoles([User::DEFAULT_ROLE]);
|
||||||
|
|
||||||
|
$entityManager = $this->getDoctrine()->getManager();
|
||||||
|
$entityManager->persist($user);
|
||||||
|
$entityManager->flush();
|
||||||
|
|
||||||
|
$this->flashSuccess('action.updated_successfully');
|
||||||
|
|
||||||
|
return $this->redirectToRoute(
|
||||||
|
'user_profile_edit', ['username' => $user->getUsername()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'admin/user_edit.html.twig',
|
||||||
|
[
|
||||||
|
'user' => $user,
|
||||||
|
'form' => $editForm->createView()
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param User $user
|
||||||
|
* @return \Symfony\Component\Form\FormInterface
|
||||||
|
*/
|
||||||
|
private function createEditForm(User $user)
|
||||||
|
{
|
||||||
|
return $this->createForm(
|
||||||
|
UserCreateType::class,
|
||||||
|
$user,
|
||||||
|
[
|
||||||
|
'action' => $this->generateUrl('admin_user_create'),
|
||||||
|
'method' => 'POST'
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,28 @@
|
|||||||
|
|
||||||
namespace AppBundle\Entity;
|
namespace AppBundle\Entity;
|
||||||
|
|
||||||
|
use AppBundle\Validator\Constraints as KimaiAssert;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
use Symfony\Component\Validator\Constraints as Assert;
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User
|
* User
|
||||||
*
|
*
|
||||||
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
|
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
|
||||||
* @ORM\Table(name="users", uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"name"})})
|
* @ORM\Table(name="users", uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"name"}), @ORM\UniqueConstraint(name="mail", columns={"mail"})})
|
||||||
|
* @UniqueEntity("username")
|
||||||
|
* @UniqueEntity("email")
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
*/
|
*/
|
||||||
class User implements UserInterface
|
class User implements UserInterface
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const DEFAULT_ROLE = 'ROLE_USER';
|
||||||
|
const DEFAULT_LANGUAGE = 'de';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int
|
* @var int
|
||||||
*
|
*
|
||||||
@@ -27,7 +36,7 @@ class User implements UserInterface
|
|||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="name", type="string", length=160, nullable=false, unique=true)
|
* @ORM\Column(name="name", type="string", length=60, nullable=false, unique=true)
|
||||||
* @Assert\NotBlank()
|
* @Assert\NotBlank()
|
||||||
* @Assert\Length(min=6, max=160)
|
* @Assert\Length(min=6, max=160)
|
||||||
*/
|
*/
|
||||||
@@ -60,7 +69,8 @@ class User implements UserInterface
|
|||||||
/**
|
/**
|
||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="alias", type="string", length=160, nullable=true)
|
* @ORM\Column(name="alias", type="string", length=60, nullable=true)
|
||||||
|
* @Assert\Length(max=160)
|
||||||
*/
|
*/
|
||||||
private $alias;
|
private $alias;
|
||||||
|
|
||||||
@@ -68,13 +78,15 @@ class User implements UserInterface
|
|||||||
* @var string
|
* @var string
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="language", type="string", length=5, nullable=true)
|
* @ORM\Column(name="language", type="string", length=5, nullable=true)
|
||||||
|
* @Assert\Language()
|
||||||
*/
|
*/
|
||||||
private $language = 'de';
|
private $language = self::DEFAULT_LANGUAGE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var boolean
|
* @var boolean
|
||||||
*
|
*
|
||||||
* @ORM\Column(name="active", type="boolean", nullable=false)
|
* @ORM\Column(name="active", type="boolean", nullable=false)
|
||||||
|
* @Assert\NotNull()
|
||||||
*/
|
*/
|
||||||
private $active = true;
|
private $active = true;
|
||||||
|
|
||||||
@@ -103,6 +115,7 @@ class User implements UserInterface
|
|||||||
* @var string[]
|
* @var string[]
|
||||||
*
|
*
|
||||||
* @ORM\Column(type="json_array")
|
* @ORM\Column(type="json_array")
|
||||||
|
* @KimaiAssert\Role()
|
||||||
*/
|
*/
|
||||||
private $roles = [];
|
private $roles = [];
|
||||||
|
|
||||||
@@ -166,7 +179,7 @@ class User implements UserInterface
|
|||||||
*/
|
*/
|
||||||
public function setActive($active)
|
public function setActive($active)
|
||||||
{
|
{
|
||||||
$this->active = $active;
|
$this->active = (bool) $active;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -305,10 +318,12 @@ class User implements UserInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $language
|
* @param string $language
|
||||||
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setLanguage($language)
|
public function setLanguage($language)
|
||||||
{
|
{
|
||||||
$this->language = $language;
|
$this->language = $language;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
56
src/AppBundle/Form/UserCreateType.php
Normal file
56
src/AppBundle/Form/UserCreateType.php
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\Form;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the form used to create Users.
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class UserCreateType extends UserEditType
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options)
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('username', null, [
|
||||||
|
'label' => 'label.username',
|
||||||
|
'required' => true
|
||||||
|
])
|
||||||
|
->add('plainPassword', RepeatedType::class, [
|
||||||
|
'required' => true,
|
||||||
|
'type' => PasswordType::class,
|
||||||
|
'first_options' => array('label' => 'security.label.password'),
|
||||||
|
'second_options' => array('label' => 'security.label.password_repeat'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
parent::buildForm($builder, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function __configureOptions(OptionsResolver $resolver)
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'class' => 'AppBundle:User',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
src/AppBundle/Validator/Constraints/Role.php
Normal file
31
src/AppBundle/Validator/Constraints/Role.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Annotation
|
||||||
|
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
|
||||||
|
*
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class Role extends Constraint
|
||||||
|
{
|
||||||
|
const ROLE_ERROR = 'xd5hffg-dsfef3-426a-83d7-1f2d33hs5d84';
|
||||||
|
|
||||||
|
protected static $errorNames = array(
|
||||||
|
self::ROLE_ERROR => 'ROLE_ERROR',
|
||||||
|
);
|
||||||
|
|
||||||
|
public $message = 'This value is not a valid role.';
|
||||||
|
}
|
||||||
50
src/AppBundle/Validator/Constraints/RoleValidator.php
Normal file
50
src/AppBundle/Validator/Constraints/RoleValidator.php
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of the Kimai package.
|
||||||
|
*
|
||||||
|
* (c) Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace AppBundle\Validator\Constraints;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Kevin Papst <kevin@kevinpapst.de>
|
||||||
|
*/
|
||||||
|
class RoleValidator extends ConstraintValidator
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $allowedRoles = ['ROLE_CUSTOMER', 'ROLE_USER', 'ROLE_TEAMLEAD', 'ROLE_ADMIN', 'ROLE_SUPER_ADMIN'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function validate($value, Constraint $constraint)
|
||||||
|
{
|
||||||
|
if (!$constraint instanceof Role) {
|
||||||
|
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Role');
|
||||||
|
}
|
||||||
|
|
||||||
|
$roles = $value;
|
||||||
|
|
||||||
|
if (!is_array($roles)) {
|
||||||
|
$roles = [$roles];
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($roles as $role) {
|
||||||
|
if (!is_string($role) || !in_array($role, $this->allowedRoles)) {
|
||||||
|
$this->context->buildViolation($constraint->message)
|
||||||
|
->setParameter('{{ value }}', $this->formatValue($role))
|
||||||
|
->setCode(Role::ROLE_ERROR)
|
||||||
|
->addViolation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user