mirror of
https://forge.chapril.org/tykayn/caisse-bliss
synced 2025-06-20 01:44:42 +02:00
up login
This commit is contained in:
parent
d8c1b7c0f6
commit
d01ecdabba
13 changed files with 198 additions and 25 deletions
|
@ -9,6 +9,11 @@ body {
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.masthead-avatar {
|
||||||
|
width: 2rem;
|
||||||
|
max-height: 2rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
#wrapper {
|
#wrapper {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
@ -30,6 +35,7 @@ body {
|
||||||
.bg-img {
|
.bg-img {
|
||||||
background-attachment: fixed;
|
background-attachment: fixed;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
|
background-repeat: no-repeat;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
@ -37,12 +43,13 @@ body {
|
||||||
|
|
||||||
.bg-accessories {
|
.bg-accessories {
|
||||||
@extend .bg-img;
|
@extend .bg-img;
|
||||||
// background-image: url('img/accessories.jpg');
|
// background-image: url('assets/img/accessories.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-girl {
|
.bg-girl {
|
||||||
@extend .bg-img;
|
@extend .bg-img;
|
||||||
// background-image: url('img/girl_computer.jpg');
|
// background-image: url('../img/girl_computer.jpg');
|
||||||
|
// background-image: url('assets/img/girl_computer.jpg');
|
||||||
}
|
}
|
||||||
|
|
||||||
.bg-color {
|
.bg-color {
|
||||||
|
|
|
@ -1,33 +1,36 @@
|
||||||
|
# config/packages/security.yaml
|
||||||
security:
|
security:
|
||||||
# https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords
|
# Hashers pour les mots de passe
|
||||||
password_hashers:
|
password_hashers:
|
||||||
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
|
App\Entity\User:
|
||||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
algorithm: auto
|
||||||
|
|
||||||
|
# Fournisseurs d'utilisateurs
|
||||||
providers:
|
providers:
|
||||||
# used to reload user from session & other features (e.g. switch_user)
|
|
||||||
app_user_provider:
|
app_user_provider:
|
||||||
entity:
|
entity:
|
||||||
class: App\Entity\User
|
class: App\Entity\User
|
||||||
property: email
|
property: email
|
||||||
|
|
||||||
|
# Firewalls
|
||||||
firewalls:
|
firewalls:
|
||||||
dev:
|
dev:
|
||||||
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
pattern: ^/(_(profiler|wdt)|css|images|js)/
|
||||||
security: false
|
security: false
|
||||||
main:
|
main:
|
||||||
lazy: true
|
form_login:
|
||||||
provider: app_user_provider
|
# "app_login" is the name of the route created previously
|
||||||
|
login_path: app_login
|
||||||
|
check_path: app_login
|
||||||
|
# access_token:
|
||||||
|
# token_handler: App\Security\AccessTokenHandler
|
||||||
|
|
||||||
# activate different ways to authenticate
|
# Contrôle d'accès
|
||||||
# https://symfony.com/doc/current/security.html#the-firewall
|
|
||||||
|
|
||||||
# https://symfony.com/doc/current/security/impersonating_user.html
|
|
||||||
# switch_user: true
|
|
||||||
|
|
||||||
# Easy way to control access for large sections of your site
|
|
||||||
# Note: Only the *first* access control that matches will be used
|
|
||||||
access_control:
|
access_control:
|
||||||
- { path: ^/admin, roles: ROLE_ADMIN }
|
- { path: ^/admin, roles: ROLE_ADMIN }
|
||||||
- { path: ^/logged, roles: ROLE_USER }
|
- { path: ^/logged, roles: ROLE_USER }
|
||||||
|
# - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Autoriser l'accès à la page de connexion
|
||||||
|
# - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY } # Autoriser l'accès anonyme à toutes les autres pages
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
security:
|
security:
|
||||||
|
|
|
@ -25,3 +25,7 @@ services:
|
||||||
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
|
Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler:
|
||||||
arguments:
|
arguments:
|
||||||
- '%env(DATABASE_URL)%'
|
- '%env(DATABASE_URL)%'
|
||||||
|
|
||||||
|
App\Filter\UserProductsFilter:
|
||||||
|
arguments: [ '@security.helper' ]
|
||||||
|
tags: [ 'api_platform.filter' ]
|
18
src/Controller/LoginController.php
Normal file
18
src/Controller/LoginController.php
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
|
||||||
|
final class LoginController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/login', name: 'app_login')]
|
||||||
|
public function index(): Response
|
||||||
|
{
|
||||||
|
return $this->render('login/index.html.twig', [
|
||||||
|
'controller_name' => 'LoginController',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,8 +25,9 @@ class SecurityController extends AbstractController
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Route(path: '/logout', name: 'app_logout')]
|
#[Route(path: '/logout', name: 'app_logout')]
|
||||||
public function logout(): void
|
public function logout(): Response
|
||||||
{
|
{
|
||||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
return $this->redirectToRoute('app_default');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,17 @@
|
||||||
namespace App\Entity;
|
namespace App\Entity;
|
||||||
|
|
||||||
use ApiPlatform\Metadata\ApiResource;
|
use ApiPlatform\Metadata\ApiResource;
|
||||||
|
use ApiPlatform\Metadata\ApiProperty;
|
||||||
|
|
||||||
use App\Repository\ProductRepository;
|
use App\Repository\ProductRepository;
|
||||||
use Doctrine\Common\Collections\ArrayCollection;
|
use Doctrine\Common\Collections\ArrayCollection;
|
||||||
use Doctrine\Common\Collections\Collection;
|
use Doctrine\Common\Collections\Collection;
|
||||||
use Doctrine\ORM\Mapping as ORM;
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use App\Filter\UserProductsFilter;
|
||||||
|
use App\DTO\UserDTO;
|
||||||
|
|
||||||
#[ApiResource(paginationEnabled: false)]
|
#[ApiResource(paginationEnabled: false)]
|
||||||
|
#[UserProductsFilter]
|
||||||
#[ORM\Entity(repositoryClass: ProductRepository::class)]
|
#[ORM\Entity(repositoryClass: ProductRepository::class)]
|
||||||
class Product
|
class Product
|
||||||
{
|
{
|
||||||
|
@ -30,12 +35,14 @@ class Product
|
||||||
* @var Collection<int, GroupOfProducts>
|
* @var Collection<int, GroupOfProducts>
|
||||||
*/
|
*/
|
||||||
#[ORM\ManyToMany(targetEntity: GroupOfProducts::class, mappedBy: 'products')]
|
#[ORM\ManyToMany(targetEntity: GroupOfProducts::class, mappedBy: 'products')]
|
||||||
|
#[ApiProperty(readable: false)]
|
||||||
private Collection $groupOfProducts;
|
private Collection $groupOfProducts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var Collection<int, Selling>
|
* @var Collection<int, Selling>
|
||||||
*/
|
*/
|
||||||
#[ORM\ManyToMany(targetEntity: Selling::class, mappedBy: 'products')]
|
#[ORM\ManyToMany(targetEntity: Selling::class, mappedBy: 'products')]
|
||||||
|
#[ApiProperty(readable: false)]
|
||||||
private Collection $sellings;
|
private Collection $sellings;
|
||||||
|
|
||||||
#[ORM\ManyToOne(inversedBy: 'products')]
|
#[ORM\ManyToOne(inversedBy: 'products')]
|
||||||
|
|
27
src/EventSubscriber/NotShownCountSubscriber.php
Normal file
27
src/EventSubscriber/NotShownCountSubscriber.php
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<?php
|
||||||
|
namespace App\EventSubscriber;
|
||||||
|
|
||||||
|
use ApiPlatform\Symfony\EventListener\EventPriorities;
|
||||||
|
use ApiPlatform\EventListener\FilterCollectionEvent;
|
||||||
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
|
||||||
|
final class NotShownCountSubscriber implements EventSubscriberInterface
|
||||||
|
{
|
||||||
|
public static function getSubscribedEvents(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
FilterCollectionEvent::class => ['addNotShownCount', EventPriorities::PRE_WRITE],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addNotShownCount(FilterCollectionEvent $event): void
|
||||||
|
{
|
||||||
|
$context = $event->getContext();
|
||||||
|
if (isset($context['notShownCount'])) {
|
||||||
|
$event->getResponse()->setData(array_merge($event->getResponse()->getData(), [
|
||||||
|
'notShownCount' => $context['notShownCount'],
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
src/Filter/UserProductsFilter.php
Normal file
52
src/Filter/UserProductsFilter.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* filtrer les objets selon ceux qui appartiennent uniquement à l'utilisateur connecté
|
||||||
|
*/
|
||||||
|
namespace App\Filter;
|
||||||
|
|
||||||
|
use ApiPlatform\Doctrine\Orm\Filter\AbstractFilter;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface;
|
||||||
|
use Symfony\Component\Security\Core\Security;
|
||||||
|
use ApiPlatform\Metadata\Operation;
|
||||||
|
use ApiPlatform\Metadata\FilterInterface;
|
||||||
|
|
||||||
|
final class UserProductsFilter extends AbstractFilter implements FilterInterface
|
||||||
|
{
|
||||||
|
private $security;
|
||||||
|
|
||||||
|
public function __construct(Security $security)
|
||||||
|
{
|
||||||
|
$this->security = $security;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function filterProperty(string $property, $value, QueryBuilder $queryBuilder, QueryNameGeneratorInterface $queryNameGenerator, string $resourceClass, ?Operation $operation = null, array $context = []): void
|
||||||
|
{
|
||||||
|
if ($property === 'user' && $this->security->isGranted('ROLE_USER')) {
|
||||||
|
$user = $this->security->getUser();
|
||||||
|
$queryBuilder->andWhere('o.user = :user')->setParameter('user', $user);
|
||||||
|
|
||||||
|
// Compter le nombre total de produits pour l'utilisateur
|
||||||
|
$totalProducts = $queryBuilder->getEntityManager()->getRepository('App\Entity\Product')->count(['user' => $user]);
|
||||||
|
$filteredProducts = $queryBuilder->getQuery()->getResult();
|
||||||
|
$notShownCount = $totalProducts - count($filteredProducts);
|
||||||
|
|
||||||
|
// Ajouter le nombre de produits non montrés au contexte
|
||||||
|
$context['notShownCount'] = $notShownCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(string $resourceClass): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'user' => [
|
||||||
|
'property' => 'user',
|
||||||
|
'type' => 'string',
|
||||||
|
'required' => false,
|
||||||
|
'swagger' => [
|
||||||
|
'description' => 'Filtrer les produits par utilisateur connecté',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
30
src/Security/AccessTokenHandler.php
Normal file
30
src/Security/AccessTokenHandler.php
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Security;
|
||||||
|
|
||||||
|
use App\Repository\AccessTokenRepository;
|
||||||
|
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
|
||||||
|
use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface;
|
||||||
|
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
|
||||||
|
|
||||||
|
class AccessTokenHandler implements AccessTokenHandlerInterface
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private AccessTokenRepository $repository
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserBadgeFrom(string $accessToken): UserBadge
|
||||||
|
{
|
||||||
|
// e.g. query the "access token" database to search for this token
|
||||||
|
$accessToken = $this->repository->findOneByValue($accessToken);
|
||||||
|
if (null === $accessToken || !$accessToken->isValid()) {
|
||||||
|
throw new BadCredentialsException('Invalid credentials.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// and return a UserBadge object containing the user identifier from the found token
|
||||||
|
// (this is the same identifier used in Security configuration; it can be an email,
|
||||||
|
// a UUID, a username, a database ID, etc.)
|
||||||
|
return new UserBadge($accessToken->getUserId());
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,13 +32,16 @@
|
||||||
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="/#contact">Contact</a></li>
|
<li class="nav-item mx-0 mx-lg-1"><a class="nav-link py-3 px-0 px-lg-3 rounded" href="/#contact">Contact</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
{% include 'default/login-choices.html.twig' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
<!-- Masthead-->
|
<!-- Masthead-->
|
||||||
<header class="masthead bg-primary text-white text-center">
|
<header class="masthead bg-primary text-white text-center">
|
||||||
<div class="container d-flex align-items-center flex-column">
|
<div class="container d-flex align-items-center flex-column">
|
||||||
<!-- Masthead Avatar Image-->
|
<!-- Masthead Avatar Image-->
|
||||||
<img class="masthead-avatar mb-5" src="assets/img/avataaars.svg" alt="..." />
|
<img class="masthead-avatar mb-5" width="2rem" src="/img/avataaars.svg" alt="..." />
|
||||||
<!-- Masthead Heading-->
|
<!-- Masthead Heading-->
|
||||||
<h1 class="masthead-heading text-uppercase mb-0">Caisse Bliss</h1>
|
<h1 class="masthead-heading text-uppercase mb-0">Caisse Bliss</h1>
|
||||||
<!-- Icon Divider-->
|
<!-- Icon Divider-->
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12">
|
<div class="col-xs-12">
|
||||||
|
|
||||||
{% include 'default/login-choices.html.twig' %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-elements">
|
<div class="nav-elements">
|
||||||
|
|
|
@ -7,15 +7,16 @@
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<div class="main-screen" id="homepage">
|
<div class="main-screen" id="homepage">
|
||||||
<div id="welcome">
|
<div id="welcome">
|
||||||
<section class="bg-accessories" style="background : url('img/accessories.jpg')">
|
<section class="bg-accessories"
|
||||||
|
style=" background-image: url('/img/accessories.jpg') ">
|
||||||
|
|
||||||
|
{# <img src="img/accessories.jpg" alt="accesoires">#}
|
||||||
<div class="bg-shader">
|
<div class="bg-shader">
|
||||||
<div class="container main-section">
|
<div class="container main-section">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xs-12 padded-v">
|
<div class="col-xs-12 padded-v">
|
||||||
<h1 class="text-center">
|
<h1 class="text-center">
|
||||||
{#<i class="fa fa-circle-o-notch logo-main"></i>#}
|
<i class="fa fa-circle-o-notch logo-main"></i>
|
||||||
{% trans %}menu.title{% endtrans %}
|
{% trans %}menu.title{% endtrans %}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
20
templates/login/index.html.twig
Normal file
20
templates/login/index.html.twig
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Hello LoginController!{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<style>
|
||||||
|
.example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
|
||||||
|
.example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="example-wrapper">
|
||||||
|
<h1>Hello {{ controller_name }}! ✅</h1>
|
||||||
|
|
||||||
|
This friendly message is coming from:
|
||||||
|
<ul>
|
||||||
|
<li>Your controller at <code>/home/poule/encrypted/stockage-syncable/www/development/html/caisse-bliss/src/Controller/LoginController.php</code></li>
|
||||||
|
<li>Your template at <code>/home/poule/encrypted/stockage-syncable/www/development/html/caisse-bliss/templates/login/index.html.twig</code></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue