Premiere mouture application - Suppression fichiers inutiles

backend-user
nox 2019-09-18 00:31:59 +02:00
parent 46ec06dcd3
commit a0fd572948
6322 changed files with 648732 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
config/config.php
.idea
tmp
composer.lock

35
composer.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "nox/gmarche",
"authors": [
{
"name": "nox",
"email": "nox@axiom-team.fr"
}
],
"autoload": {
"psr-4": {
"Framework\\": "src/Framework",
"App\\": "src",
"Tests\\": "tests"
}
},
"require": {
"guzzlehttp/psr7": "^1.4",
"http-interop/response-sender": "^1.0",
"zendframework/zend-expressive-fastroute": "^2.0",
"twig/twig": "^2.4",
"php-di/php-di": "^5.4",
"pagerfanta/pagerfanta": "^2.1",
"middlewares/whoops": "0.4.1",
"doctrine/cache": "1.4",
"intervention/image": "2.4",
"swiftmailer/swiftmailer": "^6.0",
"ramsey/uuid": "^3.7"
},
"require-dev": {
"squizlabs/php_codesniffer": "^3.0",
"phpunit/phpunit": "^6.2",
"robmorgan/phinx": "0.8.1",
"fzaninotto/faker": "^1.8"
}
}

4
config.php Normal file
View File

@ -0,0 +1,4 @@
<?php
return [
'gmarche.prefix' => '/news'
];

View File

@ -0,0 +1,53 @@
<?php
use App\Framework\Twig\UrlExtension;
use Framework\Middleware\CsrfMiddleware;
use Framework\Renderer\RendererInterface;
use Framework\Renderer\TwigRendererFactory;
use Framework\Router;
use Framework\Router\RouterFactory;
use Framework\Router\RouterTwigExtension;
use Framework\Session\PHPSession;
use Framework\Session\SessionInterface;
use Framework\Twig\{
CsrfExtension, FlashExtension, FormExtension, PagerFantaExtension, TextExtension, TimeExtension
};
return [
'env' => \DI\env('ENV', 'production'),
/*'env' => \DI\env('ENV', 'development'),*/
'database.host' => 'localhost',
'database.username' => 'root',
'database.password' => '',
'database.name' => 'gmarche',
'views.path' => dirname(__DIR__) . '/views',
'twig.extensions' => [
\DI\get(RouterTwigExtension::class),
\DI\get(PagerFantaExtension::class),
\DI\get(TextExtension::class),
\DI\get(TimeExtension::class),
\DI\get(FlashExtension::class),
\DI\get(FormExtension::class),
\DI\get(CsrfExtension::class),
\DI\get(UrlExtension::class)
],
SessionInterface::class => \DI\object(PHPSession::class),
CsrfMiddleware::class => \DI\object()->constructor(\DI\get(SessionInterface::class)),
Router::class => \DI\factory(RouterFactory::class),
RendererInterface::class => \DI\factory(TwigRendererFactory::class),
\PDO::class => function (\Psr\Container\ContainerInterface $c) {
return new PDO(
'mysql:host='. $c->get('database.host') . ';dbname=' . $c->get('database.name'),
$c->get('database.username'),
$c->get('database.password'),
[
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_OBJ,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
]
);
},
// MAILER
'mail.to' => 'admin@gmarche-testmail.com',
'mail.from' => 'no-reply@admin.fr',
Swift_Mailer::class => \DI\factory(\Framework\SwiftMailerFactory::class)
];

