Added an simple admin page for users.

This commit is contained in:
Jan Böhmer 2019-04-28 14:18:11 +02:00
parent 0826f5e6aa
commit 8a4d665d2a
12 changed files with 276 additions and 30 deletions

View file

@ -32,6 +32,7 @@
namespace App\Controller\AdminPages;
use App\Entity\AttachmentType;
use App\Entity\NamedDBElement;
use App\Entity\StructuralDBElement;
use App\Form\BaseEntityAdminForm;
use App\Form\ImportType;
@ -59,7 +60,7 @@ abstract class BaseAdminController extends AbstractController
}
}
protected function _edit(StructuralDBElement $entity, Request $request, EntityManagerInterface $em)
protected function _edit(NamedDBElement $entity, Request $request, EntityManagerInterface $em)
{
$this->denyAccessUnlessGranted('read', $entity);
@ -125,7 +126,7 @@ abstract class BaseAdminController extends AbstractController
]);
}
protected function _delete(Request $request, StructuralDBElement $entity, StructuralElementRecursionHelper $recursionHelper)
protected function _delete(Request $request, NamedDBElement $entity, StructuralElementRecursionHelper $recursionHelper)
{
$this->denyAccessUnlessGranted('delete', $entity);
@ -133,7 +134,7 @@ abstract class BaseAdminController extends AbstractController
$entityManager = $this->getDoctrine()->getManager();
//Check if we need to remove recursively
if ($request->get('delete_recursive', false)) {
if ($entity instanceof StructuralDBElement && $request->get('delete_recursive', false)) {
$recursionHelper->delete($entity, false);
} else {
$parent = $entity->getParent();
@ -168,7 +169,7 @@ abstract class BaseAdminController extends AbstractController
return $exporter->exportEntityFromRequest($entities,$request);
}
protected function _exportEntity(StructuralDBElement $entity, EntityExporter $exporter, Request $request)
protected function _exportEntity(NamedDBElement $entity, EntityExporter $exporter, Request $request)
{
$this->denyAccessUnlessGranted('read', $entity);

View file

@ -29,8 +29,15 @@
namespace App\Controller;
use App\Entity\AttachmentType;
use App\Entity\Footprint;
use App\Entity\User;
use App\Form\BaseEntityAdminForm;
use App\Form\UserAdminForm;
use App\Form\UserSettingsType;
use App\Services\EntityExporter;
use App\Services\EntityImporter;
use App\Services\StructuralElementRecursionHelper;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Asset\Packages;
@ -38,16 +45,81 @@ use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Constraints\Length;
class UserController extends AbstractController
/**
* @Route("/user")
* Class UserController
* @package App\Controller
*/
class UserController extends AdminPages\BaseAdminController
{
protected $entity_class = User::class;
protected $twig_template = 'AdminPages/UserAdmin.html.twig';
protected $form_class = UserAdminForm::class;
protected $route_base = "user";
/**
* @Route("/user/info", name="user_info_self")
* @Route("/user/{id}/info")
* @Route("/{id}/edit", requirements={"id"="\d+"}, name="user_edit")
* @Route("/{id}/", requirements={"id"="\d+"})
*/
public function edit(User $entity, Request $request, EntityManagerInterface $em)
{
return $this->_edit($entity, $request, $em);
}
/**
* @Route("/new", name="user_new")
* @Route("/")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function new(Request $request, EntityManagerInterface $em, EntityImporter $importer)
{
return $this->_new($request, $em, $importer);
}
/**
* @Route("/{id}", name="user_delete", methods={"DELETE"})
*/
public function delete(Request $request, User $entity, StructuralElementRecursionHelper $recursionHelper)
{
return $this->_delete($request, $entity, $recursionHelper);
}
/**
* @Route("/export", name="user_export_all")
* @param Request $request
* @param SerializerInterface $serializer
* @param EntityManagerInterface $em
* @return Response
*/
public function exportAll(EntityManagerInterface $em, EntityExporter $exporter, Request $request)
{
return $this->_exportAll($em, $exporter, $request);
}
/**
* @Route("/{id}/export", name="user_export")
* @param Request $request
* @param AttachmentType $entity
*/
public function exportEntity(User $entity, EntityExporter $exporter, Request $request)
{
return $this->_exportEntity($entity, $exporter, $request);
}
/**
* @Route("/info", name="user_info_self")
* @Route("/{id}/info")
*/
public function userInfo(?User $user, Packages $packages)
{
@ -66,13 +138,13 @@ class UserController extends AbstractController
}
return $this->render('Users/user_info.html.twig', [
'user' => $user,
'avatar' => $avatar,
]);
'user' => $user,
'avatar' => $avatar,
]);
}
/**
* @Route("/user/settings", name="user_settings")
* @Route("/settings", name="user_settings")
*/
public function userSettings(Request $request, EntityManagerInterface $em, UserPasswordEncoderInterface $passwordEncoder)
{

View file

@ -141,4 +141,9 @@ abstract class NamedDBElement extends DBElement
$this->addedDate = new \DateTime('now');
}
}
public function __toString()
{
return $this->getName();
}
}

