Browse Source

User : mon informations personnelles / modifier mon mot de passe

tags/0.1
Guillaume 3 years ago
parent
commit
199a10935a
29 changed files with 503 additions and 1498 deletions
  1. +23
    -13
      Authenticator/LoginFormAuthenticator.php
  2. +10
    -10
      Controller/Admin/AbstractCrudController.php
  3. +2
    -2
      Controller/Admin/DashboardController.php
  4. +2
    -3
      Controller/Admin/SecurityController.php
  5. +73
    -0
      Controller/Admin/UserController.php
  6. +9
    -6
      Controller/Admin/UserCrudController.php
  7. +1
    -1
      DependencyInjection/LcSovExtension.php
  8. +93
    -0
      Form/Type/User/ChangePasswordFormType.php
  9. +80
    -0
      Form/Type/User/ProfileFormType.php
  10. +150
    -146
      Model/User/User.php
  11. +17
    -0
      Resources/config/routes.yaml
  12. +0
    -0
      Resources/config/services.yaml
  13. +1
    -1
      Resources/views/adminlte/block/navbar_header.html.twig
  14. +0
    -21
      Resources/views/tabler/crud/action/translatable.html.twig
  15. +0
    -174
      Resources/views/tabler/crud/edit.html.twig
  16. +0
    -11
      Resources/views/tabler/crud/field/collection.html.twig
  17. +0
    -10
      Resources/views/tabler/crud/field/gallery.html.twig
  18. +0
    -13
      Resources/views/tabler/crud/field/image.html.twig
  19. +0
    -18
      Resources/views/tabler/crud/filemanager/file-manager-modal.html.twig
  20. +0
    -114
      Resources/views/tabler/crud/form_theme.html.twig
  21. +0
    -411
      Resources/views/tabler/crud/index.html.twig
  22. +0
    -189
      Resources/views/tabler/crud/new.html.twig
  23. +0
    -31
      Resources/views/tabler/crud/paginator.html.twig
  24. +0
    -10
      Resources/views/tabler/dashboard.html.twig
  25. +0
    -254
      Resources/views/tabler/layout.html.twig
  26. +0
    -60
      Resources/views/tabler/menu.html.twig
  27. +19
    -0
      Resources/views/user/change_password.html.twig
  28. +18
    -0
      Resources/views/user/profile.html.twig
  29. +5
    -0
      Resources/views/user/request_password.html.twig

+ 23
- 13
Authenticator/LoginFormAuthenticator.php View File

@@ -24,15 +24,19 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P
{
use TargetPathTrait;

public const LOGIN_ROUTE = 'login';
public const LOGIN_ROUTE = 'sov_login';

private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;

public function __construct(EntityManager $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
public function __construct(
EntityManager $entityManager,
UrlGeneratorInterface $urlGenerator,
CsrfTokenManagerInterface $csrfTokenManager,
UserPasswordEncoderInterface $passwordEncoder
) {
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
@@ -42,19 +46,19 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P
public function supports(Request $request)
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
&& $request->isMethod('POST');
}

public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
Security::LAST_USERNAME,
$credentials['email']
);

return $credentials;
@@ -67,7 +71,9 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P
throw new InvalidCsrfTokenException();
}

$user = $this->entityManager->getRepository(UserInterface::class)->findOneBy(['email' => $credentials['email']]);
$user = $this->entityManager->getRepository(UserInterface::class)->findOneBy(
['email' => $credentials['email']]
);

if (!$user) {
// fail authentication with a custom error
@@ -92,11 +98,15 @@ class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements P

public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
$routeName = 'home';
$email = $request->request->get('email');
$user = $this->entityManager->getRepository(UserInterface::class)->findOneBy(['email' => $email]);

if ($user && ($user->hasRole('ROLE_ADMIN') || $user->hasRole('ROLE_SUPER_ADMIN'))) {
$routeName = 'admin_dashboard';
}

return new RedirectResponse($this->urlGenerator->generate('lc_admin_dashboard'));
return new RedirectResponse($this->urlGenerator->generate($routeName));
}

protected function getLoginUrl()

+ 10
- 10
Controller/Admin/AbstractCrudController.php View File

@@ -6,6 +6,7 @@ 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\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController as EaAbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
@@ -142,6 +143,15 @@ abstract class AbstractCrudController extends EaAbstractCrudController
return $actions;
}

public function configureResponseParameters(KeyValueStore $responseParameters): KeyValueStore
{
if (Crud::PAGE_INDEX === $responseParameters->get('pageName')) {
$responseParameters->set('fields', $this->configureFields('index'));
}

return $responseParameters;
}

public function configureCrud(Crud $crud): Crud
{
$crud = parent::configureCrud($crud);
@@ -168,14 +178,4 @@ abstract class AbstractCrudController extends EaAbstractCrudController
$crud->setPaginatorPageSize($maxResults);
}

public function index(AdminContext $context)
{
$responseParameters = parent::index($context);

// Liste des fields
$responseParameters->set('fields', $this->configureFields('index'));

return $responseParameters;
}

}

+ 2
- 2
Controller/Admin/DashboardController.php View File

@@ -61,8 +61,8 @@ class DashboardController extends AbstractDashboardController
->setMenuItems(
[
//MenuItem::linkToRoute('My Profile', 'fa fa-id-card', '', ['...' => '...']),
//MenuItem::section(),
MenuItem::linkToLogout('Déconnexion', 'fa fa-sign-out'),
MenuItem::linkToLogout('Déconnexion', 'sign-out-alt'),
//MenuItem::linkToLogout('Déconnexion', 'sign-out-alt')
]
);
}

+ 2
- 3
Controller/Admin/SecurityController.php View File

@@ -2,7 +2,6 @@

namespace Lc\SovBundle\Controller\Admin;


use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
@@ -14,7 +13,7 @@ class SecurityController extends AbstractController
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
return $this->redirectToRoute('lc_');
return $this->redirectToRoute('admin_dashboard');
}

// get the login error if there is one
@@ -45,7 +44,7 @@ class SecurityController extends AbstractController
'csrf_token_intention' => 'authenticate',

// the URL users are redirected to after the login (default: '/admin')
'target_path' => $this->generateUrl('lc_admin_dashboard'),
'target_path' => $this->generateUrl('admin_dashboard'),

// the label displayed for the username form field (the |trans filter is applied to it)
'username_label' => 'Your username',

+ 73
- 0
Controller/Admin/UserController.php View File

@@ -0,0 +1,73 @@
<?php

namespace Lc\SovBundle\Controller\Admin;

use Lc\SovBundle\Doctrine\EntityManager;
use Lc\SovBundle\Form\Type\User\ChangePasswordFormType;
use Lc\SovBundle\Form\Type\User\ProfileFormType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class UserController extends AbstractController
{

protected $em;

public function __construct(EntityManager $em)
{
$this->em = $em;
}

public function profile(Request $request): Response
{
$user = $this->getUser();
$form = $this->createForm(ProfileFormType::class, $user);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();

$this->em->update($user);
$this->em->flush();
}

return $this->render(
'@LcSov/user/profile.html.twig',
[
'form' => $form->createView()
]
);
}

public function changePassword(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
{
$user = $this->getUser();
$form = $this->createForm(ChangePasswordFormType::class, $user);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$user = $form->getData();

$plainPassword = $form->get('plainPassword')->getData();

// @TODO : créer UserManager
$newPasswordEncoded = $passwordEncoder->encodePassword($user, $plainPassword);
$user->setPassword($newPasswordEncoded);

$this->em->update($user);
$this->em->flush();
}

return $this->render(
'@LcSov/user/change_password.html.twig',
[
'form' => $form->createView()
]
);
}

}

+ 9
- 6
Controller/Admin/UserCrudController.php View File

@@ -2,16 +2,19 @@

namespace Lc\SovBundle\Controller\Admin;

use EasyCorp\Bundle\EasyAdminBundle\Config\Action;
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions;
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;

