This commit is contained in:
Tykayn 2025-02-14 15:35:03 +01:00 committed by tykayn
parent d8c1b7c0f6
commit d01ecdabba
13 changed files with 198 additions and 25 deletions

View file

@ -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 {

View file

@ -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:

View file

@ -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' ]

View 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',
]);
}
}

View file

@ -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');
} }
} }

View file

@ -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')]

View 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'],
]));
}
}
}

View 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é',
],
],
];
}
}

View 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());
}
}

View file

@ -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-->

View file

@ -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">

View file

@ -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>

View 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 %}