View file

@ -60,7 +60,7 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
* @ORM\Column(type="string", length=180, unique=true)
* @Assert\NotBlank
*/
protected $name;
protected $name = "";
/**
* //@ORM\Column(type="json").
@ -77,7 +77,7 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
* @var bool True if the user needs to change password after log in
* @ORM\Column(type="boolean")
*/
protected $need_pw_change;
protected $need_pw_change = true;
/**
* @var string|null The first name of the User
@ -413,4 +413,9 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
return $this;
}
public function __toString()
{
return $this->getFullName(true);
}
}

View file

@ -32,7 +32,9 @@
namespace App\Form;
use App\Entity\StructuralDBElement;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
@ -68,11 +70,14 @@ class ImportType extends AbstractType
['JSON' => 'json', 'XML' => 'xml','CSV'=>'csv' ,'YAML' => 'yaml'], 'label' => 'export.format',
'disabled' => $disabled])
->add('csv_separator', TextType::class, ['data' => ';', 'label' => 'import.csv_separator',
'disabled' => $disabled])
->add('parent', EntityType::class, ['class' => $data['entity_class'], 'choice_label' => 'full_path',
'attr' => ['class' => 'selectpicker', 'data-live-search' => true], 'required' => false,
'label' => 'parent.label', 'disabled' => $disabled])
->add('file', FileType::class, ['label' => 'import.file',
'disabled' => $disabled]);
if($data['entity_class'] instanceof StructuralDBElement) {
$builder->
add('parent', EntityType::class, ['class' => $data['entity_class'], 'choice_label' => 'full_path',
'attr' => ['class' => 'selectpicker', 'data-live-search' => true], 'required' => false,
'label' => 'parent.label', 'disabled' => $disabled]);
}
$builder->add('file', FileType::class, ['label' => 'import.file',
'attr' => ['class' => 'file', 'data-show-preview' => 'false', 'data-show-upload' => 'false'], 'disabled' => $disabled])
->add('preserve_children', CheckboxType::class, ['data' => true, 'required' => false,

107
src/Form/UserAdminForm.php Normal file
View file

@ -0,0 +1,107 @@
<?php
/**
*
* part-db version 0.1
* Copyright (C) 2005 Christoph Lechner
* http://www.cl-projects.de/
*
* part-db version 0.2+
* Copyright (C) 2009 K. Jacobs and others (see authors.php)
* http://code.google.com/p/part-db/
*
* Part-DB Version 0.4+
* Copyright (C) 2016 - 2019 Jan Böhmer
* https://github.com/jbtronics
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*
*/
namespace App\Form;
use App\Entity\Group;
use App\Entity\NamedDBElement;
use App\Entity\StructuralDBElement;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ResetType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Core\Security;
class UserAdminForm extends AbstractType
{
protected $security;
public function __construct(Security $security)
{
$this->security = $security;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var StructuralDBElement $entity */
$entity = $options['data'];
$is_new = $entity->getID() === null;
$builder
->add('name', TextType::class, ['empty_data' => '', 'label' => 'user.username.label',
'attr' => ['placeholder' => 'user.username.placeholder'],
'disabled' => !$this->security->isGranted('edit_username', $entity), ])
->add('group', EntityType::class, ['class' => Group::class, 'choice_label' => 'name',
'attr' => ['class' => 'selectpicker', 'data-live-search' => true], 'required' => false, 'label' => 'group.label',
'disabled' => !$this->security->isGranted('change_group', $entity), ])
->add('first_name', TextType::class, ['empty_data' => '', 'label' => 'user.firstName.label',
'attr' => ['placeholder' => 'user.firstName.placeholder'], 'required' => false,
'disabled' => !$this->security->isGranted('edit_infos', $entity), ])
->add('last_name', TextType::class, ['empty_data' => '', 'label' => 'user.lastName.label',
'attr' => ['placeholder' => 'user.lastName.placeholder'], 'required' => false,
'disabled' => !$this->security->isGranted('edit_infos', $entity), ])
->add('email', TextType::class, ['empty_data' => '', 'label' => 'user.email.label',
'attr' => ['placeholder' => 'user.email.placeholder'], 'required' => false,
'disabled' => !$this->security->isGranted('edit_infos', $entity), ])
->add('department', TextType::class, ['empty_data' => '', 'label' => 'user.department.label',
'attr' => ['placeholder' => 'user.department.placeholder'], 'required' => false,
'disabled' => !$this->security->isGranted('edit_infos', $entity), ])
;
/*->add('comment', CKEditorType::class, ['required' => false,
'label' => 'comment.label', 'attr' => ['rows' => 4], 'help' => 'bbcode.hint',
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity)]); */
$this->additionalFormElements($builder, $options, $entity);
//Buttons
$builder->add('save', SubmitType::class, ['label' => $is_new ? 'entity.create' : 'entity.edit.save',
'attr' => ['class' => $is_new ? 'btn-success' : '']])
->add('reset', ResetType::class, ['label' => 'entity.edit.reset']);
}
protected function additionalFormElements(FormBuilderInterface $builder, array $options, NamedDBElement $entity)
{
//Empty for Base
}
}

