Compare commits

..

1 Commits

Author SHA1 Message Date
0d9988cbda Update dunglas/frankenphp Docker digest to 462c2d0
Some checks failed
Apply PHP CS Fixer / php-cs-fixer (push) Failing after 22s
CI / build-test (push) Failing after 1m33s
rector / Rector (pull_request) Successful in 1m35s
rector / Rector (push) Successful in 1m39s
2025-02-20 00:02:08 +00:00
16 changed files with 39 additions and 376 deletions

7
.env
View File

@ -15,9 +15,8 @@
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=prod
APP_ENV=dev
APP_SECRET=
APP_DEBUG=false
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
@ -38,10 +37,10 @@ MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=0
###< symfony/messenger ###
###> symfony/mailer ###
MAILER_DSN=sendmail://default
MAILER_DSN=null://null
###< symfony/mailer ###
BASE_PREFIX=kumora
BASE_PREFIX=
DEFAULT_IMAGE=https://camelia-studio.org/v5/images/camelia_studio.png
APP_VERSION=1.0.0
GIT_URL=https://git.crystalyx.net/camelia-studio/Kumora

View File

@ -1,4 +1,4 @@
FROM dunglas/frankenphp@sha256:bc16b2c6900748ffd951b751a0798dba6a13ffa22ed4c793cf460ca0be4bc446
FROM dunglas/frankenphp@sha256:462c2d0eb8dd4fae16b7396f9873e0d52aab5c62d1abd0f1303bcf05041eed4e
ENV SERVER_NAME=":80"

View File

@ -1,14 +1,11 @@
@import 'tailwindcss';
@import "flowbite/src/themes/default";
@plugin "flowbite/plugin";
@plugin 'flowbite/plugin';
@source '../../vendor/tales-from-a-dev/flowbite-bundle/templates/**/*.html.twig';
@source "../node_modules/flowbite";
@custom-variant dark (@media (prefers-color-scheme: dark));
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still

View File

@ -44,7 +44,7 @@
"symfony/twig-bundle": "7.2.*",
"symfony/uid": "7.2.*",
"symfony/ux-dropzone": "^2.23.0",
"symfony/ux-icons": "^2.23",
"symfony/ux-icons": "^2.23.0",
"symfony/ux-turbo": "^2.23.0",
"symfony/ux-twig-component": "^2.23.0",
"symfony/validator": "7.2.*",

