<?php
namespace FMT\Application\Controller\Admin\Crud;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection;
use EasyCorp\Bundle\EasyAdminBundle\Collection\FilterCollection;
use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Assets;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters;
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Dto\SearchDto;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use FMT\Application\Controller\AbstractCrudController;
use FMT\Application\FormType\Admin\ExternalUserCrudType;
use FMT\Application\FormType\Admin\Filters\ArrayFilterType;
use FMT\Application\FormType\Admin\Filters\FosUserRoleFilter;
use FMT\Application\FormType\Admin\MajorType;
use FMT\Data\Entity\User;
use FMT\Data\Entity\UserMajor;
use FMT\Data\Entity\UserProfile;
use FMT\Data\Repository\UserRepository;
use FMT\Domain\Service\Manager\UserManager;
use FMT\Domain\Service\Synchronizer\MajorSynchronizer;
use FMT\Infrastructure\Service\AmazonS3\StorageInterface;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\Security\Core\Security;
class ExternalUserController extends AbstractCrudController
{
public const EXTERNAL_USER_ROLES = [
'fmt.user.role.donor' => User::ROLE_DONOR,
'fmt.user.role.student' => User::ROLE_STUDENT,
];
public static function getEntityFqcn(): string
{
return User::class;
}
private UserManager $manager;
private AdminUrlGenerator $adminUrlGenerator;
private ExternalUserCrudType $userCrudType;
private UserRepository $userRepository;
private SessionInterface $session;
private StorageInterface $avatarStorage;
private MajorSynchronizer $majorSynchronizer;
private RequestStack $requestStack;
private Security $security;
public function __construct(
UserManager $manager,
AdminUrlGenerator $adminUrlGenerator,
ExternalUserCrudType $userCrudType,
UserRepository $userRepository,
SessionInterface $session,
StorageInterface $avatarStorage,
MajorSynchronizer $majorSynchronizer,
RequestStack $requestStack,
Security $security
) {
$this->manager = $manager;
$this->adminUrlGenerator = $adminUrlGenerator;
$this->userCrudType = $userCrudType;
$this->userRepository = $userRepository;
$this->session = $session;
$this->avatarStorage = $avatarStorage;
$this->majorSynchronizer = $majorSynchronizer;
$this->requestStack = $requestStack;
$this->security = $security;
}
public function createIndexQueryBuilder(
SearchDto $searchDto,
EntityDto $entityDto,
FieldCollection $fields,
FilterCollection $filters
): QueryBuilder {
$queryBuilder = parent::createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters);
// Check custom filter
$request = $this->requestStack->getCurrentRequest();
$userType = $request->query->get('filters')['userType'] ?? null;
if ($userType === 'STUDENT') {
$queryBuilder->andWhere('entity.roles LIKE :role')
->setParameter('role', '%ROLE_STUDENT%');
} elseif ($userType === 'DONOR') {
$queryBuilder->andWhere('entity.roles LIKE :role')
->setParameter('role', '%ROLE_DONOR%');
}
return $this->userRepository->getUserByRoleQueryBuilder($queryBuilder, self::EXTERNAL_USER_ROLES, $searchDto);
}
public function createEditFormBuilder(
EntityDto $entityDto,
KeyValueStore $formOptions,
AdminContext $context
): FormBuilderInterface {
$fields = $entityDto->getFields();
if ($entityDto->getInstance()->isSuperAdmin()) {
$this->disableFields($fields);
}
$builder = parent::createEditFormBuilder($entityDto, $formOptions, $context);
if ($builder->has('profile_avatar_filename')) {
$builder->get('profile_avatar_filename')->addModelTransformer(new CallbackTransformer(
function ($file) use ($context) {
/** @var User $user */
$user = $context->getEntity()->getInstance();
$fileName = $user->getProfile()->getAvatar()->getFilename();
if (!$fileName) {
return $file;
}
$newPath = sprintf($this->avatarStorage->getFilePath($fileName));
return new File($newPath, false);
},
function ($tagsAsString) {
return $tagsAsString;
}
));
}
$formModifier = function (FormInterface $form, array $options, $school) {
$form->remove('profile_major');
$majors = $this->majorSynchronizer->synchronizeBySchool($school)->getValues();
$options['choices'] = $majors;
$form->add('profile_major', MajorType::class, $options);
};
$builder->addEventListener(
FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$form = $event->getForm();
if (array_key_exists('profile_school', $data)) {
$majorForm = $form->get('profile_major');
$options = $majorForm->getConfig()->getOptions();
$options['ea_crud_form_arch'] = $majorForm->getConfig()->getAttributes();
$formModifier($event->getForm(), $options, $data['profile_school']);
}
});
return $builder;
}
public function configureFields(string $pageName): iterable
{
$action = Action::new('getMajorsForSchool')
->setLabel('Test School API')
->linkToRoute('admin_majors')
->setCssClass('btn btn-primary');
$url = $this->adminUrlGenerator
->setController(ExternalUserController::class)
->setAction($action)
->generateUrl();
$user = $this->getContext()->getEntity()->getInstance();
return $this->userCrudType->getUserCrudFields(
$this->getContext()->getCrud()->getCurrentAction(),
$this->avatarStorage,
$user,
$url
);
}
/**
* @param string $entityFqcn
* @return User
*/
public function createEntity(string $entityFqcn)
{
$user = parent::createEntity($entityFqcn);
$user->getProfile()->setVisible(UserProfile::VISIBILITY_NON);
return $user;
}
/**
* @param EntityManagerInterface $entityManager
* @param $entityInstance
* @return void
*/
public function deleteEntity(EntityManagerInterface $entityManager, $entityInstance): void
{
return;
}
/**
* @param EntityManagerInterface $entityManager
* @param $user
* @return void
*/
public function persistEntity(EntityManagerInterface $entityManager, $user): void
{
$this->manager->create($user);
parent::persistEntity($entityManager, $user);
}
public function configureFilters(Filters $filters): Filters
{
return $filters;
/*->add(
FosUserRoleFilter::new('roles')
->setChoices(self::EXTERNAL_USER_ROLES)
->setFormType(ArrayFilterType::class)
->canSelectMultiple()
->setFormTypeOption('required', false)
);*/
}
public function configureCrud(Crud $crud): Crud
{
// Get the request and check the userType filter
$request = $this->requestStack->getCurrentRequest();
$userType = $request->query->get('filters')['userType'] ?? 'Users';
// Customize the header dynamically
$pageTitle = match (strtoupper($userType)) {
'STUDENT' => 'Student Users',
'DONOR' => 'Donor Users',
default => 'Users',
};
$crud = parent::configureCrud($crud);
$crud
->setSearchFields(['profile.firstName', 'profile.lastName'])
->setDefaultSort(['profile.lastName' => 'ASC'])
->overrideTemplate('crud/edit', 'admin/crud/edit.html.twig')
->setFormOptions(['validation_groups' => ['Admin', 'Default']]);
$crud
->setEntityLabelInPlural($pageTitle)
->setEntityLabelInSingular('User')
->setPaginatorPageSize(PHP_INT_MAX)
->setPageTitle('index', $pageTitle)->setPageTitle('edit', "Edit " . rtrim($pageTitle,"s")); // Adjusts the H1 title
return $crud;
}
public function configureActions(Actions $actions): Actions
{
$loggedInUser = $this->security->getUser();
$isSchoolAdmin = $loggedInUser->hasRole(User::ROLE_SCHOOL_ADMIN);
// Conditionally show or hide actions dropdown
if ($isSchoolAdmin) {
return $actions
->disable(Action::DELETE)
->disable(Action::NEW)
->disable(Action::EDIT);
} else {
return $actions
->disable(Action::DELETE)
->disable(Action::NEW)
->add(Crud::PAGE_INDEX, Action::DETAIL);
}
}
private function disableFields(?FieldCollection $fields): void
{
foreach ($fields as $field) {
if ($field->getProperty() === 'roles') {
$field->setFormTypeOption('disabled', true);
$field->setFormTypeOption('choices', self::EXTERNAL_USER_ROLES);
}
if ($field->getProperty() === 'login') {
$field->setFormTypeOption('disabled', true);
}
}
}
public function configureAssets(Assets $assets): Assets
{
return parent::configureAssets($assets)
->addWebpackEncoreEntry('sign_up')
->addWebpackEncoreEntry('admin_profile_major')
->addWebpackEncoreEntry('admin_crud_edit');
}
/**
* @Route("/admin/getMajorsForSchool",
* methods={"GET"},
* name="admin_majors"
* )
*/
public function getMajorsForSchool(Request $request)
{
$schoolId = $request->get('schoolId');
$majors = $this->majorSynchronizer->synchronizeBySchool($schoolId);
$majorNew = new UserMajor();
$form = $this->createFormBuilder($majorNew)
->add('User_profile_major', EntityType::class, [
'required' => true,
'class' => UserMajor::class,
'label' => 'fmt.form.major',
'choice_label' => 'name',
'mapped' => false,
'choices' => $majors,
'by_reference' => false,
])
->getForm();
return $this->render('admin/form/major.html.twig', [
'form' => $form->createView(),
]);
}
}