abstract class AbstractAdminController extends EaAbstractCrudController | abstract class AbstractAdminController extends EaAbstractCrudController | ||||
{ | { | ||||
protected $session; | |||||
protected $request; | |||||
protected $em; | |||||
protected $translatorAdmin; | |||||
public function __construct( | |||||
SessionInterface $session, | |||||
RequestStack $request, | |||||
EntityManagerInterface $em, | |||||
TranslatorAdmin $translatorAdmin | |||||
) | |||||
public static function getSubscribedServices() | |||||
{ | { | ||||
$this->session = $session; | |||||
$this->request = $request; | |||||
$this->em = $em; | |||||
$this->translatorAdmin = $translatorAdmin; | |||||
return array_merge(parent::getSubscribedServices(), [ | |||||
'session' => SessionInterface::class, | |||||
'request' => RequestStack::class, | |||||
'em' => EntityManagerInterface::class, | |||||
'translator_admin' => TranslatorAdmin::class, | |||||
]); | |||||
} | } | ||||
public function configureActions(Actions $actions): Actions | public function configureActions(Actions $actions): Actions | ||||
$actionsArray[Crud::PAGE_INDEX] = [ | $actionsArray[Crud::PAGE_INDEX] = [ | ||||
Action::NEW => [ | Action::NEW => [ | ||||
'icon' => 'plus', | 'icon' => 'plus', | ||||
'label' => $this->translatorAdmin->transAction('create'), | |||||
'label' => $this->get('translator_admin')->transAction('create'), | |||||
'add_class' => 'btn-sm' | 'add_class' => 'btn-sm' | ||||
], | ], | ||||
Action::EDIT => [ | Action::EDIT => [ | ||||
'label' => false, | 'label' => false, | ||||
'html_attributes' => array( | 'html_attributes' => array( | ||||
'data-toggle' => 'tooltip', | 'data-toggle' => 'tooltip', | ||||
'title' => $this->translatorAdmin->transAction('edit') | |||||
'title' => $this->get('translator_admin')->transAction('edit') | |||||
) | |||||
], | |||||
Action::DETAIL => [ | |||||
'icon' => 'eye', | |||||
'add_class' => 'btn btn-sm btn-success', | |||||
'label' => false, | |||||
'html_attributes' => array( | |||||
'data-toggle' => 'tooltip', | |||||
'title' => $this->get('translator_admin')->transAction('detail') | |||||
) | ) | ||||
], | ], | ||||
Action::DELETE => [ | Action::DELETE => [ | ||||
'icon' => 'trash', | 'icon' => 'trash', | ||||
'dropdown' => true, | 'dropdown' => true, | ||||
'label' => $this->translatorAdmin->transAction('delete') | |||||
'label' => $this->get('translator_admin')->transAction('delete') | |||||
], | ], | ||||
Action::BATCH_DELETE => [ | Action::BATCH_DELETE => [ | ||||
'class' => 'btn btn-sm btn-danger', | 'class' => 'btn btn-sm btn-danger', | ||||
'icon' => 'trash', | 'icon' => 'trash', | ||||
'label' => $this->translatorAdmin->transAction('delete') | |||||
'label' => $this->get('translator_admin')->transAction('delete') | |||||
], | ], | ||||
]; | ]; | ||||
/* Boutons des actions dans l'édition */ | /* Boutons des actions dans l'édition */ | ||||
$actionSaveAndReturn = [ | $actionSaveAndReturn = [ | ||||
'add_class' => 'float-right', | 'add_class' => 'float-right', | ||||
'icon' => 'check', | 'icon' => 'check', | ||||
'label' => $this->translatorAdmin->transAction('save_and_return') | |||||
'label' => $this->get('translator_admin')->transAction('save_and_return') | |||||
]; | ]; | ||||
$actionIndex = [ | $actionIndex = [ | ||||
'icon' => 'chevron-left', | 'icon' => 'chevron-left', | ||||
'class' => 'btn btn-link', | 'class' => 'btn btn-link', | ||||
'label' => $this->translatorAdmin->transAction('back_index') | |||||
'label' => $this->get('translator_admin')->transAction('back_index') | |||||
]; | ]; | ||||
$actionsArray[Crud::PAGE_EDIT] = [ | $actionsArray[Crud::PAGE_EDIT] = [ | ||||
Action::SAVE_AND_CONTINUE => [ | Action::SAVE_AND_CONTINUE => [ | ||||
'class' => 'btn btn-info float-right', | 'class' => 'btn btn-info float-right', | ||||
'label' => $this->translatorAdmin->transAction('save_and_continue') | |||||
'label' => $this->get('translator_admin')->transAction('save_and_continue') | |||||
], | ], | ||||
Action::DELETE => [ | Action::DELETE => [ | ||||
'icon' => 'trash', | 'icon' => 'trash', | ||||
'class' => 'btn btn-outline-danger action-delete', | 'class' => 'btn btn-outline-danger action-delete', | ||||
'label' => $this->translatorAdmin->transAction('delete') | |||||
'label' => $this->get('translator_admin')->transAction('delete') | |||||
], | ], | ||||
Action::SAVE_AND_RETURN => $actionSaveAndReturn, | Action::SAVE_AND_RETURN => $actionSaveAndReturn, | ||||
Action::INDEX => $actionIndex, | Action::INDEX => $actionIndex, | ||||
$actionsArray[Crud::PAGE_NEW] = [ | $actionsArray[Crud::PAGE_NEW] = [ | ||||
Action::SAVE_AND_ADD_ANOTHER => [ | Action::SAVE_AND_ADD_ANOTHER => [ | ||||
'class' => 'btn btn-info float-right', | 'class' => 'btn btn-info float-right', | ||||
'label' => $this->translatorAdmin->transAction('save_and_add_another') | |||||
'label' => $this->get('translator_admin')->transAction('save_and_add_another') | |||||
], | ], | ||||
Action::SAVE_AND_RETURN => $actionSaveAndReturn, | Action::SAVE_AND_RETURN => $actionSaveAndReturn, | ||||
Action::INDEX => $actionIndex, | Action::INDEX => $actionIndex, | ||||
$actions->add(Crud::PAGE_NEW, Action::INDEX); | $actions->add(Crud::PAGE_NEW, Action::INDEX); | ||||
if ($this->isInstanceOf(SortableInterface::class)) { | if ($this->isInstanceOf(SortableInterface::class)) { | ||||
$sortAction = Action::new('sort', $this->translatorAdmin->transAction('sort'), 'fa fa-sort') | |||||
$sortAction = Action::new('sort', $this->get('translator_admin')->transAction('sort'), 'fa fa-sort') | |||||
->linkToCrudAction('sort') | ->linkToCrudAction('sort') | ||||
->setCssClass('btn btn-sm btn-success') | ->setCssClass('btn btn-sm btn-success') | ||||
->createAsGlobalAction(); | ->createAsGlobalAction(); | ||||
if ($this->isInstanceOf(TreeInterface::class)) { | if ($this->isInstanceOf(TreeInterface::class)) { | ||||
$indexChildAction = Action::new( | $indexChildAction = Action::new( | ||||
'index_children', | 'index_children', | ||||
$this->translatorAdmin->transAction('index_children'), | |||||
$this->get('translator_admin')->transAction('index_children'), | |||||
'fa fa-list' | 'fa fa-list' | ||||
) | ) | ||||
->linkToCrudAction(Action::INDEX) | ->linkToCrudAction(Action::INDEX) | ||||
$backParentAction = Action::new( | $backParentAction = Action::new( | ||||
'index_parent', | 'index_parent', | ||||
$this->translatorAdmin->transAction('index_parent'), | |||||
$this->get('translator_admin')->transAction('index_parent'), | |||||
'fa fa-chevron-left' | 'fa fa-chevron-left' | ||||
) | ) | ||||
->linkToCrudAction(Action::INDEX) | ->linkToCrudAction(Action::INDEX) | ||||
foreach ($actionsArray as $crudActionName => $actionsStyle) { | foreach ($actionsArray as $crudActionName => $actionsStyle) { | ||||
foreach ($actionsStyle as $actionName => $button) { | foreach ($actionsStyle as $actionName => $button) { | ||||
$actions->update( | |||||
$crudActionName, | |||||
$actionName, | |||||
function (Action $action) use ($button) { | |||||
if (isset($button['add_class'])) { | |||||
$action->addCssClass($button['add_class']); | |||||
} | |||||
if (isset($button['class'])) { | |||||
$action->setCssClass($button['class']); | |||||
} | |||||
if (isset($button['icon'])) { | |||||
$action->setIcon('fa fa-' . $button['icon']); | |||||
// si l'action existe | |||||
if ($actions->getAsDto('actions')->getAction($crudActionName, $actionName)) { | |||||
$actions->update( | |||||
$crudActionName, | |||||
$actionName, | |||||
function (Action $action) use ($button) { | |||||
if (isset($button['add_class'])) { | |||||
$action->addCssClass($button['add_class']); | |||||
} | |||||
if (isset($button['class'])) { | |||||
$action->setCssClass($button['class']); | |||||
} | |||||
if (isset($button['icon'])) { | |||||
$action->setIcon('fa fa-' . $button['icon']); | |||||
} | |||||
if (isset($button['label'])) { | |||||
$action->setLabel($button['label']); | |||||
} | |||||
if (isset($button['dropdown']) && $button['dropdown']) { | |||||
$action->addCssClass('in-dropdown'); | |||||
} | |||||
if (isset($button['html_attributes']) && $button['html_attributes']) { | |||||
$action->setHtmlAttributes($button['html_attributes']); | |||||
} | |||||
return $action; | |||||
} | } | ||||
if (isset($button['label'])) { | |||||
$action->setLabel($button['label']); | |||||
} | |||||
if (isset($button['dropdown']) && $button['dropdown']) { | |||||
$action->addCssClass('in-dropdown'); | |||||
} | |||||
if (isset($button['html_attributes']) && $button['html_attributes']) { | |||||
$action->setHtmlAttributes($button['html_attributes']); | |||||
} | |||||
return $action; | |||||
} | |||||
); | |||||
); | |||||
} | |||||
} | } | ||||
} | } | ||||
$entityClass = $this->getEntityFqcn(); | $entityClass = $this->getEntityFqcn(); | ||||
$paramListMaxResults = 'listMaxResults'; | $paramListMaxResults = 'listMaxResults'; | ||||
$paramSessionListMaxResults = $entityClass . '-' . $paramListMaxResults; | $paramSessionListMaxResults = $entityClass . '-' . $paramListMaxResults; | ||||
$requestListMaxResults = $this->request->getCurrentRequest()->get($paramListMaxResults); | |||||
$requestListMaxResults = $this->get('request')->getCurrentRequest()->get($paramListMaxResults); | |||||
if ($requestListMaxResults) { | if ($requestListMaxResults) { | ||||
$this->session->set($paramSessionListMaxResults, $requestListMaxResults); | |||||
$this->get('session')->set($paramSessionListMaxResults, $requestListMaxResults); | |||||
} | } | ||||
$maxResults = $this->session->get($paramSessionListMaxResults) ? $this->session->get( | |||||
$maxResults = $this->get('session')->get($paramSessionListMaxResults) ? $this->get('session')->get( | |||||
$paramSessionListMaxResults | $paramSessionListMaxResults | ||||
) : 30; | ) : 30; | ||||
} | } | ||||
public function createIndexQueryBuilder( | public function createIndexQueryBuilder( | ||||
SearchDto $searchDto, | |||||
EntityDto $entityDto, | |||||
FieldCollection $fields, | |||||
FilterCollection $filters | |||||
SearchDto $searchDto, | |||||
EntityDto $entityDto, | |||||
FieldCollection $fields, | |||||
FilterCollection $filters | |||||
): QueryBuilder { | ): QueryBuilder { | ||||
$queryBuilder = parent::createIndexQueryBuilder( | $queryBuilder = parent::createIndexQueryBuilder( | ||||
$searchDto, | |||||
$entityDto, | |||||
$fields, | |||||
$filters | |||||
$searchDto, | |||||
$entityDto, | |||||
$fields, | |||||
$filters | |||||
); | ); | ||||
if ($this->isInstanceOf(TreeInterface::class)) { | if ($this->isInstanceOf(TreeInterface::class)) { | ||||
} | } | ||||
public function createSortQueryBuilder( | public function createSortQueryBuilder( | ||||
SearchDto $searchDto, | |||||
EntityDto $entityDto, | |||||
FieldCollection $fields, | |||||
FilterCollection $filters | |||||
SearchDto $searchDto, | |||||
EntityDto $entityDto, | |||||
FieldCollection $fields, | |||||
FilterCollection $filters | |||||
): QueryBuilder { | ): QueryBuilder { | ||||
$queryBuilder = $this->createIndexQueryBuilder($searchDto,$entityDto,$fields,$filters); | |||||
$queryBuilder = $this->createIndexQueryBuilder($searchDto, $entityDto, $fields, $filters); | |||||
return $queryBuilder; | return $queryBuilder; | ||||
} | } | ||||
// on vide le flash bag si édition en ajax (notification déjà affichée en Javascript) | // on vide le flash bag si édition en ajax (notification déjà affichée en Javascript) | ||||
if ($context->getRequest()->isXmlHttpRequest()) { | if ($context->getRequest()->isXmlHttpRequest()) { | ||||
$this->session->getFlashBag()->clear(); | |||||
$this->get('session')->getFlashBag()->clear(); | |||||
} | } | ||||
return $response; | return $response; |
namespace Lc\SovBundle\Controller\Ticket; | namespace Lc\SovBundle\Controller\Ticket; | ||||
use App\Entity\Ticket\Ticket; | use App\Entity\Ticket\Ticket; | ||||
use App\Entity\Ticket\TicketMessage; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Collection\FieldCollection; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Action; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Config\Filters; | use EasyCorp\Bundle\EasyAdminBundle\Config\Filters; | ||||
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterCrudActionEvent; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Event\AfterEntityPersistedEvent; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeCrudActionEvent; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Event\BeforeEntityPersistedEvent; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Exception\ForbiddenActionException; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Exception\InsufficientEntityPermissionException; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Factory\EntityFactory; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; | use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; | ||||
use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField; | use EasyCorp\Bundle\EasyAdminBundle\Field\ChoiceField; | ||||
use EasyCorp\Bundle\EasyAdminBundle\Field\DateField; | use EasyCorp\Bundle\EasyAdminBundle\Field\DateField; | ||||
use EasyCorp\Bundle\EasyAdminBundle\Field\IntegerField; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; | use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; | ||||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Security\Permission; | |||||
use Lc\SovBundle\Factory\Ticket\TicketFactory; | |||||
use Lc\SovBundle\Field\StatusField; | use Lc\SovBundle\Field\StatusField; | ||||
use Lc\SovBundle\Form\Ticket\TicketFormType; | |||||
use Lc\SovBundle\Model\Ticket\TicketInterface; | use Lc\SovBundle\Model\Ticket\TicketInterface; | ||||
use Lc\SovBundle\Controller\AbstractAdminController; | use Lc\SovBundle\Controller\AbstractAdminController; | ||||
use Symfony\Component\HttpFoundation\Request; | |||||
use Symfony\Component\HttpFoundation\Response; | |||||
abstract class TicketAdminController extends AbstractAdminController | |||||
class TicketAdminController extends AbstractAdminController | |||||
{ | { | ||||
protected $ticketFactory; | |||||
public function __construct(TicketFactory $ticketFactory) | |||||
{ | |||||
$this->ticketFactory = $ticketFactory; | |||||
} | |||||
public static function getEntityFqcn(): string | public static function getEntityFqcn(): string | ||||
{ | { | ||||
public function configureFields(string $pageName): iterable | public function configureFields(string $pageName): iterable | ||||
{ | { | ||||
return [ | return [ | ||||
TextField::new('id')->hideOnForm(), | |||||
DateField::new('createdAt')->setFormat('short')->hideOnForm(), | |||||
TextField::new('visitorFirstName')->hideOnForm(), | |||||
TextField::new('visitorLastName')->hideOnForm(), | |||||
TextField::new('visitorEmail')->hideOnForm(), | |||||
IntegerField::new('id') | |||||
->hideOnForm(), | |||||
DateField::new('createdAt')->setFormat('short') | |||||
->hideOnForm(), | |||||
TextField::new('visitorFirstName') | |||||
->setTemplatePath('@LcSov/admin/ticket/index_username.html.twig') | |||||
->hideOnForm(), | |||||
TextField::new('visitorEmail') | |||||
->setTemplatePath('@LcSov/admin/ticket/index_email.html.twig') | |||||
->hideOnForm(), | |||||
AssociationField::new('user') | AssociationField::new('user') | ||||
->hideOnIndex(), | ->hideOnIndex(), | ||||
TextField::new('subject'), | TextField::new('subject'), | ||||
TextField::new('lastMessage') | |||||
->setTemplatePath('@LcSov/admin/ticket/index_lastmessage.html.twig') | |||||
->hideOnForm(), | |||||
ChoiceField::new('type') | ChoiceField::new('type') | ||||
->autocomplete() | ->autocomplete() | ||||
->setChoices( | ->setChoices( | ||||
'entity.Ticket.fields.typeOptions.' . Ticket::TYPE_TECHNICAL_PROBLEM => Ticket::TYPE_TECHNICAL_PROBLEM, | 'entity.Ticket.fields.typeOptions.' . Ticket::TYPE_TECHNICAL_PROBLEM => Ticket::TYPE_TECHNICAL_PROBLEM, | ||||
] | ] | ||||
), | ), | ||||
StatusField::new('status')->setRequired(false)->hideOnForm(), | |||||
ChoiceField::new('status') | |||||
->autocomplete() | |||||
->setChoices( | |||||
[ | |||||
'entity.Ticket.fields.statusOptions.' . Ticket::TICKET_STATUS_OPEN => Ticket::TICKET_STATUS_OPEN, | |||||
'entity.Ticket.fields.statusOptions.' . Ticket::TICKET_STATUS_BEING_PROCESSED => Ticket::TICKET_STATUS_BEING_PROCESSED, | |||||
'entity.Ticket.fields.statusOptions.' . Ticket::TICKET_STATUS_CLOSED => Ticket::TICKET_STATUS_CLOSED, | |||||
] | |||||
) | |||||
->setTemplatePath('@LcSov/admin/ticket/index_status.html.twig') | |||||
->hideOnForm(), | |||||
]; | ]; | ||||
} | } | ||||
public function configureCrud(Crud $crud): Crud | |||||
{ | |||||
$crud | |||||
->overrideTemplate('crud/detail', '@LcSov/admin/ticket/detail.html.twig'); | |||||
return parent::configureCrud($crud); | |||||
} | |||||
public function configureActions(Actions $actions): Actions | |||||
{ | |||||
$actions | |||||
->add(Crud::PAGE_INDEX, Action::DETAIL) | |||||
->remove(Crud::PAGE_INDEX, Action::EDIT); | |||||
return parent::configureActions($actions); | |||||
} | |||||
public function new(AdminContext $context) | |||||
{ | |||||
$ticket = $this->ticketFactory->create(); | |||||
$form = $this->createForm(TicketFormType::class, $ticket); | |||||
$form->handleRequest($context->getRequest()); | |||||
if ($form->isSubmitted() && $form->isValid()) { | |||||
$ticket = $form->getData(); | |||||
foreach($ticket->getTicketMessages() as $ticketMessage){ | |||||
$this->get('em')->persist($ticketMessage); | |||||
} | |||||
$this->get('em')->persist($ticket); | |||||
$this->get('em')->flush(); | |||||
return $this->redirectToRoute('admin_dashboard'); | |||||
} | |||||
return $this->render( | |||||
'@LcSov/admin/ticket/new.html.twig', | |||||
[ | |||||
'form' => $form->createView(), | |||||
] | |||||
); | |||||
} | |||||
} | } |
<?php | |||||
namespace Lc\SovBundle\Factory\Ticket; | |||||
use App\Entity\Ticket\Ticket; | |||||
use Doctrine\ORM\EntityManagerInterface; | |||||
use Lc\SovBundle\Factory\AbstractFactory; | |||||
use Lc\SovBundle\Model\Ticket\TicketInterface; | |||||
class TicketFactory extends AbstractFactory | |||||
{ | |||||
protected $ticketMessageFactory; | |||||
public function __construct(EntityManagerInterface $em, TicketMessageFactory $ticketMessageFactory) | |||||
{ | |||||
$this->ticketMessageFactory = $ticketMessageFactory; | |||||
parent::__construct($em); | |||||
} | |||||
public function getEntityClass() | |||||
{ | |||||
return TicketInterface::class; | |||||
} | |||||
public function create($params = array()) | |||||
{ | |||||
if (!isset($params['status'])) { | |||||
$params['status'] = Ticket::TICKET_STATUS_OPEN; | |||||
} | |||||
$ticket = parent::create($params); | |||||
if (!isset($params['ticketMessages'])) { | |||||
$ticket->addTicketMessage($this->ticketMessageFactory->create()); | |||||
} | |||||
return $ticket; | |||||
} | |||||
} |
<?php | |||||
namespace Lc\SovBundle\Factory\Ticket; | |||||
use Lc\SovBundle\Factory\AbstractFactory; | |||||
use Lc\SovBundle\Model\Ticket\TicketMessageInterface; | |||||
class TicketMessageFactory extends AbstractFactory | |||||
{ | |||||
public function getEntityClass() | |||||
{ | |||||
return TicketMessageInterface::class; | |||||
} | |||||
public function create($params = array()) | |||||
{ | |||||
if (!isset($params['status'])) { | |||||
$params['status'] = 1; | |||||
} | |||||
return parent::create($params); | |||||
} | |||||
} |
<?php | |||||
namespace Lc\SovBundle\Form\Ticket; | |||||
use EasyCorp\Bundle\EasyAdminBundle\Field\AssociationField; | |||||
use Lc\SovBundle\Doctrine\EntityManager; | |||||
use Lc\SovBundle\Model\Ticket\TicketInterface; | |||||
use Lc\SovBundle\Model\Ticket\TicketModel; | |||||
use Lc\SovBundle\Model\User\UserInterface; | |||||
use Lc\SovBundle\Translation\TranslatorAdmin; | |||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType; | |||||
use Symfony\Component\Form\AbstractType; | |||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType; | |||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType; | |||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType; | |||||
use Symfony\Component\Form\Extension\Core\Type\TextType; | |||||
use Symfony\Component\Form\FormBuilderInterface; | |||||
use Symfony\Component\OptionsResolver\OptionsResolver; | |||||
class TicketFormType extends AbstractType | |||||
{ | |||||
protected $em; | |||||
protected $translatorAdmin; | |||||
public function __construct(EntityManager $em, TranslatorAdmin $translatorAdmin) | |||||
{ | |||||
$this->em = $em; | |||||
$this->translatorAdmin = $translatorAdmin; | |||||
} | |||||
public function buildForm(FormBuilderInterface $builder, array $options) | |||||
{ | |||||
$entityName = $this->em->getEntityName(TicketInterface::class); | |||||
$builder->add( | |||||
'user', | |||||
EntityType::class, | |||||
[ | |||||
'class' => $this->em->getEntityName(UserInterface::class), | |||||
] | |||||
); | |||||
$builder->add( | |||||
'subject', | |||||
TextType::class | |||||
); | |||||
$builder->add( | |||||
'type', | |||||
ChoiceType::class, | |||||
[ | |||||
'label' => 'Type', | |||||
'choices' => [ | |||||
$this->translatorAdmin->transField( | |||||
'typeOptions.' . TicketModel::TYPE_TECHNICAL_PROBLEM, | |||||
$entityName | |||||
) => TicketModel::TYPE_TECHNICAL_PROBLEM, | |||||
$this->translatorAdmin->transField( | |||||
'typeOptions.' . TicketModel::TYPE_GENERAL_QUESTION, | |||||
$entityName | |||||
) => TicketModel::TYPE_TECHNICAL_PROBLEM, | |||||
], | |||||
] | |||||
); | |||||
$builder->add( | |||||
'ticketMessages', | |||||
CollectionType::class, | |||||
[ | |||||
'entry_type' => TicketMessageType::class, | |||||
'allow_add' => false, | |||||
] | |||||
); | |||||
$builder->add( | |||||
'submit', | |||||
SubmitType::class, | |||||
[ | |||||
'label' => $this->translatorAdmin->transAction('save') | |||||
] | |||||
); | |||||
} | |||||
/** | |||||
* {@inheritdoc} | |||||
*/ | |||||
public function configureOptions(OptionsResolver $resolver) | |||||
{ | |||||
$resolver->setDefaults( | |||||
[ | |||||
'data_class' => $this->em->getEntityName(TicketInterface::class), | |||||
] | |||||
); | |||||
} | |||||
} |
namespace Lc\SovBundle\Form\Ticket; | namespace Lc\SovBundle\Form\Ticket; | ||||
use Doctrine\ORM\EntityManagerInterface; | |||||
use Lc\SovBundle\Doctrine\EntityManager; | |||||
use Lc\SovBundle\Model\Ticket\TicketMessageInterface; | use Lc\SovBundle\Model\Ticket\TicketMessageInterface; | ||||
use Symfony\Component\Form\AbstractType; | use Symfony\Component\Form\AbstractType; | ||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType; | |||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType; | use Symfony\Component\Form\Extension\Core\Type\TextareaType; | ||||
use Symfony\Component\Form\FormBuilderInterface; | use Symfony\Component\Form\FormBuilderInterface; | ||||
use Symfony\Component\OptionsResolver\OptionsResolver; | use Symfony\Component\OptionsResolver\OptionsResolver; | ||||
{ | { | ||||
protected $em; | protected $em; | ||||
public function __construct(EntityManagerInterface $em) | |||||
public function __construct(EntityManager $em) | |||||
{ | { | ||||
$this->em = $em; | $this->em = $em; | ||||
} | } | ||||
$builder->add( | $builder->add( | ||||
'message', | 'message', | ||||
TextareaType::class, | TextareaType::class, | ||||
array( | |||||
[ | |||||
'required' => true | 'required' => true | ||||
) | |||||
) | |||||
->add( | |||||
'add', | |||||
SubmitType::class, | |||||
array( | |||||
'label' => 'action.send', | |||||
'attr' => array( | |||||
'class' => 'btn btn-primary float-right' | |||||
) | |||||
) | |||||
); | |||||
] | |||||
); | |||||
} | } | ||||
public function configureOptions(OptionsResolver $resolver) | public function configureOptions(OptionsResolver $resolver) | ||||
{ | { | ||||
$resolver->setDefaults( | $resolver->setDefaults( | ||||
[ | [ | ||||
'data_class' => $this->em->getClassMetadata(TicketMessageInterface::class)->getName(), | |||||
'translation_domain' => 'lcshop' | |||||
'data_class' => $this->em->getEntityName(TicketMessageInterface::class), | |||||
] | ] | ||||
); | ); | ||||
} | } |
protected $visitorToken; | protected $visitorToken; | ||||
/** | /** | ||||
* @ORM\OneToMany(targetEntity="Lc\SovBundle\Model\Ticket\TicketMessageInterface", mappedBy="ticket", orphanRemoval=true) | |||||
* @ORM\OneToMany(targetEntity="Lc\SovBundle\Model\Ticket\TicketMessageInterface", mappedBy="ticket", orphanRemoval=true, cascade={"persist", "remove"}) | |||||
* @ORM\OrderBy({"id" = "ASC"}) | * @ORM\OrderBy({"id" = "ASC"}) | ||||
*/ | */ | ||||
protected $ticketMessages; | protected $ticketMessages; | ||||
} | } | ||||
} | } | ||||
public function getUserInfosTicket() | |||||
{ | |||||
$user = $this->getUser(); | |||||
if ($user) { | |||||
return '#' . $user->getId() . ' ' . $user->getName() . ' ' . $user->getEmail(); | |||||
} else { | |||||
return strtoupper($this->getVisitorLastname()) . ' ' . $this->getVisitorFirstname( | |||||
) . ' ' . $this->getVisitorEmail(); | |||||
} | |||||
} | |||||
public function getEmail() | |||||
{ | |||||
if ($this->getUser()) { | |||||
return $this->getUser()->getEmail(); | |||||
} else { | |||||
return $this->getVisitorEmail(); | |||||
} | |||||
} | |||||
public function getVisitorInfos() | public function getVisitorInfos() | ||||
{ | { | ||||
return strtoupper($this->getVisitorLastname()) . ' ' . $this->getVisitorFirstname( | return strtoupper($this->getVisitorLastname()) . ' ' . $this->getVisitorFirstname( | ||||
) . ' (' . $this->getVisitorEmail() . ')'; | ) . ' (' . $this->getVisitorEmail() . ')'; | ||||
} | } | ||||
public function getLastMessage() | |||||
{ | |||||
return $this->getTicketMessages()->last(); | |||||
} | |||||
public function getType(): ?string | public function getType(): ?string | ||||
{ | { | ||||
return $this->type; | return $this->type; | ||||
public function getTypeLabel(): string | public function getTypeLabel(): string | ||||
{ | { | ||||
return 'field.Ticket.typeOptions.' . $this->getType(); | |||||
return 'entity.Ticket.fields.typeOptions.' . $this->getType(); | |||||
} | } | ||||
public function getStatus(): ?string | public function getStatus(): ?string |
table.table { | table.table { | ||||
.dropdown-actions { | .dropdown-actions { | ||||
display: inline-block ; | display: inline-block ; | ||||
} | } | ||||
.actions { | |||||
white-space: nowrap; | |||||
} | |||||
} | } |
path: /admin/setting/global | path: /admin/setting/global | ||||
controller: Lc\SovBundle\Controller\Setting\SettingAdminController::manageGlobal | controller: Lc\SovBundle\Controller\Setting\SettingAdminController::manageGlobal | ||||
sov_admin_create_ticket: | |||||
path: /admin/ticket/create | |||||
controller: Lc\SovBundle\Controller\Ticket\TicketAdminController::create | |||||
sort: Ordonner | sort: Ordonner | ||||
delete: Supprimer | delete: Supprimer | ||||
change: Changer | change: Changer | ||||
detail: Voir | |||||
index_children: Afficher les enfants | index_children: Afficher les enfants | ||||
index_parent: Retour au parent | index_parent: Retour au parent | ||||
back_index: Retour à la liste | back_index: Retour à la liste | ||||
label: Page | label: Page | ||||
label_plurial: Pages | label_plurial: Pages | ||||
Ticket: | Ticket: | ||||
label: Ticket | |||||
label_plurial: Tickets | |||||
fields: | fields: | ||||
visitorFirstName: Nom | visitorFirstName: Nom | ||||
visitorLastName: Prénom | visitorLastName: Prénom | ||||
typeOptions: | typeOptions: | ||||
general-question: Questions générales | general-question: Questions générales | ||||
technical-problem: Problème technique | technical-problem: Problème technique | ||||
lastMessage: Dernier message | |||||
statusOptions: | |||||
open: Ouvert | |||||
being-processed: En attente | |||||
closed: Fermé | |||||
default: | default: | ||||
fields: | fields: | ||||
id: Id | id: Id | ||||
createdAt: Created at | |||||
createdAt: Créé à | |||||
user: Utilisateur | user: Utilisateur | ||||
firstname: Prénom | firstname: Prénom | ||||
lastname: Nom | lastname: Nom | ||||
position: Position | position: Position | ||||
description: Description | description: Description | ||||
file: Fichier | file: Fichier | ||||
status: Statut | |||||
status: Status | |||||
email: Email | email: Email | ||||
value: Valeur | value: Valeur | ||||
metaTitle: Meta title | metaTitle: Meta title |
{% extends '@LcSov/adminlte/layout.html.twig' %} | |||||
{% import '@LcSov/adminlte/macro/infobox.html.twig' as macro %} | |||||
{% block content_title %} | |||||
{{ 'detail'|lc_trans_admin_title(ea.getEntity().getFqcn(), {id: ea.getEntity().getInstance().getId()}) }} | |||||
{% endblock %} | |||||
{% block main %} | |||||
{{ dump() }} | |||||
<div class="row"> | |||||
<div class="col-4"> | |||||
{% embed '@LcSov/adminlte/embed/infobox.html.twig' %} | |||||
{% block color %}blue{% endblock %} | |||||
{% block icon %}fa fa-toggle-on{% endblock %} | |||||
{% block title %}Status{% endblock %} | |||||
{% block content %} | |||||
{{ entity.instance.status }} | |||||
{% endblock %} | |||||
{% endembed %} | |||||
{% embed '@LcSov/adminlte/embed/infobox.html.twig' %} | |||||
{% block color %}blue{% endblock %} | |||||
{% block icon %}fa fa-user{% endblock %} | |||||
{% block title %}Utilisateur{% endblock %} | |||||
{% block content %} | |||||
{{ entity.instance.getUserInfosTicket() }} | |||||
{% endblock %} | |||||
{% endembed %} | |||||
{{ macro.infobox('Date',entity.instance.createdAt|date('d/m/Y'), "yellow", "fa fa-calendar") }} | |||||
{{ macro.infobox('Catégorie',entity.instance.getTypeLabel()|trans({},'admin'), "green", "fa fa-archive") }} | |||||
</div> | |||||
<div class="col-8"> | |||||
{% embed '@LcSov/adminlte/embed/card.html.twig' %} | |||||
{% block css %}card-primary{% endblock %} | |||||
{% block header %} | |||||
<h3 class="card-title"> | |||||
Liste des messages | |||||
</h3> | |||||
{% endblock header %} | |||||
{% block body_wrapper %} | |||||
<div class="card-body row"> | |||||
<div class="col-12 direct-chat-primary"> | |||||
{% for message in entity.instance.ticketMessages %} | |||||
<div class="direct-chat-msg {{ message.answerByAdmin ? 'right' }}"> | |||||
<div class="direct-chat-infos clearfix"> | |||||
<div class="direct-chat-name {{ message.answerByAdmin ? 'float-right' : 'float-left' }}"> | |||||
{{ message.createdBy }} | |||||
</div> | |||||
<div class="direct-chat-timestamp {{ message.answerByAdmin ? 'float-left' : 'float-right' }}"> | |||||
{{ message.createdAt|date('d/m/Y H:i') }} | |||||
</div> | |||||
</div> | |||||
{% if message.answerByAdmin %} | |||||
<div class="direct-chat-img align-items-center"> | |||||
<i class="fa fa-user-circle" style="font-size: 2rem; color: red"></i> | |||||
</div> | |||||
{% else %} | |||||
<div class="direct-chat-img align-items-center"> | |||||
<i class="fa fa-user-circle" style="font-size: 2rem"></i> | |||||
</div> | |||||
{% endif %} | |||||
<div class="direct-chat-text"> | |||||
<p>{{ message.message|nl2br }}</p> | |||||
{% if message.imageFilename is not null %} | |||||
<i>Photo jointe au message : </i> <br/> | |||||
<a href="{{ lc_liip(message.imageFilename, 'big') }}" | |||||
data-toggle="lightbox"> | |||||
<img src="{{ lc_liip(message.imageFilename, 'thumb') }}" | |||||
alt="Illustration ticket"/> | |||||
</a> | |||||
{% endif %} | |||||
</div> | |||||
</div> | |||||
{% endfor %} | |||||
</div> | |||||
</div> | |||||
{% endblock %} | |||||
{% endembed %} | |||||
</div> | |||||
</div> | |||||
{% endblock %} |
{{ entity.instance.getEmail() }} |
{% set ticketMessage = entity.instance.getLastMessage() %} | |||||
{% if ticketMessage.answerByAdmin != true %} | |||||
<span class="badge badge-danger"> | |||||
New | |||||
</span> | |||||
{% endif %} | |||||
{{ ticketMessage.createdAt|date('d/m/Y H:i') }} par {{ ticketMessage.createdBy }} {{ ticketMessage.message }} |
{% import "@LcSov/adminlte/macro/badge.html.twig" as macro %} | |||||
{% set status = entity.instance.status %} | |||||
{% if status == 'open' %} | |||||
{{ macro.badge_success(('entity.Ticket.fields.statusOptions.'~entity.instance.status)|trans({},'admin')) }} | |||||
{% elseif status == 'being-processed' %} | |||||
{{ macro.badge_warning(('entity.Ticket.fields.statusOptions.'~entity.instance.status)|trans({},'admin')) }} | |||||
{% elseif status == 'closed' %} | |||||
{{ macro.badge_danger(('entity.Ticket.fields.statusOptions.'~entity.instance.status)|trans({},'admin')) }} | |||||
{% endif %} |
{{ entity.instance.getUsername() }} |
{% extends '@LcSov/adminlte/layout.html.twig' %} | |||||
{% block content_title %} | |||||
{# {{ 'ticket.profile'|lc_trans_admin_title }}#} | |||||
Créer un ticket | |||||
{% endblock %} | |||||
{% block main %} | |||||
<div class="col-8"> | |||||
{% embed '@LcSov/adminlte/embed/card.html.twig' %} | |||||
{% block header_wrapper %}{% endblock %} | |||||
{% block body %} | |||||
{% form_theme form '@LcSov/adminlte/crud/form_theme.html.twig' %} | |||||
{{ form(form) }} | |||||
{% endblock %} | |||||
{% block footer_wrapper %}{% endblock %} | |||||
{% endembed %} | |||||
</div> | |||||
{% endblock main %} |
<div class="info-box"> | |||||
<!-- Apply any bg-* class to to the icon to color it --> | |||||
<span class="info-box-icon bg-{% block color %}{% endblock %}"><i class="{% block icon %}{% endblock %}"></i></span> | |||||
<div class="info-box-content"> | |||||
<span class="info-box-text">{% block title %}{% endblock %}</span> | |||||
<span class="info-box-number">{% block content %}{% endblock %}</span> | |||||
</div> | |||||
</div> |
{% macro badge_success(title) %} | |||||
{{ _self.badge('success', title) }} | |||||
{% endmacro %} | |||||
{% macro badge_warning(title) %} | |||||
{{ _self.badge('warning', title) }} | |||||
{% endmacro %} | |||||
{% macro badge_danger(title) %} | |||||
{{ _self.badge('danger', title) }} | |||||
{% endmacro %} | |||||
{% macro badge(status, title) %} | |||||
<span class="badge badge-{{ status }}">{{ title }}</span> | |||||
{% endmacro %} |
{% macro infobox(title, number, color, icon) %} | |||||
{% macro infobox(title, content, color, icon) %} | |||||
<div class="info-box"> | <div class="info-box"> | ||||
<!-- Apply any bg-* class to to the icon to color it --> | <!-- Apply any bg-* class to to the icon to color it --> | ||||
<span class="info-box-icon bg-{{ color }}"><i class="{{ icon }}"></i></span> | <span class="info-box-icon bg-{{ color }}"><i class="{{ icon }}"></i></span> | ||||
<div class="info-box-content"> | <div class="info-box-content"> | ||||
<span class="info-box-text">{{ title }}</span> | <span class="info-box-text">{{ title }}</span> | ||||
<span class="info-box-number">{{ number }}</span> | |||||
<span class="info-box-number">{{ content }}</span> | |||||
</div> | </div> | ||||
<!-- /.info-box-content --> | <!-- /.info-box-content --> | ||||
</div> | </div> |