28
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "72ea41f1f7c8eb87e441882a22736fba",
"content-hash": "03d94385bcbb64b3bf80357c56e137b0",
"packages": [
{
"name": "composer/semver",
@ -1987,16 +1987,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "2.1.0",
"version": "2.0.2",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68"
"reference": "51087f87dcce2663e1fed4dfd4e56eccd580297e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68",
"reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51087f87dcce2663e1fed4dfd4e56eccd580297e",
"reference": "51087f87dcce2663e1fed4dfd4e56eccd580297e",
"shasum": ""
},
"require": {
@ -2028,9 +2028,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0"
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.2"
},
"time": "2025-02-19T13:28:12+00:00"
"time": "2025-02-17T20:25:51+00:00"
},
{
"name": "psr/cache",
@ -9871,16 +9871,16 @@
},
{
"name": "phpunit/phpunit",
"version": "11.5.9",
"version": "11.5.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "c91c830e7108a81e5845aeb6ba8fe3c1a4351c0b"
"reference": "c9bd61aab12f0fc5e82ecfe621ff518a1d1f1049"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c91c830e7108a81e5845aeb6ba8fe3c1a4351c0b",
"reference": "c91c830e7108a81e5845aeb6ba8fe3c1a4351c0b",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c9bd61aab12f0fc5e82ecfe621ff518a1d1f1049",
"reference": "c9bd61aab12f0fc5e82ecfe621ff518a1d1f1049",
"shasum": ""
},
"require": {
@ -9890,7 +9890,7 @@
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.13.0",
"myclabs/deep-copy": "^1.12.1",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.2",
@ -9952,7 +9952,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.9"
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.8"
},
"funding": [
{
@ -9968,7 +9968,7 @@
"type": "tidelift"
}
],
"time": "2025-02-21T06:08:50+00:00"
"time": "2025-02-18T06:26:59+00:00"
},
{
"name": "react/cache",

79
package-lock.json generated
View File

@ -5,8 +5,7 @@
"packages": {
"": {
"dependencies": {
"flowbite": "^3.1.2",
"tailwindcss": "^4.0.8"
"flowbite": "^3.0.0"
}
},
"node_modules/@popperjs/core": {
@ -93,15 +92,14 @@
"license": "MIT"
},
"node_modules/flowbite": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-3.1.2.tgz",
"integrity": "sha512-MkwSgbbybCYgMC+go6Da5idEKUFfMqc/AmSjm/2ZbdmvoKf5frLPq/eIhXc9P+rC8t9boZtUXzHDgt5whZ6A/Q==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/flowbite/-/flowbite-3.0.0.tgz",
"integrity": "sha512-5FyZOzsD6iER+iEm+NnqtzTdBVXOjG2HhuuAjOWmqTlz4GK/tVkXHJoY41lnHBMSHlFPM4aPcppnXrzPYL3vYA==",
"license": "MIT",
"dependencies": {
"@popperjs/core": "^2.9.3",
"flowbite-datepicker": "^1.3.1",
"mini-svg-data-uri": "^1.4.3",
"postcss": "^8.5.1"
"mini-svg-data-uri": "^1.4.3"
}
},
"node_modules/flowbite-datepicker": {
@ -176,36 +174,12 @@
"mini-svg-data-uri": "cli.js"
}
},
"node_modules/nanoid": {
"version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/path-parse": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
"license": "MIT"
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
@ -218,34 +192,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/postcss": {
"version": "8.5.3",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
"integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.8",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@ -266,15 +212,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@ -286,12 +223,6 @@
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tailwindcss": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.0.8.tgz",
"integrity": "sha512-Me7N5CKR+D2A1xdWA5t5+kjjT7bwnxZOE6/yDI/ixJdJokszsn2n++mdU5yJwrsTpqFX2B9ZNMBJDwcqk9C9lw==",
"license": "MIT"
}
}
}

View File

@ -1,6 +1,5 @@
{
"dependencies": {
"flowbite": "^3.1.2",
"tailwindcss": "^4.0.8"
"flowbite": "^3.0.0"
}
}

View File

@ -4,28 +4,13 @@ declare(strict_types=1);
namespace App\Controller;
use App\DTO\EmailDTO;
use App\DTO\PasswordDTO;
use App\Entity\User;
use App\Form\EmailFormType;
use App\Form\PasswordFormType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
class ProfileController extends AbstractController
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly UserPasswordHasherInterface $passwordHasher,
)
{
}
#[Route('/profile', name: 'app_profile')]
#[IsGranted('ROLE_USER')]
public function index()
@ -34,75 +19,8 @@ class ProfileController extends AbstractController
* @var User $user
*/
$user = $this->getUser();
return $this->render('profile/index.html.twig', [
'user' => $user,
]);
}
#[Route('/profile/edit/email', name: 'app_profile_email_edit')]
#[IsGranted('ROLE_USER')]
public function editEmail(Request $request): Response
{
$emailDTO = new EmailDTO();
$form = $this->createForm(EmailFormType::class, $emailDTO);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/**
* @var User $user
*/
$user = $this->getUser();
if ($this->passwordHasher->isPasswordValid($user, $emailDTO->password)) {
$user->setEmail($emailDTO->email);
$this->entityManager->flush();
$this->addFlash('success', 'Votre adresse email a bien été modifiée.');
return $this->redirectToRoute('app_profile');
}
$this->addFlash('error', 'Le mot de passe est incorrect.');
}
return $this->render('profile/edit_email.html.twig', [
'form' => $form->createView(),
]);
}
#[Route('/profile/edit/password', name: 'app_profile_password_edit')]
#[IsGranted('ROLE_USER')]
public function editPassword(Request $request): Response
{
$passwordDTO = new PasswordDTO();
$form = $this->createForm(PasswordFormType::class, $passwordDTO);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/**
* @var User $user
*/
$user = $this->getUser();
if ($this->passwordHasher->isPasswordValid($user, $passwordDTO->password)) {
$user->setPassword($this->passwordHasher->hashPassword($user, $passwordDTO->newPassword));
$this->entityManager->flush();
$this->addFlash('success', 'Votre mot de passe a bien été modifiée.');
return $this->redirectToRoute('app_profile');
}
$this->addFlash('error', 'Le mot de passe est incorrect.');
}
return $this->render('profile/edit_password.html.twig', [
'form' => $form->createView(),
]);
}
}