85
gm.iml Normal file
View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="App\" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="false" packagePrefix="Tests\" />
<sourceFolder url="file://$MODULE_DIR$/src/Framework" isTestSource="false" packagePrefix="Framework\" />
<excludeFolder url="file://$MODULE_DIR$/vendor/composer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/container-interop/container-interop" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/cache" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/instantiator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/doctrine/lexer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/egulias/email-validator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fig/http-message-util" />
<excludeFolder url="file://$MODULE_DIR$/vendor/filp/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/fzaninotto/faker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/guzzlehttp/psr7" />
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/http-factory" />
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/http-middleware" />
<excludeFolder url="file://$MODULE_DIR$/vendor/http-interop/response-sender" />
<excludeFolder url="file://$MODULE_DIR$/vendor/intervention/image" />
<excludeFolder url="file://$MODULE_DIR$/vendor/middlewares/utils" />
<excludeFolder url="file://$MODULE_DIR$/vendor/middlewares/whoops" />
<excludeFolder url="file://$MODULE_DIR$/vendor/myclabs/deep-copy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/nikic/fast-route" />
<excludeFolder url="file://$MODULE_DIR$/vendor/pagerfanta/pagerfanta" />
<excludeFolder url="file://$MODULE_DIR$/vendor/paragonie/random_compat" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/manifest" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phar-io/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-di/invoker" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-di/php-di" />
<excludeFolder url="file://$MODULE_DIR$/vendor/php-di/phpdoc-reader" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-common" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/reflection-docblock" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpdocumentor/type-resolver" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpspec/prophecy" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-code-coverage" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-file-iterator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-text-template" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-timer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/php-token-stream" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit" />
<excludeFolder url="file://$MODULE_DIR$/vendor/phpunit/phpunit-mock-objects" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/container" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/http-message" />
<excludeFolder url="file://$MODULE_DIR$/vendor/psr/log" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ralouphie/getallheaders" />
<excludeFolder url="file://$MODULE_DIR$/vendor/ramsey/uuid" />
<excludeFolder url="file://$MODULE_DIR$/vendor/robmorgan/phinx" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/code-unit-reverse-lookup" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/comparator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/diff" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/environment" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/exporter" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/global-state" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-enumerator" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/object-reflector" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/recursion-context" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/resource-operations" />
<excludeFolder url="file://$MODULE_DIR$/vendor/sebastian/version" />
<excludeFolder url="file://$MODULE_DIR$/vendor/squizlabs/php_codesniffer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/swiftmailer/swiftmailer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/config" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/console" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/debug" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/filesystem" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-ctype" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-iconv" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-intl-idn" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-mbstring" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/polyfill-php72" />
<excludeFolder url="file://$MODULE_DIR$/vendor/symfony/yaml" />
<excludeFolder url="file://$MODULE_DIR$/vendor/theseer/tokenizer" />
<excludeFolder url="file://$MODULE_DIR$/vendor/twig/twig" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webimpress/composer-extra-dependency" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webimpress/http-middleware-compatibility" />
<excludeFolder url="file://$MODULE_DIR$/vendor/webmozart/assert" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-expressive-fastroute" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-expressive-router" />
<excludeFolder url="file://$MODULE_DIR$/vendor/zendframework/zend-stdlib" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

28
phinx.php Normal file
View File

@ -0,0 +1,28 @@
<?php
require 'public/index.php';
$migrations = [];
$seeds = [];
foreach ($app->getModules() as $module) {
if ($module::MIGRATIONS) {
$migrations[] = $module::MIGRATIONS;
}
if ($module::SEEDS) {
$seeds[] = $module::SEEDS;
}
}
return [
'paths' => [
'migrations' => $migrations,
'seeds' => $seeds
],
'environments' => [
'default_database' => 'development',
'development' => [
'adapter' => 'mysql',
'host' => $app->getContainer()->get('database.host'),
'name' => $app->getContainer()->get('database.name'),
'user' => $app->getContainer()->get('database.username'),
'pass' => $app->getContainer()->get('database.password')
]
]
];

14
phpcs.xml Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" ?>
<ruleset name="MonFramework">
<description>Mes règles de formattage</description>
<arg name="colors"/>
<arg value="p"/>
<file>src</file>
<file>public/index.php</file>
<rule ref="PSR2">
<exclude name="PSR1.Classes.ClassDeclaration.MissingNamespace"/>
</rule>
</ruleset>

7
phpunit.xml Normal file
View File

@ -0,0 +1,7 @@
<phpunit bootstrap="./vendor/autoload.php">
<testsuites>
<testsuite name="Framework">
<directory>tests</directory>
</testsuite>
</testsuites>
</phpunit>

BIN
public/avatar_user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 B

199
public/css/style.css Normal file
View File

