443 lines
16 KiB
PHP

<?php
declare(strict_types=1);
/*
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Chill\MainBundle\Controller;
use Chill\MainBundle\CRUD\Controller\CRUDController;
use Chill\MainBundle\Entity\GroupCenter;
use Chill\MainBundle\Entity\User;
use Chill\MainBundle\Form\Type\ComposedGroupCenterType;
use Chill\MainBundle\Form\UserCurrentLocationType;
use Chill\MainBundle\Form\UserPasswordType;
use Chill\MainBundle\Form\UserType;
use Chill\MainBundle\Pagination\PaginatorInterface;
use Chill\MainBundle\Repository\UserRepository;
use Chill\MainBundle\Security\ChillSecurity;
use Chill\MainBundle\Templating\Listing\FilterOrderHelper;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class UserController extends CRUDController
{
final public const FORM_GROUP_CENTER_COMPOSED = 'composed_groupcenter';
public function __construct(
private readonly LoggerInterface $logger,
private readonly ValidatorInterface $validator,
private readonly \Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface $passwordEncoder,
private readonly UserRepository $userRepository,
protected ParameterBagInterface $parameterBag,
private readonly TranslatorInterface $translator,
private readonly ChillSecurity $security,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry
) {}
#[Route(path: '/{_locale}/admin/main/user/{uid}/add_link_groupcenter', name: 'admin_user_add_groupcenter')]
public function addLinkGroupCenterAction(Request $request, mixed $uid): Response
{
$em = $this->managerRegistry->getManager();
$user = $em->getRepository(User::class)->find($uid);
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$form = $this->createAddLinkGroupCenterForm($user, $request);
$form->handleRequest($request);
if ($form->isValid()) {
$groupCenter = $this->getPersistedGroupCenter(
$form[self::FORM_GROUP_CENTER_COMPOSED]->getData()
);
$user->addGroupCenter($groupCenter);
if (0 === $this->validator->validate($user)->count()) {
$em->flush();
$this->addFlash('success', $this->translator->trans('The '
.'permissions have been successfully added to the user'));
$returnPathParams = $request->query->has('returnPath') ?
['returnPath' => $request->query->get('returnPath')] : [];
return $this->redirectToRoute('chill_crud_admin_user_edit', array_merge(['id' => $uid], $returnPathParams));
}
foreach ($this->validator->validate($user) as $error) {
$this->addFlash('error', $error->getMessage());
}
}
return $this->render('@ChillMain/User/edit.html.twig', [
'entity' => $user,
'access_permissions_group_list' => $this->parameterBag->get('chill_main.access_permissions_group_list'),
'edit_form' => $this->createEditForm($user)->createView(),
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($user, $request)->createView(),
'delete_groupcenter_form' => array_map(
static fn (Form $form) => $form->createView(),
iterator_to_array($this->getDeleteLinkGroupCenterByUser($user, $request), true)
),
]);
}
#[Route(path: '/{_locale}/admin/main/user/{uid}/delete_link_groupcenter/{gcid}', name: 'admin_user_delete_groupcenter')]
public function deleteLinkGroupCenterAction(mixed $uid, mixed $gcid, Request $request): RedirectResponse
{
$em = $this->managerRegistry->getManager();
$user = $em->getRepository(User::class)->find($uid);
if (!$user) {
throw $this->createNotFoundException('Unable to find User entity.');
}
$groupCenter = $em->getRepository(GroupCenter::class)
->find($gcid);
if (!$groupCenter) {
throw $this->createNotFoundException('Unable to find groupCenter entity');
}
try {
$user->removeGroupCenter($groupCenter);
} catch (\RuntimeException $ex) {
$this->addFlash('error', $this->translator->trans($ex->getMessage()));
return $this->redirectToRoute('chill_crud_admin_user_edit', ['id' => $uid]);
}
$em->flush();
$this->addFlash('success', $this->translator
->trans('The permissions where removed.'));
return $this->redirectToRoute('chill_crud_admin_user_edit', ['id' => $uid]);
}
public function edit(Request $request, $id): Response
{
$action = 'edit';
$entity = $this->getEntity($action, $id, $request);
if (null === $entity) {
throw $this->createNotFoundException(sprintf('The %s with id %s is not found', $this->getCrudName(), $id));
}
$response = $this->checkACL($action, $entity);
if ($response instanceof Response) {
return $response;
}
$response = $this->onPostCheckACL($action, $request, $entity);
if ($response instanceof Response) {
return $response;
}
$form = $this->createFormFor($action, $entity);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->onFormValid($action, $entity, $form, $request);
$em = $this->managerRegistry->getManager();
$this->onPreFlush($action, $entity, $form, $request);
$em->flush();
$this->onPostFlush($action, $entity, $form, $request);
$this->addFlash('success', $this->generateFormSuccessMessage($action, $entity));
$result = $this->onBeforeRedirectAfterSubmission($action, $entity, $form, $request);
if ($result instanceof Response) {
return $result;
}
return $this->redirectToRoute('chill_crud_'.$this->getCrudName().'_index');
}
if ($form->isSubmitted()) {
$this->addFlash('error', $this->generateFormErrorMessage($action, $form));
}
$defaultTemplateParameters = [
'form' => $form->createView(),
'entity' => $entity,
'crud_name' => $this->getCrudName(),
'access_permissions_group_list' => $this->parameterBag->get('chill_main.access_permissions_group_list'),
];
return $this->render(
$this->getTemplateFor($action, $entity, $request),
$this->generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters)
);
}
/**
* Displays a form to edit the user current location.
*/
#[Route(path: '/{_locale}/main/user/current-location/edit', name: 'chill_main_user_currentlocation_edit')]
public function editCurrentLocationAction(Request $request)
{
$user = $this->security->getUser();
$form = $this->createForm(UserCurrentLocationType::class, $user)
->add('submit', SubmitType::class, ['label' => 'Save'])
->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$currentLocation = $form->get('currentLocation')->getData();
$user->setCurrentLocation($currentLocation);
$this->managerRegistry->getManager()->flush();
$this->addFlash('success', $this->translator->trans('Current location successfully updated'));
return $this->redirect(
$request->query->has('returnPath') ? $request->query->get('returnPath') :
$this->generateUrl('chill_main_homepage')
);
}
return $this->render('@ChillMain/User/edit_current_location.html.twig', [
'entity' => $user,
'edit_form' => $form->createView(),
]);
}
/**
* Displays a form to edit the user password.
*/
#[Route(path: '/{_locale}/admin/user/{id}/edit_password', name: 'admin_user_edit_password')]
public function editPasswordAction(User $user, Request $request)
{
$editForm = $this->createEditPasswordForm($user);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$password = $editForm->get('new_password')->getData();
// logging for prod
$this->logger->info('update password for an user', [
'by' => $this->getUser()->getUsername(),
'user' => $user->getUsername(),
]);
$user->setPassword($this->passwordEncoder->hashPassword($user, $password));
$this->managerRegistry->getManager()->flush();
$this->addFlash('success', $this->translator->trans('Password successfully updated!'));
return $this->redirect(
$request->query->has('returnPath') ? $request->query->get('returnPath') :
$this->generateUrl('chill_crud_admin_user_edit', ['id' => $user->getId()])
);
}
return $this->render('@ChillMain/User/edit_password.html.twig', [
'entity' => $user,
'edit_form' => $editForm->createView(),
]);
}
protected function buildFilterOrderHelper(string $action, Request $request): ?FilterOrderHelper
{
return $this->getFilterOrderHelperFactory()
->create(self::class)
->addSearchBox(['label'])
->build();
}
protected function countEntities(string $action, Request $request, ?FilterOrderHelper $filterOrder = null): int
{
if (!$filterOrder instanceof FilterOrderHelper) {
return parent::countEntities($action, $request, $filterOrder);
}
if (null === $filterOrder->getQueryString()) {
return parent::countEntities($action, $request, $filterOrder);
}
return $this->userRepository->countByUsernameOrEmail($filterOrder->getQueryString());
}
protected function createFormFor(string $action, $entity, ?string $formClass = null, array $formOptions = []): FormInterface
{
// for "new", add special config
if ('new' === $action) {
return $this->createForm(UserType::class, $entity, [
'is_creation' => true,
]);
}
// default behaviour
return parent::createFormFor($action, $entity, $formClass, $formOptions);
}
protected function generateTemplateParameter(string $action, $entity, Request $request, array $defaultTemplateParameters = [])
{
// add mini-forms for edit action
if ('edit' === $action) {
return array_merge(
$defaultTemplateParameters,
[
'add_groupcenter_form' => $this->createAddLinkGroupCenterForm($entity, $request)->createView(),
'delete_groupcenter_form' => array_map(
static fn (Form $form) => $form->createView(),
iterator_to_array($this->getDeleteLinkGroupCenterByUser($entity, $request), true)
),
]
);
}
if ('index' === $action) {
return array_merge(
['allow_change_password' => $this->parameterBag->get('chill_main.access_user_change_password')],
$defaultTemplateParameters
);
}
// default behaviour
return parent::generateTemplateParameter($action, $entity, $request, $defaultTemplateParameters);
}
protected function getQueryResult(
string $action,
Request $request,
int $totalItems,
PaginatorInterface $paginator,
?FilterOrderHelper $filterOrder = null
) {
if (0 === $totalItems) {
return [];
}
if (!$filterOrder instanceof FilterOrderHelper) {
return parent::getQueryResult($action, $request, $totalItems, $paginator, $filterOrder);
}
if (null === $filterOrder->getQueryString()) {
return parent::getQueryResult($action, $request, $totalItems, $paginator, $filterOrder);
}
return $this->userRepository->findByUsernameOrEmail(
$filterOrder->getQueryString(),
['usernameCanonical' => 'ASC'],
$paginator->getItemsPerPage(),
$paginator->getCurrentPageFirstItemNumber()
);
}
protected function onPrePersist(string $action, $entity, FormInterface $form, Request $request)
{
// for "new", encode the password
if ('new' === $action && $this->parameterBag->get('chill_main.access_user_change_password')) {
$entity->setPassword($this->passwordEncoder
->hashPassword($entity, $form['plainPassword']->getData()));
}
// default behaviour
parent::onPrePersist($action, $entity, $form, $request);
}
protected function orderQuery(string $action, $query, Request $request, PaginatorInterface $paginator)
{
$query
->addOrderBy('e.usernameCanonical', 'ASC');
return parent::orderQuery($action, $query, $request, $paginator);
}
/**
* Create a form to add a link to a groupcenter.
*/
private function createAddLinkGroupCenterForm(User $user, Request $request): FormInterface
{
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
return $this->createFormBuilder()
->setAction($this->generateUrl(
'admin_user_add_groupcenter',
array_merge($returnPathParams, ['uid' => $user->getId()])
))
->setMethod('POST')
->add(self::FORM_GROUP_CENTER_COMPOSED, ComposedGroupCenterType::class)
->add('submit', SubmitType::class, ['label' => 'Add a new groupCenter'])
->getForm();
}
/**
* Creates a form to delete a link to a GroupCenter.
*/
private function createDeleteLinkGroupCenterForm(User $user, GroupCenter $groupCenter, mixed $request): FormInterface
{
$returnPathParams = $request->query->has('returnPath') ? ['returnPath' => $request->query->get('returnPath')] : [];
return $this->createFormBuilder()
->setAction($this->generateUrl(
'admin_user_delete_groupcenter',
array_merge($returnPathParams, ['uid' => $user->getId(), 'gcid' => $groupCenter->getId()])
))
->setMethod('DELETE')
->add('submit', SubmitType::class, ['label' => 'Delete'])
->getForm();
}
private function createEditPasswordForm(User $user): FormInterface
{
return $this->createForm(
UserPasswordType::class,
null,
[
'user' => $user,
]
)
->add('submit', SubmitType::class, ['label' => 'Change password'])
->remove('actual_password');
}
private function getDeleteLinkGroupCenterByUser(User $user, Request $request)
{
foreach ($user->getGroupCenters() as $groupCenter) {
yield $groupCenter->getId() => $this->createDeleteLinkGroupCenterForm($user, $groupCenter, $request);
}
}
private function getPersistedGroupCenter(GroupCenter $groupCenter)
{
$em = $this->managerRegistry->getManager();
$groupCenterManaged = $em->getRepository(GroupCenter::class)
->findOneBy([
'center' => $groupCenter->getCenter(),
'permissionsGroup' => $groupCenter->getPermissionsGroup(),
]);
if (!$groupCenterManaged) {
$em->persist($groupCenter);
return $groupCenter;
}
return $groupCenterManaged;
}
}