abstract class UserCrudController extends AbstractCrudController
{

public function configureFields(string $pageName): iterable
{
return [
TextField::new('email')
];
}
public function configureFields(string $pageName): iterable
{
return [
TextField::new('email')
];
}

}

+ 1
- 1
DependencyInjection/LcSovExtension.php View File

@@ -22,7 +22,7 @@ class LcSovExtension extends Extension implements PrependExtensionInterface
$container->setParameter(sprintf('lc_sov.%s', $parameter), $value);

$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
$loader->load('services.yml');
$loader->load('services.yaml');
}

public function prepend(ContainerBuilder $container)

+ 93
- 0
Form/Type/User/ChangePasswordFormType.php View File

@@ -0,0 +1,93 @@
<?php

/*
* This file is part of the FOSUserBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Lc\SovBundle\Form\Type\User;

use Lc\SovBundle\Doctrine\EntityManager;
use Lc\SovBundle\Model\User\UserInterface;
use Symfony\Component\Form\AbstractType;
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\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
use Symfony\Component\Validator\Constraints\NotBlank;

class ChangePasswordFormType extends AbstractType
{

protected $em ;

public function __construct(EntityManager $em)
{
$this->em = $em ;
}

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$constraintsOptions = [
'message' => 'Mot de passe invalide',
];

if (!empty($options['validation_groups'])) {
$constraintsOptions['groups'] = [reset($options['validation_groups'])];
}

$builder->add(
'current_password',
PasswordType::class,
[
'label' => 'Mot de passe actuel',
'mapped' => false,
'constraints' => [
new NotBlank(),
new UserPassword($constraintsOptions),
],
]
);

$builder->add(
'plainPassword',
RepeatedType::class,
[
'type' => PasswordType::class,
'mapped' => false,
'first_options' => ['label' => 'Nouveau mot de passe'],
'second_options' => ['label' => 'Nouveau mot de passe (confirmation)'],
'invalid_message' => 'Les deux mots de passe ne correspondent pas.',
]
);

$builder->add(
'submit',
SubmitType::class,
array(
'label' => 'Sauvegarder'
)
);
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => $this->em->getEntityName(UserInterface::class),
]
);
}
}

+ 80
- 0
Form/Type/User/ProfileFormType.php View File

@@ -0,0 +1,80 @@
<?php

namespace Lc\SovBundle\Form\Type\User;

use Lc\SovBundle\DataTransformer\FileManagerTypeToDataTransformer;
use Lc\SovBundle\Doctrine\EntityManager;
use Lc\SovBundle\Entity\File\File;
use Lc\SovBundle\Doctrine\Extension\FileInterface;
use Lc\SovBundle\Model\User\UserInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
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 ProfileFormType extends AbstractType
{
protected $em ;

public function __construct(EntityManager $em)
{
$this->em = $em ;
}

public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add(
'firstname',
TextType::class,
array(
'label' => 'Prénom'
)
);

$builder->add(
'lastname',
TextType::class,
array(
'label' => 'Nom'
)
);

$builder->add(
'email',
EmailType::class,
array(
'label' => 'Email'
)
);

$builder->add(
'phone',
TextType::class,
array(
'label' => 'Téléphone'
)
);

$builder->add(
'submit',
SubmitType::class,
array(
'label' => 'Sauvegarder'
)
);
}

/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(
[
'data_class' => $this->em->getEntityName(UserInterface::class),
]
);
}
}

+ 150
- 146
Model/User/User.php View File

@@ -11,150 +11,154 @@ use Symfony\Component\Security\Core\User\UserInterface;
*/
abstract class User implements EntityInterface, UserInterface
{
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
protected $email;

/**
* @ORM\Column(type="json")
*/
protected $roles = [];

/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
protected $password;

/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
protected $lastname;

/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
protected $firstname;

/**
* @ORM\Column(type="boolean")
*/
protected $isVerified = false;



public function getEmail(): ?string
{
return $this->email;
}

public function setEmail(string $email): self
{
$this->email = $email;

return $this;
}

/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUsername(): string
{
return (string)$this->email;
}

/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';

return array_unique($roles);
}

public function setRoles(array $roles): self
{
$this->roles = $roles;

return $this;
}

/**
* @see UserInterface
*/
public function getPassword(): string
{
return (string)$this->password;
}

public function setPassword(string $password): self
{
$this->password = $password;

return $this;
}

/**
* @see UserIn
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}

/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}

public function getLastname(): ?string
{
return $this->lastname;
}

public function setLastname(?string $lastname): self
{
$this->lastname = $lastname;

return $this;
}

public function getFirstname(): ?string
{
return $this->firstname;
}

public function setFirstname(?string $firstname): self
{
$this->firstname = $firstname;

return $this;
}

public function getName(): ?string
{
return $this->getFirstname(). ' '.strtoupper($this->getLastname());
}

public function isVerified(): bool
{
return $this->isVerified;
}

public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;

return $this;
}
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
protected $email;

/**
* @ORM\Column(type="json")
*/
protected $roles = [];

/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
protected $password;

/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
protected $lastname;

/**
* @ORM\Column(type="string", length=255, nullable=true)
*/
protected $firstname;

/**
* @ORM\Column(type="boolean")
*/
protected $isVerified = false;


public function getEmail(): ?string
{
return $this->email;
}

public function setEmail(string $email): self
{
$this->email = $email;

return $this;
}

/**
* A visual identifier that represents this user.
*
* @see UserInterface
*/
public function getUsername(): string
{
return (string)$this->email;
}

/**
* @see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';

return array_unique($roles);
}

public function setRoles(array $roles): self
{
$this->roles = $roles;

return $this;
}

public function hasRole($role)
{
return in_array(strtoupper($role), $this->getRoles(), true);
}

/**
* @see UserInterface
*/
public function getPassword(): string
{
return (string)$this->password;
}

public function setPassword(string $password): self
{
$this->password = $password;

return $this;
}