View File

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
namespace App\DTO;
class EmailDTO
{
public string $email;
public string $password;
public function __construct(
) {
}
}

View File

@ -1,12 +0,0 @@
<?php
namespace App\DTO;
class PasswordDTO
{
public string $password;
public string $newPassword;
public function __construct()
{
}
}

View File

@ -1,49 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Form;
use App\DTO\EmailDTO;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class EmailFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class, [
'label' => 'Nouvelle adresse email',
'attr' => [
'placeholder' => 'Nouvelle adresse email',
'autocomplete' => 'new-email',
],
])
->add('password', PasswordType::class, [
'label' => 'Mot de passe actuel',
'attr' => [
'placeholder' => 'Mot de passe actuel',
'autocomplete' => 'new-password',
]
])
->add('submit', SubmitType::class, [
'label' => 'Enregistrer',
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => EmailDTO::class,
'attr' => [
'autocomplete' => 'off',
]
]);
}
}

View File

@ -1,51 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Form;
use App\DTO\PasswordDTO;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
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;
class PasswordFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('password', PasswordType::class, [
'label' => 'Mot de passe actuel',
'attr' => [
'placeholder' => 'Mot de passe actuel',
'autocomplete' => 'old-password',
],
'required' => true,
])
->add('newPassword', RepeatedType::class, [
'type' => PasswordType::class,
'first_options' => ['label' => 'Nouveau mot de passe'],
'attr' => [
'autocomplete' => 'new-password',
],
'second_options' => ['label' => 'Confirmer le mot de passe'],
'required' => true,
'invalid_message' => 'Les mots de passe ne correspondent pas.',
])
->add('submit', SubmitType::class, [
'label' => 'Enregistrer',
])
;
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => PasswordDTO::class,
]);
}
}

View File

@ -1,15 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./vendor/tales-from-a-dev/flowbite-bundle/templates/**/*.html.twig",
"./assets/**/*.js",
"./templates/**/*.html.twig",
],
theme: {
extend: {},
},
plugins: [
require('flowbite/plugin')
],
darkMode: 'media',
}

View File

@ -1,15 +0,0 @@
{% extends 'base.html.twig' %}
{% block body %}
<div class="container mx-auto px-16 mt-4">
<div class="block p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
<h3 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Modifier mon adresse email</h3>
{{ form(form) }}
</div>
</div>
{% endblock %}
{% block title %}
Modifier mon email
{% endblock %}

View File

@ -1,15 +0,0 @@
{% extends 'base.html.twig' %}
{% block body %}
<div class="container mx-auto px-16 mt-4">
<div class="block p-6 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
<h3 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Modifier mon mot de passe</h3>
{{ form(form) }}
</div>
</div>
{% endblock %}
{% block title %}
Modifier mon email
{% endblock %}

View File

@ -12,29 +12,20 @@
<div class="flex justify-center pb-2">
{{ user.fullname }}
</div>
<div class="border-t border-gray-500 p-2 flex justify-between">
<div>
<div class="border-t border-gray-500 py-2 pl-2">
<span class="font-bold">
Email :
</span> {{ user.email }}
</div>
<a href="{{ path('app_profile_email_edit') }}" class="text-white hover:text-gray-200">
<twig:ux:icon name="line-md:pencil" class="w-6 h-6" />
</a>
</div>
<div class="border-t border-gray-500 p-2">
<div class="border-t border-gray-500 py-2 pl-2">
<span class="font-bold">
Rôle :
</span> {{ user.roles[0] == 'ROLE_ADMIN' ? 'Administrateur' : 'Utilisateur' }}
</div>
<div class="border-t border-gray-500 p-2">
<div class="border-t border-gray-500 py-2 pl-2">
<span class="font-bold">
Rôle de dossier :
</span>
{{ user.folderRole.value }}
</div>
<div class="border-t border-gray-500 py-2 pl-2">
<a href="{{ path('app_profile_password_edit') }}" class="font-medium text-blue-600 dark:text-blue-500 hover:underline">Modifier mon mot de passe</a>
</span> {{ user.folderRole.value }}
</div>
</div>