diff --git a/app/Resources/translations/validators.de.xliff b/app/Resources/translations/validators.de.xliff
index 6acda37c..9b943504 100644
--- a/app/Resources/translations/validators.de.xliff
+++ b/app/Resources/translations/validators.de.xliff
@@ -1,35 +1,12 @@
-
+
-
- post.blank_summary
- Gib deinem Beitrag eine Zusammenfassung!
-
-
- post.blank_content
- Dein Beitrag sollte einen Inhalt haben!
-
-
- post.too_short_content
- Der Beitragsinhalt ist zu kurz (mindestens {{ limit }} Zeichen)
-
-
- comment.blank
- Bitte gib einen Kommentar ein!
-
-
- comment.too_short
- Der Kommentar ist zu kurz (mindestens {{ limit }} Zeichen)
-
-
- comment.too_long
- Der Kommentar ist zu lang (maximal {{ limit }} Zeichen)
-
-
- comment.is_spam
- Der Inhalt des Kommentars wird als Spam eingestuft.
-
+
+ This value is not a valid role.
+ Dieser Wert ist keine gültige Rolle.
+
+
diff --git a/app/Resources/views/admin/user.html.twig b/app/Resources/views/admin/user.html.twig
index 31a3a081..468ca3ac 100644
--- a/app/Resources/views/admin/user.html.twig
+++ b/app/Resources/views/admin/user.html.twig
@@ -17,17 +17,21 @@
'label.active': '',
'label.roles': '',
'label.actions': '',
- }) }}
+ }, null, {'plus-square': path('admin_user_create')}) }}
{% for entry in entries %}
{{ entry.id }}
-
{{ entry.alias }}
+
{{ entry.alias|default(entry.username) }}
{{ entry.username }}
{{ entry.email }}
{{ entry.title }}
{{ widgets.label_visible(entry.active) }}
-
{{ entry.roles|join(',')|trans }}
+
+ {% for role in entry.roles %}
+ {{ widgets.label_role(role) }}
+ {% endfor %}
+
{{ widgets.button_group({
'edit': path('user_profile', {'username' : entry.username}),
diff --git a/app/Resources/views/admin/user_edit.html.twig b/app/Resources/views/admin/user_edit.html.twig
new file mode 100644
index 00000000..5eadabc2
--- /dev/null
+++ b/app/Resources/views/admin/user_edit.html.twig
@@ -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 %}
diff --git a/app/Resources/views/macros/widgets.html.twig b/app/Resources/views/macros/widgets.html.twig
index a881e756..c9d46355 100644
--- a/app/Resources/views/macros/widgets.html.twig
+++ b/app/Resources/views/macros/widgets.html.twig
@@ -12,6 +12,11 @@
{% endif %}
{% endmacro %}
+{% macro label_role(role) %}
+ {% import _self as macro %}
+ {{ macro.label(role, 'primary') }}
+{% endmacro %}
+
{% macro label_project(project) %}
{% import _self as macro %}
{{ macro.label(project.name, 'primary') }}
diff --git a/src/AppBundle/Controller/Admin/UserController.php b/src/AppBundle/Controller/Admin/UserController.php
index 5a6b9f01..0e94d10c 100644
--- a/src/AppBundle/Controller/Admin/UserController.php
+++ b/src/AppBundle/Controller/Admin/UserController.php
@@ -11,13 +11,15 @@
namespace AppBundle\Controller\Admin;
+use AppBundle\Controller\AbstractController;
use AppBundle\Entity\User;
+use AppBundle\Form\UserCreateType;
use Pagerfanta\Pagerfanta;
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
+use Symfony\Component\HttpFoundation\Request;
/**
* 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
*/
-class UserController extends Controller
+class UserController extends AbstractController
{
/**
* @Route("/", defaults={"page": 1}, name="admin_user")
@@ -42,4 +44,58 @@ class UserController extends Controller
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'
+ ]
+ );
+ }
}
diff --git a/src/AppBundle/Entity/User.php b/src/AppBundle/Entity/User.php
index 110334dc..028441cf 100644
--- a/src/AppBundle/Entity/User.php
+++ b/src/AppBundle/Entity/User.php
@@ -2,19 +2,28 @@
namespace AppBundle\Entity;
+use AppBundle\Validator\Constraints as KimaiAssert;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
+use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* User
*
* @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
*/
class User implements UserInterface
{
+ const DEFAULT_ROLE = 'ROLE_USER';
+ const DEFAULT_LANGUAGE = 'de';
+
/**
* @var int
*
@@ -27,7 +36,7 @@ class User implements UserInterface
/**
* @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\Length(min=6, max=160)
*/
@@ -60,7 +69,8 @@ class User implements UserInterface
/**
* @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;
@@ -68,13 +78,15 @@ class User implements UserInterface
* @var string
*
* @ORM\Column(name="language", type="string", length=5, nullable=true)
+ * @Assert\Language()
*/
- private $language = 'de';
+ private $language = self::DEFAULT_LANGUAGE;
/**
* @var boolean
*
* @ORM\Column(name="active", type="boolean", nullable=false)
+ * @Assert\NotNull()
*/
private $active = true;
@@ -103,6 +115,7 @@ class User implements UserInterface
* @var string[]
*
* @ORM\Column(type="json_array")
+ * @KimaiAssert\Role()
*/
private $roles = [];
@@ -166,7 +179,7 @@ class User implements UserInterface
*/
public function setActive($active)
{
- $this->active = $active;
+ $this->active = (bool) $active;
return $this;
}
@@ -305,10 +318,12 @@ class User implements UserInterface
/**
* @param string $language
+ * @return $this
*/
public function setLanguage($language)
{
$this->language = $language;
+ return $this;
}
/**
diff --git a/src/AppBundle/Form/UserCreateType.php b/src/AppBundle/Form/UserCreateType.php
new file mode 100644
index 00000000..236a58f9
--- /dev/null
+++ b/src/AppBundle/Form/UserCreateType.php
@@ -0,0 +1,56 @@
+
+ *
+ * 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
+ */
+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',
+ ]);
+ }
+}
diff --git a/src/AppBundle/Validator/Constraints/Role.php b/src/AppBundle/Validator/Constraints/Role.php
new file mode 100644
index 00000000..5957a935
--- /dev/null
+++ b/src/AppBundle/Validator/Constraints/Role.php
@@ -0,0 +1,31 @@
+
+ *
+ * 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
+ */
+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.';
+}
diff --git a/src/AppBundle/Validator/Constraints/RoleValidator.php b/src/AppBundle/Validator/Constraints/RoleValidator.php
new file mode 100644
index 00000000..1ad369a8
--- /dev/null
+++ b/src/AppBundle/Validator/Constraints/RoleValidator.php
@@ -0,0 +1,50 @@
+
+ *
+ * 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
+ */
+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();
+ }
+ }
+ }
+}