/**
* @see UserIn
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}

/**
* @see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}

public function getLastname(): ?string
{
return $this->lastname;
}

public function setLastname(?string $lastname): self
{
$this->lastname = $lastname;

return $this;
}

public function getFirstname(): ?string
{
return $this->firstname;
}

public function setFirstname(?string $firstname): self
{
$this->firstname = $firstname;

return $this;
}

public function getName(): ?string
{
return $this->getFirstname() . ' ' . strtoupper($this->getLastname());
}

public function isVerified(): bool
{
return $this->isVerified;
}

public function setIsVerified(bool $isVerified): self
{
$this->isVerified = $isVerified;

return $this;
}
}

+ 17
- 0
Resources/config/routes.yaml View File

@@ -0,0 +1,17 @@

sov_login:
path: /login
controller: Lc\SovBundle\Controller\Admin\SecurityController::login

sov_logout:
path: /logout
controller: Lc\SovBundle\Controller\Admin\SecurityController::logout

sov_admin_account_profile:
path: /admin/account/profile
controller: Lc\SovBundle\Controller\Admin\UserController::profile

sov_admin_account_password:
path: /admin/account/password
controller: Lc\SovBundle\Controller\Admin\UserController::changePassword


Resources/config/services.yml → Resources/config/services.yaml View File


+ 1
- 1
Resources/views/adminlte/block/navbar_header.html.twig View File

@@ -35,7 +35,7 @@
target="{{ item.linkTarget }}" rel="{{ item.linkRel }}"
referrerpolicy="origin-when-cross-origin">
{% if item.icon is not empty %}
<i class="ta ta-{{ item.icon }}"></i>
<i class="fa fa-{{ item.icon }}"></i>
{% endif %}
<span>{{ item.label }}</span>
</a>

+ 0
- 21
Resources/views/tabler/crud/action/translatable.html.twig View File

@@ -1,21 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var action \EasyCorp\Bundle\EasyAdminBundle\Dto\ActionDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}

{% if 'a' == action.htmlElement %}
{% for _locale in app_locales %}
<a class="{{ isIncludedInDropdown|default(false) ? 'dropdown-item' }} {{ action.cssClass }}"
href="{{ action.linkUrl }}&_locale={{ _locale }}"
{% for name, value in action.htmlAttributes %}{{ name }}="{{ value|e('html_attr') }}" {% endfor %}>
{%- if action.icon %}<i class="action-icon {{ action.icon }}"></i> {% endif -%}
{%- if action.label is not empty -%}{{ action.label }} {{ _locale }}{%- endif -%}
</a>
{% endfor %}
{% elseif 'button' == action.htmlElement %}
<button class="{{ action.cssClass }}" {% for name, value in action.htmlAttributes %}{{ name }}="{{ value|e('html_attr') }}" {% endfor %}>
<span class="btn-label">
{%- if action.icon %}<i class="action-icon {{ action.icon }}"></i> {% endif -%}
{%- if action.label is not empty -%}{{ action.label }}{%- endif -%}
</span>
</button>
{% endif %}

+ 0
- 174
Resources/views/tabler/crud/edit.html.twig View File

@@ -1,174 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% extends ea.templatePath('layout') %}
{% form_theme edit_form with ea.crud.formThemes only %}

{% trans_default_domain ea.i18n.translationDomain %}

{% block body_id 'ea-edit-' ~ entity.name ~ '-' ~ entity.primaryKeyValue %}
{% block body_class 'ea-edit ea-edit-' ~ entity.name %}

{% block configured_head_contents %}
{{ parent() }}
{% for htmlContent in edit_form.vars.ea_crud_form.assets.headContents %}
{{ htmlContent|raw }}
{% endfor %}
{% endblock %}

{% block configured_body_contents %}
{{ parent() }}
{% for htmlContent in edit_form.vars.ea_crud_form.assets.bodyContents %}
{{ htmlContent|raw }}
{% endfor %}
{% endblock %}

{% block configured_stylesheets %}
{{ parent() }}
{% for css_asset in edit_form.vars.ea_crud_form.assets.cssFiles %}
<link rel="stylesheet" href="{{ asset(css_asset) }}">
{% endfor %}

{% for webpack_encore_entry in edit_form.vars.ea_crud_form.assets.webpackEncoreEntries %}
{{ ea_call_function_if_exists('encore_entry_link_tags', webpack_encore_entry) }}
{% endfor %}
{% endblock %}

{% block configured_javascripts %}
{{ parent() }}
{% for js_asset in edit_form.vars.ea_crud_form.assets.jsFiles %}
<script src="{{ asset(js_asset) }}"></script>
{% endfor %}

{% for webpack_encore_entry in edit_form.vars.ea_crud_form.assets.webpackEncoreEntries %}
{{ ea_call_function_if_exists('encore_entry_script_tags', webpack_encore_entry) }}
{% endfor %}
{% endblock %}

{% block content_title %}
{%- apply spaceless -%}
{{ ea.crud.customPageTitle is null
? (ea.crud.defaultPageTitle|trans(ea.i18n.translationParameters, 'EasyAdminBundle'))|raw
: ea.crud.customPageTitle|trans(ea.i18n.translationParameters)|raw }}
{%- endapply -%}
{% endblock %}

{% block page_actions %}
{% for action in entity.actions %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
{% endblock %}

{% block main %}
<div class="card">
<div class="card-body">
{% block edit_form %}
{{ form(edit_form) }}
{% endblock edit_form %}

{% block delete_form %}
{{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', { entity_id: entity.primaryKeyValue }, with_context = false) }}
{% endblock delete_form %}
</div>
</div>
{% endblock %}

{% block body_javascript %}
{{ parent() }}

<script type="text/javascript">
$(function () {
$('.ea-edit-form').areYouSure({'message': '{{ 'form.are_you_sure'|trans({}, 'EasyAdminBundle')|e('js') }}'});

const entityForm = document.querySelector('form.ea-edit-form');
const inputFieldsSelector = 'input,select,textarea';

// Adding visual feedback for invalid fields: any ".form-group" with invalid fields
// receives "has-error" class. The class is removed on click on the ".form-group"
// itself to support custom/complex fields.
entityForm.addEventListener('submit', function (submitEvent) {
entityForm.querySelectorAll(inputFieldsSelector).forEach(function (input) {
if (!input.validity.valid) {
const formGroup = input.closest('div.form-group');

formGroup.classList.add('has-error');

formGroup.addEventListener('click', function onFormGroupClick() {
formGroup.classList.remove('has-error');
formGroup.removeEventListener('click', onFormGroupClick);
});
}
});

const eaEvent = new CustomEvent('ea.form.submit', {
cancelable: true,
detail: {page: 'edit', form: entityForm}
});
const eaEventResult = document.dispatchEvent(eaEvent);
if (false === eaEventResult) {
submitEvent.preventDefault();
submitEvent.stopPropagation();
}
});

// forms with tabs require some special treatment for errors. The problem
// is when the field with errors is included in a tab not currently visible.
// Browser shows this error "An invalid form control with name='...' is not focusable."
// So, the user clicks on Submit button, the form is not submitted and the error
// is not displayed. This JavaScript code ensures that each tab shows a badge with
// the number of errors in it.
entityForm.addEventListener('submit', function () {
const formTabPanes = entityForm.querySelectorAll('.tab-pane');
if (0 === formTabPanes.length) {
return;
}

let firstNavTabItemWithError = null;

formTabPanes.forEach(function (tabPane) {
let tabPaneNumErrors = 0;
tabPane.querySelectorAll(inputFieldsSelector).forEach(function (input) {
if (!input.validity.valid) {
tabPaneNumErrors++;
}
});

let navTabItem = entityForm.querySelector('.nav-item a[href="#' + tabPane.id + '"]');
let existingErrorBadge = navTabItem.querySelector('span.badge.badge-danger');
if (null !== existingErrorBadge) {
navTabItem.removeChild(existingErrorBadge);
}

if (tabPaneNumErrors > 0) {
let newErrorBadge = document.createElement('span');
newErrorBadge.classList.add('badge', 'badge-danger');
newErrorBadge.title = 'form.tab.error_badge_title';
newErrorBadge.textContent = tabPaneNumErrors;

navTabItem.appendChild(newErrorBadge);

if (null === firstNavTabItemWithError) {
firstNavTabItemWithError = navTabItem;
}
}
});

if (firstNavTabItemWithError) {
firstNavTabItemWithError.click();
}
});

$('.action-delete').on('click', function (e) {
e.preventDefault();
const formAction = $(this).attr('formaction');

$('#modal-delete').modal({backdrop: true, keyboard: true})
.off('click', '#modal-delete-button')
.on('click', '#modal-delete-button', function () {
$('#delete-form').attr('action', formAction).trigger('submit');
});
});
});
</script>
<

{% endblock %}

+ 0
- 11
Resources/views/tabler/crud/field/collection.html.twig View File

@@ -1,11 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{# this is a bit ugly, but Twig doesn't have a 'is numeric' test #}
{% if field.formattedValue is iterable %}
{% for item in field.formattedValue %}
<span class="badge badge-secondary">{{ item }}</span>
{% endfor %}
{% else %}
{{ field.formattedValue }}
{% endif %}

+ 0
- 10
Resources/views/tabler/crud/field/gallery.html.twig View File

@@ -1,10 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{# this is a bit ugly, but Twig doesn't have a 'is numeric' test #}

{% if field.formattedValue matches '/^\\d+$/' %}
<span class="badge badge-secondary">{{ field.formattedValue }}</span>
{% else %}
{{ field.formattedValue }}
{% endif %}

+ 0
- 13
Resources/views/tabler/crud/field/image.html.twig View File

@@ -1,13 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% set html_id = 'ea-lightbox-' ~ field.uniqueId %}
{% if field.value is not null and field.value.path is not null %}
<a href="#" class="ea-lightbox-thumbnail" title="{{ field.value.legend }}" data-featherlight="#{{ html_id }}" data-featherlight-close-on-click="anywhere">
<img src="{{ asset(field.value.path) }}" class="img-fluid">
</a>

<div id="{{ html_id }}" class="ea-lightbox">
<img src="{{ asset(field.value.path) }}">
</div>
{% endif %}

+ 0
- 18
Resources/views/tabler/crud/filemanager/file-manager-modal.html.twig View File

@@ -1,18 +0,0 @@
<div class="modal fade" id="lc-filemanager-modal" tabindex="-1" role="dialog" aria-labelledby="File manager modal">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Gestionaire de fichier</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<iframe id="lc-filemanager-frame" src="" width="100%" height="500" frameborder="0"></iframe>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>

+ 0
- 114
Resources/views/tabler/crud/form_theme.html.twig View File

@@ -1,114 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{% use '@EasyAdmin/crud/form_theme.html.twig' %}



{% block form_label -%}
{% if label is not same as(false) -%}
{%- if compound is defined and compound -%}
{%- set element = 'legend' -%}
{%- set label_attr = label_attr|merge({class: (label_attr.class|default('') ~ ' col-form-label')|trim}) -%}
{%- else -%}
{%- set label_attr = label_attr|merge({for: id, class: (label_attr.class|default('') ~ ' form-label')|trim}) -%}
{%- endif -%}
{{ parent() }}
{%- endif -%}
{%- endblock form_label %}


{% block gallery_manager_row %}
{{ block('collection_row') }}
{% endblock gallery_manager_row %}

{% block gallery_manager_widget %}
{{ block('collection_widget') }}
{% endblock gallery_manager_widget %}


{% block collection_row %}
{% if prototype is defined and not prototype.rendered %}
{% set row_attr = row_attr|merge({ 'data-prototype': form_row(prototype) }) %}
{% endif %}

{% set row_attr = row_attr|merge({
'data-entry-is-complex': form.vars.ea_crud_form.ea_field and form.vars.ea_crud_form.ea_field.customOptions.get('entryIsComplex') ? 'true' : 'false',
'data-allow-add': allow_add ? 'true' : 'false',
'data-allow-delete': allow_delete ? 'true' : 'false',
'data-num-items': form.children|length,
'data-form-type-name-placeholder': prototype is defined ? prototype.vars.name : '',
}) %}

{{ block('form_row') }}
{% endblock collection_row %}

{% block collection_widget %}
{{ block('form_widget') }}
{% if allow_add|default(false) %}
<button type="button" class="btn btn-link field-collection-add">
<i class="fa fa-plus pr-1"></i>
{{ 'action.add_new_item'|trans({}, 'EasyAdminBundle') }}
</button>
{% endif %}
{% endblock collection_widget %}

{% block collection_entry_widget %}
{% set is_complex = form_parent(form).vars.ea_crud_form.ea_field.customOptions.get('entryIsComplex') ?? false %}

<div class="field-collection-item {{ is_complex ? 'field-collection-item-complex' }}">
{{ form_widget(form) }}

{% if form_parent(form).vars.allow_delete|default(false) %}
<button type="button" class="btn btn-link field-collection-delete"
title="{{ 'action.remove_item'|trans({}, 'EasyAdminBundle') }}">
<i class="fas fa-times"></i>
</button>
{% endif %}
</div>
{% endblock collection_entry_widget %}

{% block file_manager_image_row %}
{{ form_widget(form) }}
{% endblock file_manager_image_row %}

{% block file_manager_legend_row %}
{{ form_widget(form) }}
{% endblock file_manager_legend_row %}

{% block file_manager_position_row %}
{{ form_widget(form) }}
{% endblock file_manager_position_row %}


{% block file_manager_widget %}

<div class="lc-filemanager col-xs-12">
<div class="col-md-6 col-xs-12 nopadding" style="padding: 0px;">
<img style="width: 200px; height: 150px; object-fit: contain; background: #ddd; " src="{{ form.path.vars.value }}" class="lc-filemenager-preview" id="{{ form.path.vars.id }}_preview" alt="">
</div>
<div class="input-group">
{{ form_widget(form) }}

<div class="input-group-append">
<button type="button" class="btn btn-sm lc-filemanager-open" data-id="{{ form.path.vars.id }}"
data-target="{{ path('file_manager', {module:1, conf:'default'})|raw }}">
<i class="fa fa-folder-open-o"></i>
</button>
{% if value %}
<button type="button" class="btn btn-sm lc-filemanager-delete" data-id="{{ form.path.vars.id }}">
<i class="fa fa-trash-o"></i>
</button>
{% endif %}

{% if form.parent.vars['row_attr']['data-sortable'] is defined %}
<button type="button" class="btn btn-sm lc-btn-sortable" >
<i class="fa fa-arrows"></i>
</button>
{% endif %}
</div>
</div>

</div>


{% endblock file_manager_widget %}


+ 0
- 411
Resources/views/tabler/crud/index.html.twig View File

@@ -1,411 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var entities \EasyCorp\Bundle\EasyAdminBundle\Collection\EntityDtoCollection #}
{# @var paginator \EasyCorp\Bundle\EasyAdminBundle\Orm\EntityPaginator #}
{% extends ea.templatePath('layout') %}
{% trans_default_domain ea.i18n.translationDomain %}

{% block body_id entities|length > 0 ? 'ea-index-' ~ entities|first.name : '' %}
{% block body_class 'index' ~ (entities|length > 0 ? ' index-' ~ entities|first.name : '') %}

{% block content_title %}
{%- apply spaceless -%}
{% set default_title = ea.crud.defaultPageTitle('index')|trans(ea.i18n.translationParameters, 'EasyAdminBundle') %}
{{ ea.crud.customPageTitle is null ? default_title|raw : ea.crud.customPageTitle('index')|trans(ea.i18n.translationParameters)|raw }}
{%- endapply -%}
{% endblock %}

{% set has_batch_actions = batch_actions|length > 0 %}
{% block page_actions %}
{% block global_actions %}
<div class="global-actions">
{% for action in global_actions %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
</div>
{% endblock global_actions %}
{% block batch_actions %}
{% if has_batch_actions %}
<div class="batch-actions" style="display: none">
{% for action in batch_actions %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}
</div>
{% endif %}
{% endblock %}
{% endblock page_actions %}

{% block main %}
{# sort can be multiple; let's consider the sorting field the first one #}
{% set sort_field_name = app.request.get('sort')|keys|first %}
{% set sort_order = app.request.get('sort')|first %}
{% set some_results_are_hidden = false %}
{% set has_footer = entities|length != 0 %}
{% set has_search = ea.crud.isSearchEnabled %}
{% set has_filters = filters|length > 0 %}
{% set has_datagrid_tools = has_search or has_filters %}


<div class="card">
<div class="card-header">
<div class="d-flex">
<div class="text-muted">
Show
<div class="mx-2 d-inline-block">
<input type="text" class="form-control form-control-sm" value="8" size="3"
aria-label="Invoices count" data-cip-id="cIPJQ342845640">
</div>
entries
</div>
<div class="ms-auto text-muted">
Search:
<div class="ms-2 d-inline-block">
<input type="text" class="form-control form-control-sm" aria-label="Search invoice"
data-cip-id="cIPJQ342845641">
</div>
</div>
</div>
</div>
<div class="card-body border-bottom py-3">
</div>

<div class="table-responsive">
<table class="table card-table table-vcenter text-nowrap table-mobile-md table-striped">
<thead>
{% block table_head %}
<tr>
{% if has_batch_actions %}
<th class="w-1"><span><input type="checkbox"
class="form-check-input m-0 align-middle form-batch-checkbox-all"></span>
</th>
{% endif %}


{% set ea_sort_asc = constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Option\\SortOrder::ASC') %}
{% set ea_sort_desc = constant('EasyCorp\\Bundle\\EasyAdminBundle\\Config\\Option\\SortOrder::DESC') %}
{% for field in entities|first.fields ?? [] %}
{% set is_sorting_field = ea.search.isSortingField(field.property) %}
{% set next_sort_direction = is_sorting_field ? (ea.search.sortDirection(field.property) == ea_sort_desc ? ea_sort_asc : ea_sort_desc) : ea_sort_desc %}
{% set column_icon = is_sorting_field ? (next_sort_direction == ea_sort_desc ? 'fa-arrow-up' : 'fa-arrow-down') : 'fa-sort' %}

<th class="{{ is_sorting_field ? 'sorted' }} {{ field.isVirtual ? 'field-virtual' }} text-{{ field.textAlign }}"
dir="{{ ea.i18n.textDirection }}">
{% if field.isSortable %}
<a href="{{ ea_url({ page: 1, sort: { (field.property): next_sort_direction } }).includeReferrer() }}">
{{ field.label|raw }} <i class="fa fa-fw {{ column_icon }}"></i>
</a>
{% else %}
<span>{{ field.label|raw }}</span>
{% endif %}
</th>
{% endfor %}


<th {% if ea.crud.showEntityActionsAsDropdown %}width="10px"{% endif %} dir="{{ ea.i18n.textDirection }}">
<span class="sr-only">{{ 'action.entity_actions'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}</span>
</th>
</tr>
{% endblock table_head %}
</thead>

<tbody>
{% block table_body %}
{% for entity in entities %}
{% if not entity.isAccessible %}
{% set some_results_are_hidden = true %}
{% else %}
<tr data-id="{{ entity.primaryKeyValueAsString }}">
{% if has_batch_actions %}
<td><input type="checkbox" class="form-batch-checkbox"
value="{{ entity.primaryKeyValue }}"></td>
{% endif %}

{% for field in entity.fields %}
<td class="{{ field.property == sort_field_name ? 'sorted' }} text-{{ field.textAlign }} {{ field.cssClass }}"
dir="{{ ea.i18n.textDirection }}">
{{ include(field.templatePath, { field: field, entity: entity }, with_context = false) }}
</td>
{% endfor %}

{% block entity_actions %}
<td class="actions">
{% if not ea.crud.showEntityActionsAsDropdown %}
{% for action in entity.actions %}
{{ include(action.templatePath, { action: action, entity: entity, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }}
{% endfor %}
{% else %}
<div class="dropdown dropdown-actions">
<a class="dropdown-toggle btn btn-secondary btn-sm" href="#"
role="button" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
<i class="fa fa-fw fa-ellipsis-h"></i>
</a>

<div class="dropdown-menu dropdown-menu-right">
{% for action in entity.actions %}
{{ include(action.templatePath, { action: action, isIncludedInDropdown: ea.crud.showEntityActionsAsDropdown }, with_context = false) }}
{% endfor %}
</div>
</div>
{% endif %}
</td>
{% endblock entity_actions %}
</tr>

{% endif %}
{% else %}
<tr>
<td class="no-results" colspan="100">
{{ 'datagrid.no_results'|trans(ea.i18n.translationParameters, 'EasyAdminBundle') }}
</td>
</tr>
{% endfor %}

{% if some_results_are_hidden %}
<tr class="datagrid-row-empty">
<td class="text-center" colspan="{{ entities|first.fields|length + 1 }}">
<span class="datagrid-row-empty-message"><i
class="fa fa-lock mr-1"></i> {{ 'datagrid.hidden_results'|trans({}, 'EasyAdminBundle') }}</span>
</td>
</tr>
{% endif %}
{% endblock table_body %}
</tbody>
</table>
</div>


{% if entities|length > 0 %}
<div class="card-footer d-flex align-items-center">
{% block paginator %}
{{ include(ea.templatePath('crud/paginator')) }}
{% endblock paginator %}
</div>
{% endif %}
</div>

{% block delete_form %}
{{ include('@EasyAdmin/crud/includes/_delete_form.html.twig', with_context = false) }}
{% endblock delete_form %}

{% if has_filters %}
{{ include('@EasyAdmin/crud/includes/_filters_modal.html.twig') }}
{% endif %}

{% if has_batch_actions %}
{{ include('@EasyAdmin/crud/includes/_batch_action_modal.html.twig', {}, with_context = false) }}
{% endif %}
{% endblock main %}

{% block body_javascript %}
{{ parent() }}

<script type="text/javascript">
$(function () {
const customSwitches = document.querySelectorAll('td.field-boolean .custom-control.custom-switch input[type="checkbox"]');
for (i = 0; i < customSwitches.length; i++) {
customSwitches[i].addEventListener('change', function () {
const customSwitch = this;
const newValue = this.checked;
const oldValue = !newValue;

const toggleUrl = this.getAttribute('data-toggle-url') + "&newValue=" + newValue.toString();
let toggleRequest = $.ajax({type: "GET", url: toggleUrl, data: {}});

toggleRequest.done(function (result) {
});

toggleRequest.fail(function () {
// in case of error, restore the original value and disable the toggle
customSwitch.checked = oldValue;
customSwitch.disabled = true;
customSwitch.closest('.custom-switch').classList.add('disabled');
});
});
}

$('.action-delete').on('click', function (e) {
e.preventDefault();
const formAction = $(this).attr('formaction');

$('#modal-delete').modal({backdrop: true, keyboard: true})
.off('click', '#modal-delete-button')
.on('click', '#modal-delete-button', function () {
let deleteForm = $('#delete-form');
deleteForm.attr('action', formAction);
deleteForm.submit();
});
});

{% if filters|length > 0 %}
const filterModal = document.querySelector('#modal-filters');

const removeFilter = function (field) {
field.closest('form').querySelectorAll('input[name^="filters[' + field.dataset.filterProperty + ']"]').forEach(hidden => {
hidden.remove();
});
field.remove();
}

document.querySelector('#modal-clear-button').addEventListener('click', function () {
filterModal.querySelectorAll('.filter-field').forEach(filterField => {
removeFilter(filterField);
});
filterModal.querySelector('form').submit();
});

document.querySelector('#modal-apply-button').addEventListener('click', function () {
filterModal.querySelectorAll('.filter-checkbox:not(:checked)').forEach(notAppliedFilter => {
removeFilter(notAppliedFilter.closest('.filter-field'));
});
filterModal.querySelector('form').submit();
});

// HTML5 specifies that a <script> tag inserted with innerHTML should not execute
// https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#Security_considerations
// That's why we can't use just 'innerHTML'. See https://stackoverflow.com/a/47614491/2804294
let setInnerHTML = function (element, htmlContent) {
element.innerHTML = htmlContent;
Array.from(element.querySelectorAll('script')).forEach(oldScript => {
const newScript = document.createElement('script');
Array.from(oldScript.attributes)
.forEach(attr => newScript.setAttribute(attr.name, attr.value));
newScript.appendChild(document.createTextNode(oldScript.innerHTML));
oldScript.parentNode.replaceChild(newScript, oldScript);
});
};

let filterButton = document.querySelector('.action-filters-button');
filterButton.addEventListener('click', function (event) {
let filterModal = document.querySelector(filterButton.dataset.modal);
let filterModalBody = filterModal.querySelector('.modal-body');

$(filterModal).modal({backdrop: true, keyboard: true});
filterModalBody.innerHTML = '<div class="fa-3x px-3 py-3 text-muted text-center"><i class="fas fa-circle-notch fa-spin"></i></div>';

$.get(filterButton.getAttribute('href'), function (response) {
setInnerHTML(filterModalBody, response);
});

event.preventDefault();
event.stopPropagation();
});
filterButton.setAttribute('href', filterButton.getAttribute('data-href'));
filterButton.removeAttribute('data-href');
filterButton.classList.remove('disabled');
{% endif %}


{% if has_batch_actions %}
const titleContent = $('.content-header-title > .title').html();
$(document).on('click', '.deselect-batch-button', function () {
$(this).closest('.content').find(':checkbox.form-batch-checkbox-all').prop('checked', false).trigger('change');
});
$(document).on('change', '.form-batch-checkbox-all', function () {
$(this).closest('.content').find(':checkbox.form-batch-checkbox').prop('checked', $(this).prop('checked')).trigger('change');
});
$(document).on('change', '.form-batch-checkbox', function () {
const $content = $(this).closest('.content');
let $input = $content.find(':hidden#batch_form_entityIds');
let ids = $input.val() ? $input.val().split(',') : [];
const id = $(this).val();

if ($(this).prop('checked')) {
$(this).closest('tr').addClass('selected-row');
if (-1 === ids.indexOf(id)) {
ids.push(id);
}
} else {
$(this).closest('tr').removeClass('selected-row');
ids = ids.filter(function (value) {
return value !== id
});
$content.find(':checkbox.form-batch-checkbox-all').prop('checked', false);
}

if (0 === ids.length) {
$content.find('.global-actions').show();
$content.find('.batch-actions').hide();
$content.find('table').removeClass('table-batch');
} else {
$content.find('.batch-actions').show();
$content.find('.global-actions').hide();
$content.find('table').addClass('table-batch');
}

$input.val(ids.join(','));
$content.find('.content-header-title > .title').html(0 === ids.length ? titleContent : '');
});

let modalTitle = $('#batch-action-confirmation-title');
const titleContentWithPlaceholders = modalTitle.text();

$('[data-action-batch]').on('click', function (event) {
event.preventDefault();
event.stopPropagation();
let $actionElement = $(this);

const actionName = $actionElement.attr('data-action-name');
const selectedItems = $('input[type="checkbox"].form-batch-checkbox:checked');
modalTitle.text(titleContentWithPlaceholders
.replace('%action_name%', actionName)
.replace('%num_items%', selectedItems.length));

$('#modal-batch-action').modal({backdrop: true, keyboard: true})
.off('click', '#modal-batch-action-button')
.on('click', '#modal-batch-action-button', function () {
$actionElement.unbind('click');

$form = document.createElement('form');
$form.setAttribute('action', $actionElement.attr('data-action-url'));
$form.setAttribute('method', 'POST');

$actionNameInput = document.createElement('input');
$actionNameInput.setAttribute('type', 'hidden');
$actionNameInput.setAttribute('name', 'batchActionName');
$actionNameInput.setAttribute('value', $actionElement.attr('data-action-name'));
$form.appendChild($actionNameInput);

$entityFqcnInput = document.createElement('input');
$entityFqcnInput.setAttribute('type', 'hidden');
$entityFqcnInput.setAttribute('name', 'entityFqcn');
$entityFqcnInput.setAttribute('value', $actionElement.attr('data-entity-fqcn'));
$form.appendChild($entityFqcnInput);

$actionUrlInput = document.createElement('input');
$actionUrlInput.setAttribute('type', 'hidden');
$actionUrlInput.setAttribute('name', 'batchActionUrl');
$actionUrlInput.setAttribute('value', $actionElement.attr('data-action-url'));
$form.appendChild($actionUrlInput);

$csrfTokenInput = document.createElement('input');
$csrfTokenInput.setAttribute('type', 'hidden');
$csrfTokenInput.setAttribute('name', 'batchActionCsrfToken');
$csrfTokenInput.setAttribute('value', $actionElement.attr('data-action-csrf-token'));
$form.appendChild($csrfTokenInput);

selectedItems.each((i, item) => {
$entityIdInput = document.createElement('input');
$entityIdInput.setAttribute('type', 'hidden');
$entityIdInput.setAttribute('name', `batchActionEntityIds[${i}]`);
$entityIdInput.setAttribute('value', item.value);
$form.appendChild($entityIdInput);
});

document.body.appendChild($form);

//modalTitle.text(titleContentWithPlaceholders);
$form.submit();
});
});
{% endif %}
});
</script>

{% if app.request.get('query') is not empty %}
<script type="text/javascript">
const search_query = "{{ ea.search.query|default('')|e('js') }}";
// the original query is prepended to allow matching exact phrases in addition to single words
$('#main').find('table tbody td:not(.actions)').highlight($.merge([search_query], search_query.split(' ')));
</script>
{% endif %}
{% endblock %}

+ 0
- 189
Resources/views/tabler/crud/new.html.twig View File

@@ -1,189 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% extends ea.templatePath('layout') %}
{% form_theme new_form with ea.crud.formThemes only %}

{% trans_default_domain ea.i18n.translationDomain %}

{% block body_id 'ea-new-' ~ entity.name ~ '-' ~ entity.primaryKeyValue %}
{% block body_class 'ea-new ea-new-' ~ entity.name %}

{% block configured_head_contents %}
{{ parent() }}
{% for htmlContent in new_form.vars.ea_crud_form.assets.headContents %}
{{ htmlContent|raw }}
{% endfor %}
{% endblock %}

{% block configured_stylesheets %}
{{ parent() }}
{% for css_asset in new_form.vars.ea_crud_form.assets.cssFiles %}
<link rel="stylesheet" href="{{ asset(css_asset) }}">
{% endfor %}

{% for webpack_encore_entry in new_form.vars.ea_crud_form.assets.webpackEncoreEntries %}
{{ ea_call_function_if_exists('encore_entry_link_tags', webpack_encore_entry) }}
{% endfor %}
{% endblock %}

{% block configured_javascripts %}
{{ parent() }}
{% for js_asset in new_form.vars.ea_crud_form.assets.jsFiles %}
<script src="{{ asset(js_asset) }}"></script>
{% endfor %}

{% for webpack_encore_entry in new_form.vars.ea_crud_form.assets.webpackEncoreEntries %}
{{ ea_call_function_if_exists('encore_entry_script_tags', webpack_encore_entry) }}
{% endfor %}
{% endblock %}

{% block content_title %}
{# {%- apply spaceless -%}
{% set default_title = ea.crud.defaultPageTitle('new')|trans(ea.i18n.translationParameters, 'EasyAdminBundle') %}
{{ ea.crud.customPageTitle is null ? default_title|raw : ea.crud.customPageTitle('new')|trans(ea.i18n.translationParameters)|raw }}
{%- endapply -%}#}
{% endblock %}

{% block content_header_wrapper %}
{# {% for action in entity.actions %}
{{ include(action.templatePath, { action: action }, with_context = false) }}
{% endfor %}#}
{% endblock %}

{% block main %}
<div class="col-8">
<div class="card">
<div class="card-status-top bg-primary"></div>
<div class="card-header ">
{% set default_title = ea.crud.defaultPageTitle('new')|trans(ea.i18n.translationParameters, 'EasyAdminBundle') %}

<h2 class="card-title">{{ ea.crud.customPageTitle is null ? default_title|raw : ea.crud.customPageTitle('new')|trans(ea.i18n.translationParameters)|raw }}</h2>
</div>
<div class="card-body">
<button type="button" data-bs-toggle="modal" data-bs-target="#niche">Launch modal</button>

<div class="modal-dialog" role="document" id="niche">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci animi beatae delectus deleniti dolorem eveniet facere fuga iste nemo nesciunt nihil odio perspiciatis, quia quis reprehenderit sit tempora totam unde.
</div>
<div class="modal-footer">
<button type="button" class="btn me-auto" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-bs-dismiss="modal">Save changes</button>
</div>
</div>
</div>


{% block new_form %}
{{ form(new_form) }}
{% endblock new_form %}
</div>
</div>
</div>
{% endblock %}

{% block body_javascript %}
{{ parent() }}

{# <script type="text/javascript">
$(function () {
$('.ea-new-form').areYouSure({'message': '{{ 'form.are_you_sure'|trans({}, 'EasyAdminBundle')|e('js') }}'});

const entityForm = document.querySelector('form.ea-new-form');
const inputFieldsSelector = 'input,select,textarea';

// Adding visual feedback for invalid fields: any ".form-group" with invalid fields
// receives "has-error" class. The class is removed on click on the ".form-group"
// itself to support custom/complex fields.
entityForm.addEventListener('submit', function (submitEvent) {
entityForm.querySelectorAll(inputFieldsSelector).forEach(function (input) {
if (!input.validity.valid) {
const formGroup = input.closest('div.form-group');

formGroup.classList.add('has-error');

formGroup.addEventListener('click', function onFormGroupClick() {
formGroup.classList.remove('has-error');
formGroup.removeEventListener('click', onFormGroupClick);
});
}
});

const eaEvent = new CustomEvent('ea.form.submit', {
cancelable: true,
detail: {page: 'new', form: entityForm}
});
const eaEventResult = document.dispatchEvent(eaEvent);
if (false === eaEventResult) {
submitEvent.preventDefault();
submitEvent.stopPropagation();
}
});

// forms with tabs require some special treatment for errors. The problem
// is when the field with errors is included in a tab not currently visible.
// Browser shows this error "An invalid form control with name='...' is not focusable."
// So, the user clicks on Submit button, the form is not submitted and the error
// is not displayed. This JavaScript code ensures that each tab shows a badge with
// the number of errors in it.
entityForm.addEventListener('submit', function () {
const formTabPanes = entityForm.querySelectorAll('.tab-pane');
if (0 === formTabPanes.length) {
return;
}

let firstNavTabItemWithError = null;

formTabPanes.forEach(function (tabPane) {
let tabPaneNumErrors = 0;
tabPane.querySelectorAll(inputFieldsSelector).forEach(function (input) {
if (!input.validity.valid) {
tabPaneNumErrors++;
}
});

let navTabItem = entityForm.querySelector('.nav-item a[href="#' + tabPane.id + '"]');
let existingErrorBadge = navTabItem.querySelector('span.badge.badge-danger');
if (null !== existingErrorBadge) {
navTabItem.removeChild(existingErrorBadge);
}

if (tabPaneNumErrors > 0) {
let newErrorBadge = document.createElement('span');
newErrorBadge.classList.add('badge', 'badge-danger');
newErrorBadge.title = 'form.tab.error_badge_title';
newErrorBadge.textContent = tabPaneNumErrors;

navTabItem.appendChild(newErrorBadge);

if (null === firstNavTabItemWithError) {
firstNavTabItemWithError = navTabItem;
}
}
});

if (firstNavTabItemWithError) {
firstNavTabItemWithError.click();
}
});

// prevent multiple form submissions to avoid creating duplicated entities
entityForm.addEventListener('submit', function () {
// this timeout is needed to include the disabled button into the submitted form
setTimeout(function () {
const submitButtons = entityForm.querySelectorAll('[type="submit"]');
submitButtons.forEach(function (button) {
button.setAttribute('disabled', 'disabled');
});
}, 1);
}, false);
});
</script>

{{ include('@EasyAdmin/crud/includes/_select2_widget.html.twig') }}#}
{% endblock %}

+ 0
- 31
Resources/views/tabler/crud/paginator.html.twig View File

@@ -1,31 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var paginator \EasyCorp\Bundle\EasyAdminBundle\Orm\EntityPaginator #}
{% trans_default_domain 'EasyAdminBundle' %}

<p class="m-0 text-muted">
{{ 'paginator.results'|trans({'%count%': paginator.numResults})|raw }}
</p>
<ul class="pagination m-0 ms-auto">
<li class="page-item {{ not paginator.hasPreviousPage ? 'disabled' }}">
<a class="page-link" href="{{ not paginator.hasPreviousPage ? '#' : paginator.generateUrlForPage(paginator.previousPage) }}" {{ not paginator.hasPreviousPage ? 'aria-disabled="true"' }}>
<i class="ti ti-arrow-left"></i> <span class="btn-label">{{ 'paginator.previous'|trans }}</span>
</a>
</li>

{% for page in paginator.pageRange %}
<li class="page-item {{ page == paginator.currentPage ? 'active' }} {{ page is null ? 'disabled' }}">
{% if page is null %}
<span class="page-link">&hellip;</span>
{% else %}
<a class="page-link" href="{{ paginator.generateUrlForPage(page) }}">{{ page }}</a>
{% endif %}
</li>
{% endfor %}

<li class="page-item {{ not paginator.hasNextPage ? 'disabled' }}">
<a class="page-link" href="{{ not paginator.hasNextPage ? '#' : paginator.generateUrlForPage(paginator.nextPage) }}" {{ not paginator.hasNextPage ? 'aria-disabled="true"' }}>
<span class="btn-label">{{ 'paginator.next'|trans }}</span> <i class="ti ti-arrow-right"></i>
</a>
</li>
</ul>


+ 0
- 10
Resources/views/tabler/dashboard.html.twig View File

@@ -1,10 +0,0 @@
{% extends '@LcSov/tabler/layout.html.twig' %}

{% block content_title 'SovBundle' %}

{% block main %}

Bien sur votre tableau de bord
Salut !!!
{% endblock %}


+ 0
- 254
Resources/views/tabler/layout.html.twig View File

@@ -1,254 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{% trans_default_domain ea.i18n.translationDomain %}

<!DOCTYPE html>
<html lang="{{ ea.i18n.htmlLocale }}" dir="{{ ea.i18n.textDirection }}">
<head>
{% block head_metas %}
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="robots"
content="noindex, nofollow, noarchive, nosnippet, noodp, noimageindex, notranslate, nocache"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<meta name="generator" content="EasyAdmin"/>
{% endblock head_metas %}

<title>{% block page_title %}{{ block('content_title')|striptags|raw }}{% endblock %}</title>

{# {% block head_stylesheets %}
<link rel="stylesheet" href="{{ asset('bundles/easyadmin/app.css') }}">
{% endblock %} #}

{% block configured_stylesheets %}
{% for css_asset in ea.assets.cssFiles ?? [] %}
<link rel="stylesheet" href="{{ asset(css_asset) }}">
{% endfor %}

{% for webpack_encore_entry in ea.assets.webpackEncoreEntries ?? [] %}
{{ ea_call_function_if_exists('encore_entry_link_tags', webpack_encore_entry) }}
{% endfor %}
{% endblock %}

{% block head_favicon %}
<link rel="shortcut icon" href="{{ asset(ea.dashboardFaviconPath) }}">
{% endblock %}

{# {% block head_javascript %}
<script src="{{ asset('bundles/easyadmin/app.js') }}"></script>
{% endblock head_javascript %} #}



{# {% if 'rtl' == ea.i18n.textDirection %}
<link rel="stylesheet" href="{{ asset('bundles/easyadmin/app.rtl.css') }}">
<link rel="stylesheet" href="{{ asset('bundles/easyadmin/app-custom-rtl.css') }}">
{% endif %} #}

{% block configured_head_contents %}
{% for htmlContent in ea.assets.headContents ?? [] %}
{{ htmlContent|raw }}
{% endfor %}
{% endblock %}
</head>

{% block body %}
<body id="{% block body_id %}{% endblock %}" class="antialiased {% block body_class %}{% endblock %}">
{% block javascript_page_layout %}
<script>
document.body.classList.add(
'ea-content-width-' + (localStorage.getItem('ea/content/width') || '{{ ea.crud.contentWidth ?? ea.dashboardContentWidth ?? 'normal' }}'),
'ea-sidebar-width-' + (localStorage.getItem('ea/sidebar/width') || '{{ ea.crud.sidebarWidth ?? ea.dashboardSidebarWidth ?? 'normal' }}')
);
</script>
{% endblock javascript_page_layout %}

{% block wrapper_wrapper %}
{% block flash_messages %}
{{ include(ea.templatePath('flash_messages')) }}
{% endblock flash_messages %}

<div class="wrapper">
TABLER
{% block wrapper %}

{# <header class="main-header">
{% block header %}
<nav class="navbar" role="navigation">
{% block header_navbar %}
<button id="navigation-toggler" type="button" aria-label="Toggle navigation">
<i class="fa fa-fw fa-bars"></i>
</button>
{% endblock header_navbar %}
</nav>

</header> #}

<aside class="navbar navbar-vertical navbar-expand-lg navbar-dark">
<div class="container-fluid">
{% block sidebar %}

{% block header_logo %}
<h1 class="navbar-brand navbar-brand-autodark">
<a class="{{ ea.dashboardTitle|length > 14 ? 'logo-long' }}"
title="{{ ea.dashboardTitle|striptags }}"
href="{{ path(ea.dashboardRouteName) }}">
{{ ea.dashboardTitle|raw }}
</a>
</h1>
{% endblock header_logo %}

<section class="sidebar">
{% block main_menu_wrapper %}
{{ include(ea.templatePath('main_menu')) }}
{% endblock main_menu_wrapper %}
</section>

{% endblock sidebar %}
</div>
</aside>

<div class="page-wrapper">

<div class="sticky-top">
<header class="navbar navbar-expand-md navbar-light sticky-top d-print-none">
<div class="container-xl">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbar-menu">
<span class="navbar-toggler-icon"></span>
</button>
<span class="push-menu">
<i class="ti ti-antenna-bars-1"></i>
</span>
<div class="navbar-nav flex-row order-md-last">
<div class="nav-item d-none d-md-flex me-3">
<div class="btn-list">
{#<a href="{{ path(homepage_route()) }}" class="btn btn-outline-white" target="_blank" rel="noreferrer">
Afficher le site
</a>#}
</div>
</div>

<div class="nav-item dropdown">
<a href="#" class="nav-link d-flex lh-1 text-reset p-0"
data-bs-toggle="dropdown"
aria-label="Open user menu">
<span class="ti ti-user"></span>
<div class="d-none d-xl-block ps-2">
<div>{{ ea.user is null ? 'user.anonymous'|trans(domain = 'EasyAdminBundle') : ea.userMenu.name }}</div>
{% if is_granted('ROLE_SUPER_ADMIN') %}
<div class="mt-1 small text-muted">Superadmin</div>
{% elseif is_granted('ROLE_ADMIN') %}
<div class="mt-1 small text-muted">Admin</div>
{% else %}
<div class="mt-1 small text-muted">NC</div>
{% endif %}
</div>
</a>
{% block user_menu %}
{% if ea.userMenu.items|length > 0 %}
<div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
{% for item in ea.userMenu.items %}
{% if item.isMenuSection %}
<hr class="m-0"/>
{% else %}
<a href="{{ item.linkUrl }}"
class="dropdown-item {{ item.cssClass }}"
target="{{ item.linkTarget }}" rel="{{ item.linkRel }}"
referrerpolicy="origin-when-cross-origin">
{% if item.icon is not empty %}
<i class="ta ta-{{ item.icon }}"></i>
{% endif %}
<span>{{ item.label }}</span>
</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% endblock user_menu %}
</div>
</div>
</div>
</header>
</div>


{% block content %}
{% block content_header_wrapper %}
<div class="container-xl">
<div class="page-header d-print-none">
<div class="row align-items-center">
{% set has_help_message = (ea.crud.helpMessage ?? '') is not empty %}
{% block content_header %}
<div class="col">
<h2 class="page-title">
{% block content_title %}{% endblock %}
</h2>
{% block content_help %}
{% if has_help_message %}
<div class="text-muted">
{{ ea.crud.helpMessage|e('html_attr') }}
</div>
{% endif %}
{% endblock %}
</div>

{% block page_actions_wrapper %}
<div class="col-auto ms-auto d-print-none">
<div class="btn-list">
{% block page_actions %}{% endblock %}
</div>
</div>
{% endblock %}

{% endblock %}
</div>
</div>
</div>
{% endblock %}


<div class="page-body">
<div class="container-xl">
{% block main %}{% endblock %}
</div>
</div>

{% block content_footer_wrapper %}
{% set content_footer = block('content_footer') is defined ? block('content_footer') : '' %}
{% if content_footer is not empty %}
<footer class="footer footer-transparent d-print-none">
<div class="container">
<div class="row text-center align-items-center flex-row-reverse">
{{ content_footer }}
</div>
</div>
</footer>
{% endif %}
{% endblock %}

{% endblock %}
</div>
{% endblock wrapper %}
</div>
{% endblock wrapper_wrapper %}

{% block configured_javascripts %}
{% for js_asset in ea.assets.jsFiles ?? [] %}
<script src="{{ asset(js_asset) }}"></script>
{% endfor %}

{% for webpack_encore_entry in ea.assets.webpackEncoreEntries ?? [] %}
{{ ea_call_function_if_exists('encore_entry_script_tags', webpack_encore_entry) }}
{% endfor %}
{% endblock %}

{% block body_javascript %}{% endblock body_javascript %}

{% block configured_body_contents %}
{% for htmlContent in ea.assets.bodyContents ?? [] %}
{{ htmlContent|raw }}
{% endfor %}
{% endblock %}
</body>
{% endblock body %}
</html>

+ 0
- 60
Resources/views/tabler/menu.html.twig View File

@@ -1,60 +0,0 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{% macro render_menu_item(item, is_sub_item = false) %}
{% if item.isMenuSection %}
{% if item.icon is not empty %}
<span class="nav-link-icon d-md-none d-lg-inline-block">
<i class="ti ti-{{ item.icon }}"></i>
</span>
{% endif %}
<span class="nav-link-title {{ item.cssClass }}">{{ item.label }}</span>
{% else %}
<a href="{{ item.linkUrl }}" class="{{ is_sub_item ? 'dropdown-item' : 'nav-link' }} {{ item.cssClass }} {{ item.hasSubItems ? 'dropdown-toggle' }}"
target="{{ item.linkTarget }}" rel="{{ item.linkRel }}" referrerpolicy="origin-when-cross-origin"
{{ item.hasSubItems ? 'data-bs-toggle="dropdown" role="button" aria-expanded="false"' }} >
{% if item.icon is not empty %}
<span class="nav-link-icon d-md-none d-lg-inline-block">
<i class="ti ti-{{ item.icon }}"></i>
</span>
{% endif %}
<span class="nav-link-title">{{ item.label|raw }}</span>
{% if item.hasSubItems %}<i class="fa fa-fw fa-angle-right treeview-icon"></i>{% endif %}
</a>
{% endif %}
{% endmacro %}

{% block main_menu_before %}{% endblock %}

<div class="collapse navbar-collapse" id="navbar-menu">
<ul class="navbar-nav pt-lg-3">
{% block main_menu %}
{% for menuItem in ea.mainMenu.items %}
{% block menu_item %}
<li class="nav-item {{ menuItem.isMenuSection ? 'header' }} {{ menuItem.hasSubItems ? 'dropdown' }} {{ ea.mainMenu.isSelected(menuItem) ? 'active' }} {{ ea.mainMenu.isExpanded(menuItem) ? 'active submenu-active' }}">
{{ _self.render_menu_item(menuItem) }}

{% if menuItem.hasSubItems %}
<div class="dropdown-menu">
<div class="dropdown-menu-columns">
<div class="dropdown-menu-column">
{% for menuSubItem in menuItem.subItems %}
{% block menu_subitem %}
{{ _self.render_menu_item(menuSubItem, true) }}
{% endblock menu_subitem %}
{% endfor %}
</div>
</div>
</div>
{% endif %}
</li>
{% endblock menu_item %}
{% endfor %}
{% endblock main_menu %}
</ul>
</div>

{% block main_menu_after %}{% endblock %}


<!-- <a class="dropdown-item" href="./empty.html">
Empty page
</a> -->

+ 19
- 0
Resources/views/user/change_password.html.twig View File

@@ -0,0 +1,19 @@

{% extends '@LcSov/adminlte/layout.html.twig' %}

{% block content_title %}
Changer de mot de passe
{% 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 %}

+ 18
- 0
Resources/views/user/profile.html.twig View File

@@ -0,0 +1,18 @@
{% extends '@LcSov/adminlte/layout.html.twig' %}

{% block content_title %}
Mes informations personnelles
{% 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 %}

+ 5
- 0
Resources/views/user/request_password.html.twig View File

@@ -0,0 +1,5 @@
{% extends '@LcSov/adminlte/layout.html.twig' %}

{% block main %}
Demander un nouveau mot de passe
{% endblock main %}

Loading…
Cancel
Save