View file

@ -38,6 +38,7 @@ use App\Entity\NamedDBElement;
use App\Entity\Part;
use App\Entity\Storelocation;
use App\Entity\Supplier;
use App\Entity\User;
use App\Exceptions\EntityNotSupported;
use Symfony\Component\HttpKernel\HttpCache\Store;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@ -144,6 +145,10 @@ class EntityURLGenerator
return $this->urlGenerator->generate("footprint_edit", ['id' => $entity->getID()]);
}
if($entity instanceof User) {
return $this->urlGenerator->generate('user_edit', ['id' => $entity->getID()]);
}
//Otherwise throw an error
throw new EntityNotSupported('The given entity is not supported yet!');
}
@ -189,6 +194,10 @@ class EntityURLGenerator
return $this->urlGenerator->generate('footprint_new');
}
if ($entity instanceof User) {
return $this->urlGenerator->generate('user_new');
}
throw new EntityNotSupported('The given entity is not supported yet!');
}
@ -244,15 +253,19 @@ class EntityURLGenerator
}
if ($entity instanceof Manufacturer) {
return $this->urlGenerator->generate('manufacturer_new', ['id' => $entity->getID()]);
return $this->urlGenerator->generate('manufacturer_delete', ['id' => $entity->getID()]);
}
if ($entity instanceof Storelocation) {
return $this->urlGenerator->generate('store_location_new', ['id' => $entity->getID()]);
return $this->urlGenerator->generate('store_location_delete', ['id' => $entity->getID()]);
}
if ($entity instanceof Footprint) {
return $this->urlGenerator->generate('footprint_new', ['id' => $entity->getID()]);
return $this->urlGenerator->generate('footprint_delete', ['id' => $entity->getID()]);
}
if ($entity instanceof User) {
return $this->urlGenerator->generate('user_delete', ['id' => $entity->getID()]);
}
throw new EntityNotSupported('The given entity is not supported yet!');

View file