@ -0,0 +1,199 @@
* {
box-sizing: border-box;
}
btn-custom {
background-color: hsl(0, 0%, 79%) !important;
background-repeat: repeat-x;
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr="#134134134", endColorstr="#c9c9c9");
background-image: -khtml-gradient(linear, left top, left bottom, from(#134134134), to(#c9c9c9));
background-image: -moz-linear-gradient(top, #134134134, #c9c9c9);
background-image: -ms-linear-gradient(top, #134134134, #c9c9c9);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #134134134), color-stop(100%, #c9c9c9));
background-image: -webkit-linear-gradient(top, #134134134, #c9c9c9);
background-image: -o-linear-gradient(top, #134134134, #c9c9c9);
background-image: linear-gradient(#134134134, #c9c9c9);
border-color: #c9c9c9 #c9c9c9 hsl(0, 0%, 68.5%);
color: #333 !important;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.69);
-webkit-font-smoothing: antialiased;
}
img {
max-width: 100%;
height: auto;
}
a :hover {
background-color: lightgreen;
text-decoration: none;
}
li {
list-style-type: none;
text-color: black;
}
.liste_regions, .liste_antennes {
background-color: lightblue;
}
.fond {
background-image: url("../images/bildreich_1275.jpg");
background-repeat: no-repeat;
background-attachment:fixed;
background-position: center;
background-size: 100% auto;
font-family: Bree serif,"Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 18px;
}
header.header {
width: 389px;
height: 259px;
margin: 0 auto;
margin-bottom: 10px;
text-align: center;
}
header.header img {
width: 389px;
height: 259px;
}
.logo_toile img, .logo_june img {
transition: transform .5s;
}
.logo_toile img:hover, .logo_june img:hover {
transform: scale(1.1) rotate(10deg);
}
.post-header {
width: 820px;
height: 80px;
margin: 0 auto;
color: white;
opacity: 0.9;
font-family: Bree serif, Arial;
font-size: 26px;
}
#la_page {
text-align: center;
width: 1174px;
min-height: 901px;
height:auto;
margin-left: auto;
margin-right: auto;
}
.article {
width:auto;
height:auto;
font-size: 24px;
}
.article2 {
width:auto;
height:auto;
font-size: 24px;
}
.container {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.records_content th, .records_content2 th{
background-color: lightgray;
color: rgba(0,0,0,0.85);
}
.footer {
width:105px;
margin: 5px auto;
text-align: center;
}
/******************************************************************/
/* POUR MOBILES */
/******************************************************************/
@media only screen and (max-width:560px) {
.fond {
background-color: black;
background-image: url("../toureiffel.jpeg");
background-repeat: no-repeat;
background-attachment: fixed;
background-position: left;
background-size: cover;
}
.post-header {
width: auto;
height: auto;
margin-left: 10px;
}
#la_page {
width: auto;
height:auto;
margin-left: auto;
margin-right: auto;
}
.article {
width: auto;
height: auto;
margin-left: 10px;
margin-bottom: 10px;
}
.article2 {
width: auto;
height: auto;
margin-left: 10px;
margin-bottom: 10px;
}
.container button{
float: left;
}
/**************************************/
/* Tableaux responsives */
/**************************************/
.table-responsive table,
.table-responsive thead,
.table-responsive tbody,
.table-responsive tr,
.table-responsive th,
.table-responsive td {
display: block;
}
.table-responsive thead {
display: none;
}
.table-responsive td {
padding-left: 95px !important;
position: relative;
margin-top: -1px;
background: #FFF;
}
.table-responsive td button{
margin-top:-7px;
max-height: 30px;
}
.table-responsive td:nth-child(odd) {
background-color: #eee;
}
.table-responsive td::before {
padding: 10px;
content: attr(data-label);
position: absolute;
top: 0;
left: 0;
width: 85px;
bottom: 0;
background-color: #000;
color: #FFF;
display: flex;
align-items: center;
font-weight: bold;
}
.table-responsive tr {
margin-bottom: 1rem;
}
.table-responsive th + td {
padding-left: 10px;
}
}

BIN
public/images/attilax.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

BIN
public/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
public/images/logo_home.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

BIN
public/images/nox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

BIN
public/images/poka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

62
public/index.php Normal file
View File

@ -0,0 +1,62 @@
<?php
ini_set('display_errors',0);
use App\Account\AccountModule;
use App\Admin\AdminModule;
use App\Auth\AuthModule;
use App\Contact\ContactModule;
use App\Gmarche\GmarcheModule;
use App\Product\ProductModule;
use Framework\Auth\RoleMiddlewareFactory;
use Framework\Middleware\{
CsrfMiddleware,
DispatcherMiddleware,
MethodMiddleware,
RendererRequestMiddleware,
RouterMiddleware,
TrailingSlashMiddleware,
NotFoundMiddleware
};
use GuzzleHttp\Psr7\ServerRequest;
use Middlewares\Whoops;
chdir(dirname(__DIR__));
require 'vendor/autoload.php';
$chemin = $_SERVER['DOCUMENT_ROOT'];
$chemin_new = substr($chemin,0,-7);
$app = (new \Framework\App( $chemin_new .'/config/config.php'))
//$app = (new \Framework\App( '../config/config.php'))
->addModule(AdminModule::class)
->addModule(ContactModule::class)
->addModule(ProductModule::class)
->addModule(GmarcheModule::class)
->addModule(AuthModule::class)
->addModule(AccountModule::class);
$container = $app->getContainer();
// Pose problème
$container->get(\Framework\Router::class)->get('/', \App\Gmarche\Actions\RegionIndexAction::class, 'home');
$app->pipe(Whoops::class);
$app->pipe(TrailingSlashMiddleware::class);
$app->pipe(\App\Auth\ForbiddenMiddleware::class);
// admin pose problème
//$app->pipe(
// $container->get('admin.prefix'),
// $container->get(RoleMiddlewareFactory::class)->makeForRole('admin')
// );
$app->pipe(MethodMiddleware::class)
->pipe(RendererRequestMiddleware::class)
// ->pipe(CsrfMiddleware::class)
->pipe(RouterMiddleware::class)
->pipe(DispatcherMiddleware::class)
->pipe(NotFoundMiddleware::class);
if (php_sapi_name() !== "cli") {
$response = $app->run(ServerRequest::fromGlobals());
\Http\Response\send($response);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

View File

@ -0,0 +1,27 @@
<?php
namespace App\Account;
use App\Account\Action\AccountAction;
use App\Account\Action\AccountEditAction;
use App\Account\Action\SignupAction;
use Framework\Auth\LoggedInMiddleware;
use Framework\Module;
use Framework\Renderer\RendererInterface;
use Framework\Router;
class AccountModule extends Module
{
const MIGRATIONS = __DIR__ . '/migrations';
const DEFINITIONS = __DIR__ . '/definitions.php';
public function __construct(Router $router, RendererInterface $renderer)
{
$renderer->addPath('account', __DIR__ . '/views');
$router->get('/inscription', SignupAction::class, 'account.signup');
$router->post('/inscription', SignupAction::class);
$router->get('/mon-profil', [LoggedInMiddleware::class, AccountAction::class], 'account');
$router->post('/mon-profil', [LoggedInMiddleware::class, AccountEditAction::class]);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Account\Action;
use Framework\Auth;
use Framework\Renderer\RendererInterface;
use Psr\Http\Message\ServerRequestInterface;
class AccountAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var Auth
*/
private $auth;
public function __construct(
RendererInterface $renderer,
Auth $auth
) {
$this->renderer = $renderer;
$this->auth = $auth;
}
public function __invoke(ServerRequestInterface $request)
{
$user = $this->auth->getUser();
return $this->renderer->render('@account/account', compact('user'));
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace App\Account\Action;
use App\Auth\UserTable;
use Framework\Auth;
use Framework\Renderer\RendererInterface;
use Framework\Response\RedirectResponse;
use Framework\Session\FlashService;
use Framework\Validator;
use Psr\Http\Message\ServerRequestInterface;
class AccountEditAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var Auth
*/
private $auth;
/**
* @var FlashService
*/
private $flashService;
/**
* @var UserTable
*/
private $userTable;
public function __construct(
RendererInterface $renderer,
Auth $auth,
FlashService $flashService,
UserTable $userTable
) {
$this->renderer = $renderer;
$this->auth = $auth;
$this->flashService = $flashService;
$this->userTable = $userTable;
}
public function __invoke(ServerRequestInterface $request)
{
$user = $this->auth->getUser();
$params = $request->getParsedBody();
$validator = (new Validator($params))
->confirm('password')
->required('firstname', 'lastname');
if ($validator->isValid()) {
$userParams = [
'firstname' => $params['firstname'],
'lastname' => $params['lastname']
];
if (!empty($params['password'])) {
$userParams['password'] = password_hash($params['password'], PASSWORD_DEFAULT);
}
$this->userTable->update($user->id, $userParams);
$this->flashService->success('Votre compte a bien été mis à jour');
return new RedirectResponse($request->getUri()->getPath());
}
$errors = $validator->getErrors();
return $this->renderer->render('@account/account', compact('user', 'errors'));
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace App\Account\Action;
use App\Auth\DatabaseAuth;
use App\Auth\User;
use App\Auth\UserTable;
use Framework\Database\Hydrator;
use Framework\Renderer\RendererInterface;
use Framework\Response\RedirectResponse;
use Framework\Router;
use Framework\Session\FlashService;
use Framework\Validator;
use Psr\Http\Message\ServerRequestInterface;
class SignupAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var UserTable
*/
private $userTable;
/**
* @var Router
*/
private $router;
/**
* @var DatabaseAuth
*/
private $auth;
/**
* @var FlashService
*/
private $flashService;
public function __construct(
RendererInterface $renderer,
UserTable $userTable,
Router $router,
DatabaseAuth $auth,
FlashService $flashService
) {
$this->renderer = $renderer;
$this->userTable = $userTable;
$this->router = $router;
$this->auth = $auth;
$this->flashService = $flashService;
}
public function __invoke(ServerRequestInterface $request)
{
if ($request->getMethod() === 'GET') {
return $this->renderer->render('@account/signup');
}
$params = $request->getParsedBody();
$validator = (new Validator($params))
->required('username', 'email', 'password', 'password_confirm', 'firstname', 'lastname', 'created_at')
->length('username', 3)
->length('firstname', 2)
->length('lastname', 2)
->email('email')
->confirm('password')
->length('password', 4)
->unique('username', $this->userTable)
->unique('email', $this->userTable);
if ($validator->isValid()) {
$userParams = [
'username' => $params['username'],
'firstname'=> $params['firstname'],
'lastname'=> $params['lastname'],
'email' => $params['email'],
'password' => password_hash($params['password'], PASSWORD_DEFAULT),
'created_at' => $params['created_at'],
'role' => 'user'
];
$this->userTable->insert($userParams);
$user = Hydrator::hydrate($userParams, User::class);
$user->id = $this->userTable->getPdo()->lastInsertId();
$this->auth->setUser($user);
$this->flashService->success('Votre compte a bien été créé');
return new RedirectResponse($this->router->generateUri('account'));
}
$errors = $validator->getErrors();
return $this->renderer->render('@account/signup', [
'errors' => $errors,
'user' => [
'username' => $params['username'],
'email' => $params['email'],
'firstname'=> $params['firstname'],
'lastname'=> $params['lastname'],
'created_at' => $params['created_at']
]
]);
}
}

74
src/Account/User.php Normal file
View File

@ -0,0 +1,74 @@
<?php
namespace App\Account;
class User extends \App\Auth\User
{
/**
* @var string
*/
public $firstname;
/**
* @var string
*/
public $lastname;
/**
* @var string
*/
private $role;
public function getRoles(): array
{
return [$this->role];
}
/**
* @return string
*/
public function getFirstname(): string
{
return $this->firstname;
}
/**
* @param string $firstname
*/
public function setFirstname(string $firstname)
{
$this->firstname = $firstname;
}
/**
* @return string
*/
public function getLastname(): string
{
return $this->lastname;
}
/**
* @param string $lastname
*/
public function setLastname(string $lastname)
{
$this->lastname = $lastname;
}
/**
* @return mixed
*/
public function getRole()
{
return $this->role;
}
/**
* @param mixed $role
*/
public function setRole($role)
{
$this->role = $role;
}
}

View File

@ -0,0 +1,4 @@
<?php
return [
'auth.entity' => \App\Account\User::class
];

View File

@ -0,0 +1,18 @@
{% extends 'layout.twig' %}
{% block body %}
<div class="container" style="background-color: lightgray;opacity: 0.9;margin: 1rem;padding:1rem;">
<h2>Mon compte</h2>
<form action="" method="post">
{{ csrf_input() }}
{{ field('firstname', user.firstname, 'Prénom') }}
{{ field('lastname', user.lastname, 'Nom') }}
<h2>Changer de mot de passe</h2>
<p>Laissez vide pour ne rien changer</p>
{{ field('password', null, 'Mot de passe', {type: 'password'}) }}
{{ field('password_confirm', null, 'Confirmer le mot de passe', {type: 'password'}) }}
<button class="btn btn-primary">Modifier mes informations</button>
</form>
</div>
{% endblock %}

View File

@ -0,0 +1,16 @@
{% extends 'layout.twig' %}
{% block body %}
<div class="container" style="background-color: lightgray;opacity: 0.9;margin: 1rem;padding:1rem;">
<form action="{{ path('account.signup') }}" method="post">
{{ csrf_input() }}
{{ field('username', user.username, "Pseudo") }}
{{ field('firstname', user.firstname, "Prénom") }} {{ field('lastname', user.lastname, "Nom") }}
{{ field('email', user.email, "Email", {type: 'email'}) }}
{{ field('password', null, "Mot de passe", {type: 'password'}) }}
{{ field('password_confirm', null, "Confirmez le mot de passe", {type: 'password'}) }}
{{ field('created_at', date(), null, {type: 'hidden'}) }}
<button class="btn btn-primary">S'inscrire</button>
</form>
</div>
{% endblock %}

31
src/Admin/AdminModule.php Normal file
View File

@ -0,0 +1,31 @@
<?php
namespace App\Admin;
use App\Product\Actions\ProductIndexAction;
use App\Product\Actions\ProductCrudAction;
use Framework\Module;
use Framework\Renderer\RendererInterface;
use Framework\Renderer\TwigRenderer;
use Framework\Router;
class AdminModule extends Module
{
const DEFINITIONS = __DIR__ . '/config.php';
public function __construct(
RendererInterface $renderer,
Router $router,
AdminTwigExtension $adminTwigExtension,
string $prefix
) {
$renderer->addPath('admin', __DIR__ . '/views');
//$router->get('/machin', ProductCrudAction::class, 'machin.bidule'); // ProductIndexAction
/*if ($renderer instanceof TwigRenderer) {
$renderer->getTwig()->addExtension($adminTwigExtension);
}*/
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Admin;
class AdminTwigExtension extends \Twig_Extension
{
/**
* @var array
*/
private $widgets;
public function __construct(array $widgets)
{
$this->widgets = $widgets;
}
public function getFunctions(): array
{
return [
new \Twig_SimpleFunction('admin_menu', [$this, 'renderMenu'], ['is_safe' => ['html']])
];
}
public function renderMenu(): string
{
return array_reduce($this->widgets, function (string $html, AdminWidgetInterface $widget) {
return $html . $widget->renderMenu();
}, '');
}
}

View File

@ -0,0 +1,10 @@
<?php
namespace App\Admin;
interface AdminWidgetInterface
{
public function render(): string;
public function renderMenu(): string;
}

12
src/Admin/config.php Normal file
View File

@ -0,0 +1,12 @@
<?php
use App\Admin\AdminModule;
use App\Admin\DashboardAction;
return [
'admin.prefix' => '/admin',
'admin.widgets' => [],
\App\Admin\AdminTwigExtension::class => \DI\object()->constructor(\DI\get('admin.widgets')),
AdminModule::class => \DI\object()->constructorParameter('prefix', \DI\get('admin.prefix'))
//DashboardAction::class => \DI\object()->constructorParameter('widgets', \DI\get('admin.widgets'))
];

View File

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title "Mon site " %}</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/3.0.7/flatpickr.css">
<style>
body {
padding-top: 5rem;
}
</style>
</head>
<body>
<nav class="navbar fixed-top navbar-expand-sm navbar-dark bg-dark">
<a class="navbar-brand" href="{{ path(routePrefix) }}">Ğ1-Marché</a>
<ul class="navbar-nav mr-auto">
{{ admin_menu() }}
</ul>
<div class="navbar-nav">
<form class="nav-item active" method="post" action="{{ path('auth.logout') }}">
{{ csrf_input() }}
<button class="btn-primary btn-danger">Se déconnecter</button>
</form>
</div>
</nav>
<div class="container">
{% if flash('success') %}
<div class="alert alert-success">
{{ flash('success') }}
</div>
{% endif %}
{% if flash('error') %}
<div class="alert alert-danger">
{{ flash('error') }}
</div>
{% endif %}
{% block body %}{% endblock %}
</div><!-- /.container -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js" integrity="sha384-b/U6ypiBEHpOf/4+1nzFpr53nxSS+GLCkfwBdFNTxtclqqenISfwAzpKaMNFNmj4" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/js/bootstrap.min.js" integrity="sha384-h0AbiXch4ZDo7tp9hKZ4TsHbi047NrKGLO3SEJAg45jXxnGIfYzk4Si90RDIqNm1" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flatpickr/3.0.7/flatpickr.js"></script>
<script>
flatpickr('.datepicker', {
enableTime: true,
altInput: true,
altFormat: 'j F Y, H:i',
dateFormat: 'Y-m-d H:i:S'
})
</script>
</body>
</html>

View File

@ -0,0 +1,24 @@
<?php
namespace App\Auth\Action;
use Framework\Renderer\RendererInterface;
use Psr\Http\Message\ServerRequestInterface;
class LoginAction
{
/**
* @var RendererInterface
*/
private $renderer;
public function __construct(RendererInterface $renderer)
{
$this->renderer = $renderer;
}
public function __invoke(ServerRequestInterface $request)
{
return $this->renderer->render('@auth/login');
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace App\Auth\Action;
use App\Auth\DatabaseAuth;
use Framework\Actions\RouterAwareAction;
use Framework\Renderer\RendererInterface;
use Framework\Response\RedirectResponse;
use Framework\Router;
use Framework\Session\FlashService;
use Framework\Session\SessionInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Expressive\Router\RouterInterface;
class LoginAttemptAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var DatabaseAuth
*/
private $auth;
/**
* @var SessionInterface
*/
private $session;
/**
* @var RouterInterface
*/
private $router;
use RouterAwareAction;
public function __construct(
RendererInterface $renderer,
DatabaseAuth $auth,
Router $router,
SessionInterface $session
) {
$this->renderer = $renderer;
$this->auth = $auth;
$this->router = $router;
$this->session = $session;
}
public function __invoke(ServerRequestInterface $request)
{
$params = $request->getParsedBody();
$user = $this->auth->login($params['username'], $params['password']);
if ($user) {
$path = $this->session->get('auth.redirect') ?: $this->router->generateUri('gmarche.index');
$this->session->delete('auth.redirect');
return new RedirectResponse($path);
} else {
(new FlashService($this->session))->error('Identifiant ou mot de passe incorrect');
return $this->redirect('auth.login');
}
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Auth\Action;
use App\Auth\DatabaseAuth;
use Framework\Renderer\RendererInterface;
use Framework\Response\RedirectResponse;
use Framework\Session\FlashService;
use Psr\Http\Message\ServerRequestInterface;
class LogoutAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var DatabaseAuth
*/
private $auth;
/**
* @var FlashService
*/
private $flashService;
public function __construct(RendererInterface $renderer, DatabaseAuth $auth, FlashService $flashService)
{
$this->renderer = $renderer;
$this->auth = $auth;
$this->flashService = $flashService;
}
public function __invoke(ServerRequestInterface $request)
{
$this->auth->logout();
$this->flashService->success('Vous êtes maintenant déconnecté');
return new RedirectResponse('/gmarche');
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace App\Auth\Action;
use App\Auth\Mailer\PasswordResetMailer;
use App\Auth\UserTable;
use Framework\Database\NoRecordException;
use Framework\Renderer\RendererInterface;
use Framework\Response\RedirectResponse;
use Framework\Session\FlashService;
use Framework\Validator;
use Psr\Http\Message\ServerRequestInterface;
class PasswordForgetAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var UserTable
*/
private $userTable;
/**
* @var PasswordResetMailer
*/
private $mailer;
/**
* @var FlashService
*/
private $flashService;
public function __construct(
RendererInterface $renderer,
UserTable $userTable,
PasswordResetMailer $mailer,
FlashService $flashService
) {
$this->renderer = $renderer;
$this->userTable = $userTable;
$this->mailer = $mailer;
$this->flashService = $flashService;
}
public function __invoke(ServerRequestInterface $request)
{
if ($request->getMethod() === 'GET') {
return $this->renderer->render('@auth/password');
}
$params = $request->getParsedBody();
$validator = (new Validator($params))
->notEmpty('email')
->email('email');
if ($validator->isValid()) {
try {
$user = $this->userTable->findBy('email', $params['email']);
$token = $this->userTable->resetPassword($user->id);
$this->mailer->send($user->email, [
'id' => $user->id,
'token' => $token
]);
$this->flashService->success('Un email vous a été envoyé');
return new RedirectResponse($request->getUri()->getPath());
} catch (NoRecordException $e) {
$errors = ['email' => 'Aucun utilisateur ne correspon à cet email'];
}
} else {
$errors = $validator->getErrors();
}
return $this->renderer->render('@auth/password', compact('errors'));
}
}

View File

@ -0,0 +1,75 @@
<?php
namespace App\Auth\Action;
use App\Auth\User;
use App\Auth\UserTable;
use Framework\Renderer\RendererInterface;
use Framework\Response\RedirectResponse;
use Framework\Router;
use Framework\Session\FlashService;
use Framework\Validator;
use Psr\Http\Message\ServerRequestInterface;
class PasswordResetAction
{
/**
* @var RendererInterface
*/
private $renderer;
/**
* @var UserTable
*/
private $userTable;
/**
* @var Router
*/
private $router;
/**
* @var FlashService
*/
private $flashService;
public function __construct(
RendererInterface $renderer,
UserTable $userTable,
FlashService $flashService,
Router $router
) {
$this->renderer = $renderer;
$this->userTable = $userTable;
$this->router = $router;
$this->flashService = $flashService;
}
public function __invoke(ServerRequestInterface $request)
{
/** @var User $user */
$user = $this->userTable->find($request->getAttribute('id'));
if ($user->getPasswordReset() !== null &&
$user->getPasswordReset() === $request->getAttribute('token') &&
time() - $user->getPasswordResetAt()->getTimestamp() < 600
) {
if ($request->getMethod() === 'GET') {
return $this->renderer->render('@auth/reset');
} else {
$params = $request->getParsedBody();
$validator = (new Validator($params))
->length('password', 4)
->confirm('password');
if ($validator->isValid()) {
$this->userTable->updatePassword($user->getId(), $params['password']);
$this->flashService->success('Votre mot de passe a bien été changé');
return new RedirectResponse($this->router->generateUri('auth.login'));
} else {
$errors = $validator->getErrors();
return $this->renderer->render('@auth/reset', compact('errors'));
}
}
} else {
$this->flashService->error('Token invalid');
return new RedirectResponse($this->router->generateUri('auth.password'));
}
}
}

33
src/Auth/AuthModule.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace App\Auth;
use App\Auth\Action\LoginAction;
use App\Auth\Action\LoginAttemptAction;
use App\Auth\Action\LogoutAction;
use App\Auth\Action\PasswordForgetAction;
use App\Auth\Action\PasswordResetAction;
use Framework\Module;
use Framework\Renderer\RendererInterface;
use Framework\Router;
use Framework\Router\Route;
use Psr\Container\ContainerInterface;
class AuthModule extends Module
{
const DEFINITIONS = __DIR__ . '/config.php';
const MIGRATIONS = __DIR__ . '/db/migrations';
const SEEDS = __DIR__ . '/db/seeds';
public function __construct(ContainerInterface $container, Router $router, RendererInterface $renderer)
{
$renderer->addPath('auth', __DIR__ . '/views');
$router->get($container->get('auth.login'), LoginAction::class, 'auth.login');
$router->post($container->get('auth.login'), LoginAttemptAction::class);
$router->post('/logout', LogoutAction::class, 'auth.logout');
$router->any('/password', PasswordForgetAction::class, 'auth.password');
$router->any('/password/reset/{id:\d+}/{token}', PasswordResetAction::class, 'auth.reset');
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace App\Auth;
use Framework\Auth;
class AuthTwigExtension extends \Twig_Extension
{
/**
* @var Auth
*/
private $auth;
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
public function getFunctions()
{
return [
new \Twig_SimpleFunction('current_user', [$this->auth, 'getUser'])
];
}
}

81
src/Auth/DatabaseAuth.php Normal file
View File

@ -0,0 +1,81 @@
<?php
namespace App\Auth;
use Framework\Auth;
use Framework\Auth\User;
use Framework\Database\NoRecordException;
use Framework\Session\SessionInterface;
class DatabaseAuth implements Auth
{
/**
* @var UserTable
*/
private $userTable;
/**
* @var SessionInterface
*/
private $session;
/**
* @var \App\Auth\User
*/
private $user;
public function __construct(UserTable $userTable, SessionInterface $session)
{
$this->userTable = $userTable;
$this->session = $session;
}
public function login(string $username, string $password): ?User
{
if (empty($username) || empty($password)) {
return null;
}
/** @var \App\Auth\User $user */
$user = $this->userTable->findBy('username', $username);
if ($user && password_verify($password, $user->password)) {
$this->setUser($user);
return $user;
}