Create everything required to login and register
This commit is contained in:
parent
82d5625e60
commit
5be77eeba1
@ -13,14 +13,24 @@
|
|||||||
"symfony/console": "6.2.*",
|
"symfony/console": "6.2.*",
|
||||||
"symfony/dotenv": "6.2.*",
|
"symfony/dotenv": "6.2.*",
|
||||||
"symfony/flex": "^2",
|
"symfony/flex": "^2",
|
||||||
|
"symfony/form": "6.2.*",
|
||||||
"symfony/framework-bundle": "6.2.*",
|
"symfony/framework-bundle": "6.2.*",
|
||||||
|
"symfony/monolog-bundle": "^3.0",
|
||||||
"symfony/runtime": "6.2.*",
|
"symfony/runtime": "6.2.*",
|
||||||
"symfony/security-bundle": "6.2.*",
|
"symfony/security-bundle": "6.2.*",
|
||||||
"symfony/twig-bundle": "6.2.*",
|
"symfony/twig-bundle": "6.2.*",
|
||||||
|
"symfony/validator": "6.2.*",
|
||||||
"symfony/yaml": "6.2.*",
|
"symfony/yaml": "6.2.*",
|
||||||
"twig/extra-bundle": "^2.12|^3.0",
|
"twig/extra-bundle": "^2.12|^3.0",
|
||||||
"twig/twig": "^2.12|^3.0"
|
"twig/twig": "^2.12|^3.0"
|
||||||
},
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"deployer/deployer": "^7.3",
|
||||||
|
"symfony/debug-bundle": "6.2.*",
|
||||||
|
"symfony/maker-bundle": "^1.48",
|
||||||
|
"symfony/stopwatch": "6.2.*",
|
||||||
|
"symfony/web-profiler-bundle": "6.2.*"
|
||||||
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"allow-plugins": {
|
"allow-plugins": {
|
||||||
"php-http/discovery": true,
|
"php-http/discovery": true,
|
||||||
@ -68,8 +78,5 @@
|
|||||||
"allow-contrib": false,
|
"allow-contrib": false,
|
||||||
"require": "6.2.*"
|
"require": "6.2.*"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"symfony/maker-bundle": "^1.48"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
887
composer.lock
generated
887
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -8,4 +8,7 @@ return [
|
|||||||
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
|
||||||
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
Doctrine\Bundle\MigrationsBundle\DoctrineMigrationsBundle::class => ['all' => true],
|
||||||
|
Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
|
||||||
|
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
|
||||||
|
Symfony\Bundle\DebugBundle\DebugBundle::class => ['dev' => true],
|
||||||
];
|
];
|
||||||
|
5
config/packages/debug.yaml
Normal file
5
config/packages/debug.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
when@dev:
|
||||||
|
debug:
|
||||||
|
# Forwards VarDumper Data clones to a centralized server allowing to inspect dumps on CLI or in your browser.
|
||||||
|
# See the "server:dump" command to start a new server.
|
||||||
|
dump_destination: "tcp://%env(VAR_DUMPER_SERVER)%"
|
61
config/packages/monolog.yaml
Normal file
61
config/packages/monolog.yaml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
monolog:
|
||||||
|
channels:
|
||||||
|
- deprecation # Deprecations are logged in the dedicated "deprecation" channel when it exists
|
||||||
|
|
||||||
|
when@dev:
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: stream
|
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
|
level: debug
|
||||||
|
channels: ["!event"]
|
||||||
|
# uncomment to get logging in your browser
|
||||||
|
# you may have to allow bigger header sizes in your Web server configuration
|
||||||
|
#firephp:
|
||||||
|
# type: firephp
|
||||||
|
# level: info
|
||||||
|
#chromephp:
|
||||||
|
# type: chromephp
|
||||||
|
# level: info
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
|
process_psr_3_messages: false
|
||||||
|
channels: ["!event", "!doctrine", "!console"]
|
||||||
|
|
||||||
|
when@test:
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: fingers_crossed
|
||||||
|
action_level: error
|
||||||
|
handler: nested
|
||||||
|
excluded_http_codes: [404, 405]
|
||||||
|
channels: ["!event"]
|
||||||
|
nested:
|
||||||
|
type: stream
|
||||||
|
path: "%kernel.logs_dir%/%kernel.environment%.log"
|
||||||
|
level: debug
|
||||||
|
|
||||||
|
when@prod:
|
||||||
|
monolog:
|
||||||
|
handlers:
|
||||||
|
main:
|
||||||
|
type: fingers_crossed
|
||||||
|
action_level: error
|
||||||
|
handler: nested
|
||||||
|
excluded_http_codes: [404, 405]
|
||||||
|
buffer_size: 50 # How many messages should be saved? Prevent memory leaks
|
||||||
|
nested:
|
||||||
|
type: stream
|
||||||
|
path: php://stderr
|
||||||
|
level: debug
|
||||||
|
formatter: monolog.formatter.json
|
||||||
|
console:
|
||||||
|
type: console
|
||||||
|
process_psr_3_messages: false
|
||||||
|
channels: ["!event", "!doctrine"]
|
||||||
|
deprecation:
|
||||||
|
type: stream
|
||||||
|
channels: [deprecation]
|
||||||
|
path: php://stderr
|
@ -5,7 +5,7 @@ security:
|
|||||||
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
# https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider
|
||||||
providers:
|
providers:
|
||||||
# used to reload user from session & other features (e.g. switch_user)
|
# used to reload user from session & other features (e.g. switch_user)
|
||||||
app_user_provider:
|
user_provider:
|
||||||
entity:
|
entity:
|
||||||
class: App\Entity\User
|
class: App\Entity\User
|
||||||
property: username
|
property: username
|
||||||
@ -15,7 +15,19 @@ security:
|
|||||||
security: false
|
security: false
|
||||||
main:
|
main:
|
||||||
lazy: true
|
lazy: true
|
||||||
provider: app_user_provider
|
provider: user_provider
|
||||||
|
logout:
|
||||||
|
path: logout
|
||||||
|
form_login:
|
||||||
|
login_path: login
|
||||||
|
check_path: login
|
||||||
|
remember_me:
|
||||||
|
secret: '%kernel.secret%' # required
|
||||||
|
lifetime: 2419200 # 4 weeks in seconds
|
||||||
|
|
||||||
|
secured_area:
|
||||||
|
form_login:
|
||||||
|
enable_csrf: true
|
||||||
|
|
||||||
# activate different ways to authenticate
|
# activate different ways to authenticate
|
||||||
# https://symfony.com/doc/current/security.html#the-firewall
|
# https://symfony.com/doc/current/security.html#the-firewall
|
||||||
@ -26,8 +38,12 @@ security:
|
|||||||
# Easy way to control access for large sections of your site
|
# Easy way to control access for large sections of your site
|
||||||
# Note: Only the *first* access control that matches will be used
|
# Note: Only the *first* access control that matches will be used
|
||||||
access_control:
|
access_control:
|
||||||
# - { path: ^/admin, roles: ROLE_ADMIN }
|
- { path: ^/login$, role: PUBLIC_ACCESS }
|
||||||
# - { path: ^/profile, roles: ROLE_USER }
|
- { path: ^/logout$, role: ROLE_USER }
|
||||||
|
- { path: ^/register, role: ROLE_ADMIN }
|
||||||
|
- { path: ^/admin, role: ROLE_ADMIN }
|
||||||
|
|
||||||
|
- { path: ^/, role: ROLE_USER }
|
||||||
|
|
||||||
when@test:
|
when@test:
|
||||||
security:
|
security:
|
||||||
|
13
config/packages/validator.yaml
Normal file
13
config/packages/validator.yaml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
framework:
|
||||||
|
validation:
|
||||||
|
email_validation_mode: html5
|
||||||
|
|
||||||
|
# Enables validator auto-mapping support.
|
||||||
|
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
|
||||||
|
#auto_mapping:
|
||||||
|
# App\Entity\: []
|
||||||
|
|
||||||
|
when@test:
|
||||||
|
framework:
|
||||||
|
validation:
|
||||||
|
not_compromised_password: false
|
17
config/packages/web_profiler.yaml
Normal file
17
config/packages/web_profiler.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
when@dev:
|
||||||
|
web_profiler:
|
||||||
|
toolbar: true
|
||||||
|
intercept_redirects: false
|
||||||
|
|
||||||
|
framework:
|
||||||
|
profiler:
|
||||||
|
only_exceptions: false
|
||||||
|
collect_serializer_data: true
|
||||||
|
|
||||||
|
when@test:
|
||||||
|
web_profiler:
|
||||||
|
toolbar: false
|
||||||
|
intercept_redirects: false
|
||||||
|
|
||||||
|
framework:
|
||||||
|
profiler: { collect: false }
|
8
config/routes/web_profiler.yaml
Normal file
8
config/routes/web_profiler.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
when@dev:
|
||||||
|
web_profiler_wdt:
|
||||||
|
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
|
||||||
|
prefix: /_wdt
|
||||||
|
|
||||||
|
web_profiler_profiler:
|
||||||
|
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
|
||||||
|
prefix: /_profiler
|
@ -20,5 +20,6 @@ services:
|
|||||||
- '../src/Entity/'
|
- '../src/Entity/'
|
||||||
- '../src/Kernel.php'
|
- '../src/Kernel.php'
|
||||||
|
|
||||||
# add more service definitions when explicit configuration is needed
|
App\Service\LastRelease:
|
||||||
# please note that last definitions always *replace* previous ones
|
arguments:
|
||||||
|
- '%kernel.project_dir%/release.json'
|
||||||
|
108
deploy.php
Normal file
108
deploy.php
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Deployer;
|
||||||
|
|
||||||
|
require_once 'recipe/common.php';
|
||||||
|
|
||||||
|
// Project name
|
||||||
|
set('application', 'snips');
|
||||||
|
|
||||||
|
// Project repository
|
||||||
|
set('repository', 'git@git.loken.nl:ardent/Snips.git');
|
||||||
|
|
||||||
|
// [Optional] Allocate tty for git clone. Default value is false.
|
||||||
|
set('git_tty', true);
|
||||||
|
|
||||||
|
// Shared files/dirs between deploys
|
||||||
|
set('shared_dirs', ['var/log', 'var/sessions']);
|
||||||
|
set('shared_files', ['.env.local']);
|
||||||
|
//set('writable_dirs', ['var']);
|
||||||
|
set('migrations_config', '');
|
||||||
|
set('allow_anonymous_stats', false);
|
||||||
|
|
||||||
|
// Hosts
|
||||||
|
host('snips.loken.nl')
|
||||||
|
->setRemoteUser('www-data')
|
||||||
|
->set('branch', function () {
|
||||||
|
return input()->getOption('branch') ?: 'develop';
|
||||||
|
})
|
||||||
|
->set('deploy_path', '~/snips.loken.nl');
|
||||||
|
|
||||||
|
set('bin/console', function () {
|
||||||
|
return parse('{{release_path}}/bin/console');
|
||||||
|
});
|
||||||
|
|
||||||
|
set('console_options', function () {
|
||||||
|
return '--no-interaction';
|
||||||
|
});
|
||||||
|
|
||||||
|
desc('Clear cache');
|
||||||
|
task('cache:clear', function () {
|
||||||
|
run('{{bin/php}} {{bin/console}} cache:clear {{console_options}} --no-warmup');
|
||||||
|
});
|
||||||
|
|
||||||
|
desc('Warm up cache');
|
||||||
|
task('cache:warmup', function () {
|
||||||
|
run('{{bin/php}} {{bin/console}} cache:warmup {{console_options}}');
|
||||||
|
});
|
||||||
|
|
||||||
|
desc('Migrate database');
|
||||||
|
task('database:migrate', function () {
|
||||||
|
// $options = '--allow-no-migration';
|
||||||
|
// if (get('migrations_config') !== '') {
|
||||||
|
// $options = sprintf('%s --configuration={{release_path}}/{{migrations_config}}', $options);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// run(sprintf('{{bin/php}} {{bin/console}} doctrine:migrations:migrate %s {{console_options}}', $options));
|
||||||
|
run('{{bin/php}} {{bin/console}} doctrine:schema:update --force');
|
||||||
|
});
|
||||||
|
|
||||||
|
task('deployment:log', function () { //https://stackoverflow.com/questions/59686270/how-to-log-deployments-in-deployer
|
||||||
|
$branch = parse('{{branch}}');
|
||||||
|
$date = date('Y-m-d H:i:s');
|
||||||
|
$commitHashShort = runLocally('git rev-parse --short HEAD');
|
||||||
|
// $commitHash = runLocally('git rev-parse HEAD');
|
||||||
|
$commit = explode(PHP_EOL, runLocally('git log -1 --pretty="%H%n%ci"'));
|
||||||
|
$commitHash = $commit[0];
|
||||||
|
$commitDate = $commit[1];
|
||||||
|
|
||||||
|
// $line = sprintf('%s %s branch="%s" hash="%s"', $date, $commitHashShort, $branch, $commitHash);
|
||||||
|
$projectUrlBase = 'https://git.loken.nl/ardent/AnimeRSS4';
|
||||||
|
$array = [
|
||||||
|
'branch' => $branch,
|
||||||
|
'branchUrl' => sprintf('%s/src/branch/%s', $projectUrlBase, $branch),
|
||||||
|
'date' => $date,
|
||||||
|
'commitHashShort' => $commitHashShort,
|
||||||
|
'commitHashLong' => $commitHash,
|
||||||
|
'commitDate' => $commitDate,
|
||||||
|
'commitUrl' => sprintf('%s/commit/%s', $projectUrlBase, $commitHash),
|
||||||
|
'projectUrl' => $projectUrlBase,
|
||||||
|
];
|
||||||
|
$json = json_encode($array, JSON_PRETTY_PRINT);
|
||||||
|
|
||||||
|
runLocally("echo '$json' > release.json");
|
||||||
|
upload('release.json', '{{release_path}}/release.json');
|
||||||
|
});
|
||||||
|
|
||||||
|
//desc('Deploy project');
|
||||||
|
//task('deploy', [
|
||||||
|
// 'deployment:log',
|
||||||
|
//]);
|
||||||
|
|
||||||
|
desc('Deploy project');
|
||||||
|
task('deploy', [
|
||||||
|
'deploy:prepare',
|
||||||
|
'deploy:vendors',
|
||||||
|
'cache:clear',
|
||||||
|
'cache:warmup',
|
||||||
|
'database:migrate',
|
||||||
|
'deployment:log',
|
||||||
|
'deploy:symlink',
|
||||||
|
'deploy:unlock',
|
||||||
|
'deploy:cleanup',
|
||||||
|
]);
|
||||||
|
|
||||||
|
after('deploy', 'deploy:success');
|
||||||
|
|
||||||
|
// [Optional] if deploy fails automatically unlock.
|
||||||
|
after('deploy:failed', 'deploy:unlock');
|
0
src/Controller/.gitignore
vendored
0
src/Controller/.gitignore
vendored
19
src/Controller/HomeController.php
Normal file
19
src/Controller/HomeController.php
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Annotation\Route;
|
||||||
|
|
||||||
|
class HomeController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/', name: 'home')]
|
||||||
|
public function home(): Response
|
||||||
|
{
|
||||||
|
return $this->redirectToRoute('task_view');
|
||||||
|
// return $this->render('simple.html.twig', [
|
||||||
|
// 'text' => 'Welcome!'
|
||||||
|
// ]);
|
||||||
|
}
|
||||||
|
}
|
72
src/Controller/SecurityController.php
Normal file
72
src/Controller/SecurityController.php
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Form\RegistrationFormType;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Exception;
|
||||||
|
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\Annotation\Route;
|
||||||
|
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||||
|
|
||||||
|
class SecurityController extends AbstractController
|
||||||
|
{
|
||||||
|
#[Route('/login', name: 'login')]
|
||||||
|
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||||
|
{
|
||||||
|
$error = $authenticationUtils->getLastAuthenticationError();
|
||||||
|
$lastUsername = $authenticationUtils->getLastUsername();
|
||||||
|
|
||||||
|
return $this->render('security/login.html.twig', [
|
||||||
|
'last_username' => $lastUsername,
|
||||||
|
'error' => $error,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/logout', name: 'logout')]
|
||||||
|
public function logout(): void
|
||||||
|
{
|
||||||
|
// controller can be blank: it will never be called!
|
||||||
|
throw new Exception('Don\'t forget to activate logout in security.yaml');
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/register', name: 'register')]
|
||||||
|
public function register(
|
||||||
|
Request $request,
|
||||||
|
UserPasswordHasherInterface $userPasswordHasher,
|
||||||
|
EntityManagerInterface $entityManager,
|
||||||
|
): Response
|
||||||
|
{
|
||||||
|
$user = new User();
|
||||||
|
$form = $this->createForm(RegistrationFormType::class, $user);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
if ($form->get('plainPassword')->getData() !== $form->get('plainPasswordRepeated')->getData()) {
|
||||||
|
$this->addFlash('error', 'Password and password repeated must be the same');
|
||||||
|
} else {
|
||||||
|
$user->setPassword(
|
||||||
|
$userPasswordHasher->hashPassword(
|
||||||
|
$user,
|
||||||
|
$form->get('plainPassword')->getData()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$entityManager->persist($user);
|
||||||
|
$entityManager->flush();
|
||||||
|
// do anything else you need here, like send an email
|
||||||
|
$this->addFlash('success', sprintf('Successfully registered user %s', $user->getUsername()));
|
||||||
|
|
||||||
|
return $this->redirectToRoute('register');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('security/register.html.twig', [
|
||||||
|
'registrationForm' => $form->createView(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
63
src/Controller/UserController.php
Normal file
63
src/Controller/UserController.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Form\ProfileType;
|
||||||
|
use App\Form\UserSettingsType;
|
||||||
|
use App\Service\LastRelease;
|
||||||
|
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\Annotation\Route;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
#[Route('/user', name: 'user')]
|
||||||
|
class UserController extends AbstractController
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $em,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/profile', name: '_profile')]
|
||||||
|
public function profile(
|
||||||
|
Request $request,
|
||||||
|
UserPasswordHasherInterface $userPasswordHasher,
|
||||||
|
LastRelease $lastRelease,
|
||||||
|
): Response
|
||||||
|
{
|
||||||
|
$user = $this->getUser();
|
||||||
|
$form = $this->createForm(ProfileType::class, $user);
|
||||||
|
|
||||||
|
$form->handleRequest($request);
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
if (!empty($form->get('plainPassword')->getData())) {
|
||||||
|
if ($form->get('plainPassword')->getData() !== $form->get('plainPasswordRepeated')->getData()) {
|
||||||
|
$this->addFlash('error', 'Password and password repeated must be the same, password not changed');
|
||||||
|
} else {
|
||||||
|
$this->addFlash('success', 'Password updated successfully');
|
||||||
|
$user
|
||||||
|
->setPassword(
|
||||||
|
$userPasswordHasher->hashPassword(
|
||||||
|
$user,
|
||||||
|
$form->get('plainPassword')->getData()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->addFlash('success', 'Profile updated successfully');
|
||||||
|
|
||||||
|
$this->em->persist($user);
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('user/profile.html.twig', [
|
||||||
|
'form' => $form->createView(),
|
||||||
|
'release' => $lastRelease,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
49
src/Form/ProfileType.php
Normal file
49
src/Form/ProfileType.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
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;
|
||||||
|
use Symfony\Component\Validator\Constraints\Length;
|
||||||
|
|
||||||
|
class ProfileType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('email')
|
||||||
|
->add('plainPassword', PasswordType::class, [
|
||||||
|
'required' => false,
|
||||||
|
'mapped' => false,
|
||||||
|
'attr' => ['autocomplete' => 'new-password'],
|
||||||
|
'label' => 'Password',
|
||||||
|
'constraints' => [
|
||||||
|
new Length([
|
||||||
|
'min' => 8,
|
||||||
|
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||||
|
// max length allowed by Symfony for security reasons
|
||||||
|
'max' => 4096,
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('plainPasswordRepeated', PasswordType::class, [
|
||||||
|
'required' => false,
|
||||||
|
'mapped' => false,
|
||||||
|
'attr' => ['autocomplete' => 'new-password'],
|
||||||
|
'label' => 'Password repeated',
|
||||||
|
])
|
||||||
|
->add('save', SubmitType::class)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
64
src/Form/RegistrationFormType.php
Normal file
64
src/Form/RegistrationFormType.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\User;
|
||||||
|
use Symfony\Component\Form\AbstractType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
|
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;
|
||||||
|
use Symfony\Component\Validator\Constraints\IsTrue;
|
||||||
|
use Symfony\Component\Validator\Constraints\Length;
|
||||||
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
|
||||||
|
class RegistrationFormType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('username')
|
||||||
|
->add('name')
|
||||||
|
->add('plainPassword', PasswordType::class, [
|
||||||
|
// instead of being set onto the object directly,
|
||||||
|
// this is read and encoded in the controller
|
||||||
|
'mapped' => false,
|
||||||
|
'attr' => ['autocomplete' => 'new-password'],
|
||||||
|
'label' => 'Password',
|
||||||
|
'constraints' => [
|
||||||
|
new NotBlank([
|
||||||
|
'message' => 'Please enter a password',
|
||||||
|
]),
|
||||||
|
new Length([
|
||||||
|
'min' => 8,
|
||||||
|
'minMessage' => 'Your password should be at least {{ limit }} characters',
|
||||||
|
// max length allowed by Symfony for security reasons
|
||||||
|
'max' => 4096,
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->add('plainPasswordRepeated', PasswordType::class, [
|
||||||
|
'mapped' => false,
|
||||||
|
'label' => 'Password repeated',
|
||||||
|
])
|
||||||
|
->add('email', EmailType::class)
|
||||||
|
// ->add('agreeTerms', CheckboxType::class, [
|
||||||
|
// 'mapped' => false,
|
||||||
|
// 'constraints' => [
|
||||||
|
// new IsTrue([
|
||||||
|
// 'message' => 'You should agree to our terms.',
|
||||||
|
// ]),
|
||||||
|
// ],
|
||||||
|
// ])
|
||||||
|
->add('register', SubmitType::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => User::class,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
68
src/Service/LastRelease.php
Normal file
68
src/Service/LastRelease.php
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use JetBrains\PhpStorm\ArrayShape;
|
||||||
|
|
||||||
|
class LastRelease
|
||||||
|
{
|
||||||
|
#[ArrayShape([
|
||||||
|
"branch" => "string",
|
||||||
|
"date" => "string",
|
||||||
|
"commitHashShort" => "string",
|
||||||
|
"commitHashLong" => "string",
|
||||||
|
"commitDate" => "string",
|
||||||
|
"branchUrl" => "string",
|
||||||
|
"projectUrl" => "string",
|
||||||
|
"commitUrl" => "string",
|
||||||
|
])]
|
||||||
|
private array $lastRelease = [];
|
||||||
|
|
||||||
|
public function __construct(string $jsonFile)
|
||||||
|
{
|
||||||
|
if (file_exists($jsonFile)) {
|
||||||
|
$this->lastRelease = json_decode(file_get_contents($jsonFile), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBranch(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['branch'] ?? '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBranchUrl(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['branchUrl'] ?? '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProjectUrl(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['projectUrl'] ?? '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommitUrl(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['commitUrl'] ?? '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDate(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['date'] ?? '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommitHashShort(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['commitHashShort'] ?? '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommitHashLong(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['commitHashLong'] ?? '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommitDate(): string
|
||||||
|
{
|
||||||
|
return $this->lastRelease['commitDate'] ?? '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
49
symfony.lock
49
symfony.lock
@ -38,6 +38,18 @@
|
|||||||
"bin/console"
|
"bin/console"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"symfony/debug-bundle": {
|
||||||
|
"version": "6.2",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "5.3",
|
||||||
|
"ref": "5aa8aa48234c8eb6dbdd7b3cd5d791485d2cec4b"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/debug.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/flex": {
|
"symfony/flex": {
|
||||||
"version": "2.2",
|
"version": "2.2",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@ -78,6 +90,18 @@
|
|||||||
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
"ref": "fadbfe33303a76e25cb63401050439aa9b1a9c7f"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"symfony/monolog-bundle": {
|
||||||
|
"version": "3.8",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "3.7",
|
||||||
|
"ref": "213676c4ec929f046dfde5ea8e97625b81bc0578"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/monolog.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"symfony/routing": {
|
"symfony/routing": {
|
||||||
"version": "6.2",
|
"version": "6.2",
|
||||||
"recipe": {
|
"recipe": {
|
||||||
@ -116,6 +140,31 @@
|
|||||||
"templates/base.html.twig"
|
"templates/base.html.twig"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"symfony/validator": {
|
||||||
|
"version": "6.2",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "5.3",
|
||||||
|
"ref": "c32cfd98f714894c4f128bb99aa2530c1227603c"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/validator.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"symfony/web-profiler-bundle": {
|
||||||
|
"version": "6.2",
|
||||||
|
"recipe": {
|
||||||
|
"repo": "github.com/symfony/recipes",
|
||||||
|
"branch": "main",
|
||||||
|
"version": "6.1",
|
||||||
|
"ref": "e42b3f0177df239add25373083a564e5ead4e13a"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"config/packages/web_profiler.yaml",
|
||||||
|
"config/routes/web_profiler.yaml"
|
||||||
|
]
|
||||||
|
},
|
||||||
"twig/extra-bundle": {
|
"twig/extra-bundle": {
|
||||||
"version": "v3.5.1"
|
"version": "v3.5.1"
|
||||||
}
|
}
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>{% block title %}Welcome!{% endblock %}</title>
|
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 128 128%22><text y=%221.2em%22 font-size=%2296%22>⚫️</text></svg>">
|
|
||||||
{# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #}
|
|
||||||
{% block stylesheets %}
|
|
||||||
{{ encore_entry_link_tags('app') }}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block javascripts %}
|
|
||||||
{{ encore_entry_script_tags('app') }}
|
|
||||||
{% endblock %}
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{% block body %}{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
70
templates/base/base.html.twig
Normal file
70
templates/base/base.html.twig
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{% block title %}BLES{% endblock %}</title>
|
||||||
|
<link rel="shortcut icon" type="image/jpg" href="/favicon.png">
|
||||||
|
<script src="https://kit.fontawesome.com/3471b6556e.js" crossorigin="anonymous"></script>
|
||||||
|
{% if chartjs|default(false) %}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.9.1/dist/chart.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@2.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block css %}
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ include('base/navbar.html.twig') }}
|
||||||
|
|
||||||
|
{# alert block #}
|
||||||
|
<div class="container">
|
||||||
|
<br>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm mx-auto">
|
||||||
|
{% block flashes %}
|
||||||
|
{% for msg in app.session.flashBag.get('error') %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
<strong>Error: </strong> {{ msg|raw }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% for msg in app.session.flashBag.get('success') %}
|
||||||
|
<div class="alert alert-success">
|
||||||
|
<strong>Success: </strong> {{ msg|raw }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# body blocks #}
|
||||||
|
{% block bodyraw %}
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm mx-auto">
|
||||||
|
{% block body %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% if block('body2') is defined %}
|
||||||
|
<div class="col-sm mx-auto">
|
||||||
|
{{ block('body2') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{# javascript block #}
|
||||||
|
{% block js %}
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"
|
||||||
|
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"
|
||||||
|
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
33
templates/base/navbar.html.twig
Normal file
33
templates/base/navbar.html.twig
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<nav class="navbar navbar-expand-md navbar-dark bg-dark" style="z-index: 1;">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a title="BlueLinked Eco System" class="navbar-brand" href="{{ path('home') }}">BLES</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar"
|
||||||
|
aria-controls="navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbar">
|
||||||
|
<ul class="navbar-nav me-auto mb-2 mb-md-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ path('home') }}">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navbar-nav my-2 my-lg-0">
|
||||||
|
{% if app.environment == 'dev' %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="btn navbar-text bg-danger" href="#">dev</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
|
||||||
|
<span class="nav-item navbar-text mr-sm-2">Logged in as</span>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ path('user_profile') }}">{{ app.user.username }}</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ path('logout') }}">Logout</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
32
templates/security/login.html.twig
Normal file
32
templates/security/login.html.twig
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% extends 'base/base.html.twig' %}
|
||||||
|
|
||||||
|
{% block title %}Login{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<form action="{{ path('login') }}" method="post">
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if app.user %}
|
||||||
|
<div class="mb-3">
|
||||||
|
You are already logged in as {{ app.user }}, <a href="{{ path('logout') }}">Logout</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<h1 class="h3 mb-3 font-weight-normal">Please login</h1>
|
||||||
|
<label for="inputUsername">Username</label>
|
||||||
|
<input type="text" value="{{ last_username }}" name="_username" id="inputUsername" class="form-control" required
|
||||||
|
autofocus>
|
||||||
|
<br/>
|
||||||
|
<label for="inputPassword">Password</label>
|
||||||
|
<input type="password" name="_password" id="inputPassword" class="form-control" required>
|
||||||
|
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
|
||||||
|
<br/>
|
||||||
|
|
||||||
|
<div class="checkbox mb-3">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" name="_remember_me"> Remember me
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" type="submit">Login</button>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
6
templates/security/register.html.twig
Normal file
6
templates/security/register.html.twig
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% extends 'base/base.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<h1 class="h3 mb-3 font-weight-normal">Register new user</h1>
|
||||||
|
{{ form(registrationForm) }}
|
||||||
|
{% endblock %}
|
5
templates/simple.html.twig
Normal file
5
templates/simple.html.twig
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{% extends 'base/base.html.twig' %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{{ text | nl2br }}
|
||||||
|
{% endblock %}
|
26
templates/user/profile.html.twig
Normal file
26
templates/user/profile.html.twig
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{% extends "base/base.html.twig" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm">
|
||||||
|
<h4>{{ app.user.name }}</h4>
|
||||||
|
{% if is_granted('ROLE_API') %}
|
||||||
|
Your api key: {{ app.user.apiKey }} <a class="btn btn-primary" href="{{ path('user_apikey_generate') }}">Regenerate</a> <br/>
|
||||||
|
{% endif %}
|
||||||
|
<br/>
|
||||||
|
{% if is_granted('ROLE_ADMIN') %}
|
||||||
|
<br/><br/>
|
||||||
|
<h4>Latest release stats</h4>
|
||||||
|
Branch: {{ release.branch }} <br/>
|
||||||
|
Date: {{ release.date }} <br/>
|
||||||
|
Hash short: {{ release.commitHashShort }} <br/>
|
||||||
|
Hash long: {{ release.commitHashLong }} <br/>
|
||||||
|
Commit date: {{ release.commitDate }} <br/>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<h4>Change profile</h4>
|
||||||
|
{{ form(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user