@ -32,10 +32,12 @@
namespace App\Services;
use App\Entity\DBElement;
use App\Entity\NamedDBElement;
use App\Entity\StructuralDBElement;
use App\Helpers\TreeViewNode;
use App\Repository\StructuralDBElementRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\MakerBundle\Str;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
@ -66,13 +68,15 @@ class TreeBuilder
* @return TreeViewNode The Node for the given Element.
* @throws \App\Exceptions\EntityNotSupported
*/
public function elementToTreeNode(StructuralDBElement $element, ?string $href_type = 'list_parts', DBElement $selectedElement = null) : TreeViewNode
public function elementToTreeNode(NamedDBElement $element, ?string $href_type = 'list_parts', DBElement $selectedElement = null) : TreeViewNode
{
$children = $element->getSubelements();
$children_nodes = null;
foreach ($children as $child) {
$children_nodes[] = $this->elementToTreeNode($child, $href_type, $selectedElement);
if ($element instanceof StructuralDBElement) {
$children = $element->getSubelements();
foreach ($children as $child) {
$children_nodes[] = $this->elementToTreeNode($child, $href_type, $selectedElement);
}
}
//Check if we need to generate a href type
@ -82,7 +86,7 @@ class TreeBuilder
$href = $this->url_generator->getURL($element, $href_type);
}
$tree_node = new TreeViewNode($element->getName(), $href, $children_nodes);
$tree_node = new TreeViewNode($element->__toString(), $href, $children_nodes);
if($children_nodes != null) {
$tree_node->addTag((string) count($children_nodes));
@ -113,7 +117,12 @@ class TreeBuilder
* @var $repo StructuralDBElementRepository
*/
$repo = $this->em->getRepository($class_name);
$root_nodes = $repo->findRootNodes();
if (new $class_name() instanceof StructuralDBElement) {
$root_nodes = $repo->findRootNodes();
} else {
$root_nodes = $repo->findAll();
}
$array = array();
@ -134,7 +143,6 @@ class TreeBuilder
$href_type = "edit";
}
foreach ($root_nodes as $node) {
$array[] = $this->elementToTreeNode($node, $href_type, $selectedElement);
}

View file

@ -39,6 +39,7 @@ use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
use s9e\TextFormatter\Bundles\Forum as TextFormatter;
use Twig\TwigFunction;
use Twig\TwigTest;
class AppExtension extends AbstractExtension
{
@ -64,6 +65,15 @@ class AppExtension extends AbstractExtension
];
}
public function getTests()
{
return [
new TwigTest('instanceof', function ($var, $instance) {
return $var instanceof $instance;
} )
];
}
public function getFunctions()
{
return [

View file

@ -65,7 +65,9 @@
<div class="tab-content">
<div class="tab-pane active" id="home_common">
{{ form_row(form.name) }}
{{ form_row(form.parent) }}
{% if form.parent%}
{{ form_row(form.parent) }}
{% endif %}
{% block additional_controls %}{% endblock %}
@ -101,6 +103,7 @@
<label class="col-form-label col-md-3">{% trans %}createdAt{% endtrans %}</label>
<div class="col-md-9">
<p class="form-control-plaintext">
{% if date(entity.addedDate) > date('1900/01/01') %}
{{ entity.addedDate | localizeddate("long") }}
{% else %}

View file

@ -0,0 +1,15 @@
{% extends "AdminPages/EntityAdminBase.html.twig" %}
{% block card_title %}
<i class="fas fa-file-alt fa-fw"></i> {% trans %}attachment_type.caption{% endtrans %}
{% endblock %}
{% block comment %}{% endblock %}
{% block additional_controls %}
{{ form_row(form.group) }}
{{ form_row(form.first_name) }}
{{ form_row(form.last_name) }}
{{ form_row(form.email) }}
{{ form_row(form.department) }}
{% endblock %}

View file

@ -7,10 +7,12 @@
<div class=""></div>
<div class="col offset-3 pl-2">
<button class="btn btn-danger" {% if not is_granted("delete", entity) %}disabled{% endif %}">{% trans %}entity.delete{% endtrans %}</button>
{% if entity is instanceof('\App\Entity\StructuralDBElement') %}
<div class="ml-2 custom-control custom-checkbox custom-control-inline">
<input type="checkbox" class="custom-control-input" id="recursive" name="delete_recursive">
<label class="custom-control-label" for="recursive">{% trans %}entity.delete.recursive{% endtrans %}</label>
</div>
{% endif %}
</div>
</div>
</form>