Compare commits
210
Commits
8892edd19e
..
v1.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca4b5c98a1 | ||
|
|
254ea744c3 | ||
|
|
5839041e4c | ||
|
|
c8f4e181be | ||
|
|
5ea4e70988 | ||
|
|
3281713534 | ||
|
|
e3b22335e1 | ||
|
|
6cb5e7bba8 | ||
|
|
e6a41fbfab | ||
|
|
e0f2cbac92 | ||
|
|
e32b092749 | ||
|
|
538e0b9aec | ||
|
|
7bad9a69c9 | ||
|
|
f1570d1c42 | ||
|
|
71b9ca68ec | ||
|
|
020227abfe | ||
|
|
8090e493b5 | ||
|
|
75c480496a | ||
|
|
bbf8566fd2 | ||
|
|
9c5e8c66ff | ||
|
|
50b17ec8b6 | ||
|
|
064d94d32a | ||
|
|
aa15850baa | ||
|
|
c7469af952 | ||
|
|
3a60aaaf56 | ||
|
|
dce553d688 | ||
|
|
1a5d4dd0b2 | ||
|
|
5769f600fe | ||
|
|
7a292fe373 | ||
|
|
79287f28cc | ||
|
|
2a688935cc | ||
|
|
a903153fe2 | ||
|
|
ec59a36afb | ||
|
|
9556a2cba6 | ||
|
|
1c44342bbf | ||
|
|
a0f5bdebb5 | ||
|
|
ac751763a0 | ||
|
|
ac0960fb8e | ||
|
|
951f94848c | ||
|
|
8cfef06644 | ||
|
|
e526ba7128 | ||
|
|
ec313ff269 | ||
|
|
347e3c3b6c | ||
|
|
6b1be4d245 | ||
|
|
4834404575 | ||
|
|
90a2f4d083 | ||
|
|
c181cdfb28 | ||
|
|
77a13960f2 | ||
|
|
81e124b144 | ||
|
|
9cd6f10d7e | ||
|
|
a36dbeeef2 | ||
|
|
7ed88ec89f | ||
|
|
c6e9bb6a38 | ||
|
|
e934c1a92c | ||
|
|
baf6fdf952 | ||
|
|
e0da7ae734 | ||
|
|
64f273e65c | ||
|
|
9031236930 | ||
|
|
7ee4b2d8bb | ||
|
|
06c4f46486 | ||
|
|
01d48463b3 | ||
|
|
abe94e498f | ||
|
|
adfd619c5f | ||
|
|
16ee06593c | ||
|
|
5bb5e8ce61 | ||
|
|
b6212b3662 | ||
|
|
fda052d2f3 | ||
|
|
dac7acde87 | ||
|
|
b16b4803c5 | ||
|
|
d54d78ccc5 | ||
|
|
9ff4d6c44d | ||
|
|
a4b02607f3 | ||
|
|
d74c916558 | ||
|
|
ca388e8a52 | ||
|
|
b12fc65ea9 | ||
|
|
4b76a387fe | ||
|
|
7e86c0265d | ||
|
|
86fba55a30 | ||
|
|
78d2d9d291 | ||
|
|
fdca37a79d | ||
|
|
74a56bd72f | ||
|
|
990f57b94e | ||
|
|
dc52c88c03 | ||
|
|
1c2ce07179 | ||
|
|
af8b686e3b | ||
|
|
7a48d707e0 | ||
|
|
156defddc9 | ||
|
|
42f33d9f13 | ||
|
|
69382f912b | ||
|
|
583580e068 | ||
|
|
5b7da3237f | ||
|
|
2bc0c9e56f | ||
|
|
cebb213830 | ||
|
|
8e29b0513a | ||
|
|
7f549a377a | ||
|
|
f87bccb3ab | ||
|
|
212dbd17c0 | ||
|
|
f462fcc597 | ||
|
|
13d306aec9 | ||
|
|
07613bf1cb | ||
|
|
4e28917b96 | ||
|
|
163df3f4ba | ||
|
|
3320dc47c1 | ||
|
|
4484dc8985 | ||
|
|
1d61ff87e4 | ||
|
|
0cc9a8a893 | ||
|
|
fd8fa7695e | ||
|
|
0581f378a5 | ||
|
|
708af80fa2 | ||
|
|
1f828eb022 | ||
|
|
959a182d16 | ||
|
|
ffe66e522c | ||
|
|
730ce7474e | ||
|
|
97dac9ddc4 | ||
|
|
d9f9c053c3 | ||
|
|
63fde92b68 | ||
|
|
8190d3483b | ||
|
|
38b540bbd8 | ||
|
|
f9c081ff26 | ||
|
|
34ca18f34c | ||
|
|
45ffc6e128 | ||
|
|
a9db9cd846 | ||
|
|
2a18e0cb9a | ||
|
|
ff4c94f820 | ||
|
|
30d0e8a14d | ||
|
|
6c58586823 | ||
|
|
9b795b857a | ||
|
|
bc55a2ab89 | ||
|
|
5f0deef7f8 | ||
|
|
e8c3cf01c9 | ||
|
|
06741d09c0 | ||
|
|
70e7b54c65 | ||
|
|
7b0a0cf333 | ||
|
|
f3c4cbd95a | ||
|
|
4f648b47bd | ||
|
|
60d4c7a900 | ||
|
|
2378245dbf | ||
|
|
2cb911ea6f | ||
|
|
16cd95abe2 | ||
|
|
1241e188e9 | ||
|
|
08c27e38b0 | ||
|
|
1a6d730298 | ||
|
|
0ad57c87df | ||
|
|
590e9d7631 | ||
|
|
7e92391113 | ||
|
|
8f0ebfdf80 | ||
|
|
772de0015e | ||
|
|
d8045b5f3c | ||
|
|
bc6880c166 | ||
|
|
4c0cddd617 | ||
|
|
4dcc7f407e | ||
|
|
3f92287829 | ||
|
|
8d22132ef5 | ||
|
|
438b5a69c0 | ||
|
|
84b3a16b6e | ||
|
|
d30e687e21 | ||
|
|
4309f46c86 | ||
|
|
b6d7e5fad5 | ||
|
|
8a5f5f052b | ||
|
|
2e51ff9f4a | ||
|
|
777f4af1aa | ||
|
|
09b332bde7 | ||
|
|
41083c767a | ||
|
|
52c7407519 | ||
|
|
d402dbeaa0 | ||
|
|
d26a15c0ed | ||
|
|
026a13c766 | ||
|
|
4274a6f4a4 | ||
|
|
4404943f8b | ||
|
|
428e26fbfb | ||
|
|
e2d2ff72bb | ||
|
|
94fc8885cf | ||
|
|
ec250be35b | ||
|
|
8390506361 | ||
|
|
fd95e8db91 | ||
|
|
f0082bf0a9 | ||
|
|
7481b8308b | ||
|
|
778cdcc7af | ||
|
|
e07dcae5f5 | ||
|
|
40f9c246b2 | ||
|
|
58285753ed | ||
|
|
26e5e330f4 | ||
|
|
5f5ca6cff5 | ||
|
|
3ee7d57b31 | ||
|
|
f482c19df5 | ||
|
|
946281c884 | ||
|
|
977aef1b26 | ||
|
|
653f9239b2 | ||
|
|
bdf38285e4 | ||
|
|
3101573603 | ||
|
|
93ec2a7e76 | ||
|
|
7badddd72c | ||
|
|
571846ada7 | ||
|
|
591ba18912 | ||
|
|
5bebe231c7 | ||
|
|
aba9a65a3c | ||
|
|
f719246624 | ||
|
|
f23606de64 | ||
|
|
9ce8e7515a | ||
|
|
3550d9aaa7 | ||
|
|
0689f8dcb7 | ||
|
|
a78d20bc32 | ||
|
|
5c06419f15 | ||
|
|
83d52e87ae | ||
|
|
19ee40dead | ||
|
|
73aa22942f | ||
|
|
06313396f5 | ||
|
|
9f6d873a98 | ||
|
|
80dee6de86 | ||
|
|
1f0edd88d2 |
No files matched your search
+51
-67
@@ -1,90 +1,74 @@
|
||||
{
|
||||
"name": "mcnd/lean",
|
||||
"description": "A more-than-micro framework for basic apps",
|
||||
"description": "A micro framework for rapid application development (RAD)",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Dave Mc Nicoll",
|
||||
"email": "mcndave@gmail.com"
|
||||
"email": "info@mcnd.ca"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"vlucas/phpdotenv": "^3.4@dev",
|
||||
"php": "^8.2",
|
||||
"php-di/php-di": "dev-master",
|
||||
"league/route": "dev-master",
|
||||
"mcnd/storage": "dev-master",
|
||||
"mcnd/ulmus": "dev-master",
|
||||
"mcnd/picea": "dev-master",
|
||||
"mcnd/picea-ui": "dev-master",
|
||||
"mcnd/cronard": "dev-master",
|
||||
"mcnd/tell": "dev-master",
|
||||
"mcnd/dump": "dev-master",
|
||||
"mcnd/notes": "dev-master",
|
||||
"mcnd/notes-route": "dev-master",
|
||||
"mcnd/notes-security": "dev-master",
|
||||
"mcnd/notes-tell": "dev-master",
|
||||
"swiftmailer/swiftmailer": "^6.2@dev",
|
||||
"league/route": "^5.0.0-dev",
|
||||
"laminas/laminas-diactoros": "2.5.x-dev",
|
||||
"laminas/laminas-httphandlerrunner": "1.3.x-dev",
|
||||
"ext-json": "*",
|
||||
"ext-posix": "*",
|
||||
"ext-xmlreader": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"league/route": "^7.0.0-dev",
|
||||
"laminas/laminas-diactoros": "3.9.x-dev",
|
||||
"laminas/laminas-httphandlerrunner": "2.14.x-dev",
|
||||
"vlucas/phpdotenv": "^3.4@dev",
|
||||
"middlewares/whoops": "dev-master",
|
||||
"ralouphie/getallheaders": "dev-master"
|
||||
"mcnd/dump": "~1.0.0",
|
||||
"mcnd/storage": "~1.0.0",
|
||||
"mcnd/event": "~1.0.0",
|
||||
"mcnd/tell": "~1.0.0",
|
||||
"mcnd/ulmus": "^1.0 || ^2.0",
|
||||
"mcnd/kash": "~1.0.0",
|
||||
"mcnd/taxus": "1.1.0",
|
||||
"mcnd/notes": "^2.1.1",
|
||||
"mcnd/thebugs": "^1.0.0",
|
||||
"psr/simple-cache": "*"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/cronard.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/storage.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/ulmus.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/picea.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/picea-ui.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/notes.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/notes-route.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/notes-security.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/notes-tell.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/tell.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/lean-console.git"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://git.mcnd.ca/mcndave/dump.git"
|
||||
"type": "composer",
|
||||
"url": "https://git.mcnd.ca/api/packages/mcndave/composer"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Lean\\": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"Lean\\Composer::postInstall"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Lean\\Composer::postUpdate"
|
||||
]
|
||||
},
|
||||
"extra" : {
|
||||
"lean" : {
|
||||
"autoload": {
|
||||
"definitions" : [
|
||||
"meta/definitions/authorize.php",
|
||||
"meta/definitions/email.php",
|
||||
"meta/definitions/event.php",
|
||||
"meta/definitions/http.php",
|
||||
"meta/definitions/orm.php",
|
||||
"meta/definitions/language.php",
|
||||
"meta/definitions/routes.php",
|
||||
"meta/definitions/software.php",
|
||||
"meta/definitions/template.php"
|
||||
],
|
||||
"config": [
|
||||
"meta/config.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
return [];
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
use Lean\Factory\HttpFactoryInterface;
|
||||
|
||||
use Ulmus\User\{
|
||||
Authorize\CookieAuthentication,
|
||||
Authorize\HeaderAuthentication,
|
||||
Entity,
|
||||
|
||||
Lib\Authenticate,
|
||||
Lib\Authorize,
|
||||
Middleware\AuthenticationMiddleware,
|
||||
Middleware\HeaderAuthenticationMiddleware,
|
||||
Middleware\CookieAuthenticationMiddleware,
|
||||
Middleware\PostRequestAuthenticationMiddleware,
|
||||
Middleware\LocalMacAddressMiddleware,
|
||||
Authorize\MacAddressAuthentication,
|
||||
Authorize\PostRequestAuthentication};
|
||||
|
||||
use Picea\Picea;
|
||||
|
||||
use Storage\{ Cookie, Session };
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use function DI\{get, autowire, create};
|
||||
|
||||
return [
|
||||
Authorize::class => create(Authorize::class)->constructor(get(Entity\UserInterface::class), get(Session::class), get(Cookie::class), get('authentication.method')),
|
||||
|
||||
Authenticate::class => create(Authenticate::class)->constructor(get(Entity\UserInterface::class), get(Session::class), get(Cookie::class), get('authentication.method')),
|
||||
|
||||
AuthenticationMiddleware::class => create(AuthenticationMiddleware::class)->constructor(get(Authorize::class), get('authorize.error')),
|
||||
|
||||
HeaderAuthentication::class => autowire(HeaderAuthentication::class),
|
||||
|
||||
HeaderAuthenticationMiddleware::class => create(HeaderAuthenticationMiddleware::class)->constructor(get(HeaderAuthentication::class)),
|
||||
|
||||
CookieAuthentication::class => autowire(CookieAuthentication::class),
|
||||
|
||||
CookieAuthenticationMiddleware::class => create(CookieAuthenticationMiddleware::class)->constructor(get(CookieAuthentication::class), get(Cookie::class)),
|
||||
|
||||
PostRequestAuthentication::class => create(PostRequestAuthentication::class)->constructor("email", "email", "password"),
|
||||
|
||||
PostRequestAuthenticationMiddleware::class => create(PostRequestAuthenticationMiddleware::class)->constructor(get(PostRequestAuthentication::class)),
|
||||
|
||||
MacAddressAuthentication::class => create(MacAddressAuthentication::class)->constructor(get(Authenticate::class), "mac"),
|
||||
|
||||
LocalMacAddressMiddleware::class => create(LocalMacAddressMiddleware::class)->constructor(get(Entity\UserInterface::class), get('authentication.error'), get(MacAddressAuthentication::class)),
|
||||
|
||||
'authentication.method' => null,
|
||||
|
||||
'authorize.error' => function(ContainerInterface $c) {
|
||||
return function(array $errorData) use ($c) {
|
||||
return $c->get(HttpFactoryInterface::class)::createJsonResponse($errorData + [
|
||||
'api.error' => "Authorization failed",
|
||||
'api.datetime' => (new \DateTime)->format(\DateTime::ATOM),
|
||||
], 403);
|
||||
};
|
||||
},
|
||||
|
||||
'authentication.error' => function($c, Picea $picea) {
|
||||
return function($message) use ($c, $picea) {
|
||||
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/500', [
|
||||
|
||||
'title' => "Authentication failed",
|
||||
'subtitle' => "",
|
||||
'message' => $message,
|
||||
]), 401);
|
||||
};
|
||||
},
|
||||
];
|
||||
@@ -4,12 +4,12 @@ use function DI\autowire, DI\create, DI\get;
|
||||
|
||||
use TheBugs\Email\EmailConfiguration,
|
||||
TheBugs\Email\MailerInterface,
|
||||
TheBugs\Email\SwiftMailer;
|
||||
TheBugs\Email\SymfonyMailer;
|
||||
|
||||
return [
|
||||
EmailConfiguration::class => function($c) {
|
||||
$email = new EmailConfiguration( EmailConfiguration::AUTH_TYPE_SMTP );
|
||||
$email->smtpHost = getenv('SMTP_HOST');
|
||||
$email->smtpHost = getenv('SMTP_HOSTNAME');
|
||||
$email->smtpPort = getenv('SMTP_PORT');
|
||||
$email->smtpUsername = getenv('SMTP_USERNAME');
|
||||
$email->smtpPassword = getenv('SMTP_PASSWORD');
|
||||
@@ -20,5 +20,5 @@ return [
|
||||
return $email;
|
||||
},
|
||||
|
||||
MailerInterface::class => autowire(SwiftMailer::class),
|
||||
MailerInterface::class => autowire(SymfonyMailer::class),
|
||||
];
|
||||
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
use Picea\Picea;
|
||||
use Lean\{ Lean, Routing, Event\RoutingCompileRoutes, Event\RoutingMapRoutes };
|
||||
use Mcnd\Event;
|
||||
use Taxus\Taxus;
|
||||
use Storage\Session;
|
||||
use Notes\Route\Attribute\Method\Route;
|
||||
use Notes\Security\SecurityHandler;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
|
||||
use Ulmus\User\Entity\UserInterface;
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
|
||||
return [
|
||||
Event\EventManager::class => autowire(Event\EventManager::class),
|
||||
|
||||
Event\EventMiddleware::class => function($c) {
|
||||
$mw = new Event\EventMiddleware($c, $c->get(Event\EventManager::class));
|
||||
$mw->fromAttributes($c->get(Notes\Event\EventFetcher::class));
|
||||
$mw->fromDefinition($c->get(Event\EventDefinition::class));
|
||||
|
||||
return $mw;
|
||||
},
|
||||
|
||||
Event\EventDefinition::class => function($c) {
|
||||
$extension = $c->has(\Picea\Extension\UrlExtension::class) ? $c->get(\Picea\Extension\UrlExtension::class) : null;
|
||||
$user = $c->get(UserInterface::class);
|
||||
|
||||
return new Event\EventDefinition([
|
||||
new class($extension) implements RoutingCompileRoutes {
|
||||
public function __construct(
|
||||
protected ? \Picea\Extension\UrlExtension $extension,
|
||||
) {}
|
||||
|
||||
public function execute(Routing $routing, Route $attribute) : void
|
||||
{
|
||||
if (null !== ($name = $attribute->name ?? null)) {
|
||||
$this->extension->registerRoute($name, $attribute->getRoute(), $attribute->class, $attribute->classMethod, (array) ( $attribute->method ));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
new class($user) implements RoutingMapRoutes {
|
||||
|
||||
public function __construct(
|
||||
protected UserInterface $user
|
||||
) {}
|
||||
|
||||
public function execute(Routing $routing, ContainerInterface $container, ServerRequestInterface & $request, Route $attribute) : void
|
||||
{
|
||||
$class = $attribute->class;
|
||||
$method = $attribute->classMethod;
|
||||
|
||||
$request = $request->withAttribute('lean.route', $attribute);
|
||||
|
||||
# Checking if user needs to be logged
|
||||
if ( $container->has(SecurityHandler::class) ){
|
||||
$securityHandler = $container->get(SecurityHandler::class);
|
||||
|
||||
if ( $redirect = $securityHandler->verify($class, $method) ) {
|
||||
if (! $this->user->loggedIn() ) {
|
||||
if ($container->has(Session::class)) {
|
||||
$container->get(Session::class)->set('redirectedFrom', (string)$request->getUri());
|
||||
}
|
||||
|
||||
$routing->response = $redirect;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $securityHandler->isLocked($class, $method) && $container->has(Taxus::class)) {
|
||||
if ( $forbidden = $securityHandler->taxus($class, $method, $this->user) ) {
|
||||
$routing->response = $forbidden;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($container->has(Picea::class)) {
|
||||
$container->get(Picea::class)->globalVariables['route'] = $attribute;
|
||||
}
|
||||
|
||||
if ($container->has(Session::class)) {
|
||||
$container->get(Session::class)->set("lean.route", $attribute);
|
||||
}
|
||||
}
|
||||
},
|
||||
]);
|
||||
},
|
||||
|
||||
Notes\Event\EventFetcher::class => function($c) {
|
||||
$fetcher = new Notes\Event\EventFetcher(null, null, $c->get('events.caching'));
|
||||
|
||||
$fetcher->setFolderList(array_map(function($item) {
|
||||
return $item;
|
||||
}, $c->get(Lean::class)->getEvents()));
|
||||
|
||||
return $fetcher;
|
||||
},
|
||||
];
|
||||
@@ -1,12 +1,18 @@
|
||||
<?php
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
use Lean\ApplicationStrategy\{ ThrowableHandler, ThrowableHandlerInterface, NotFoundDecoratorInterface, NotFoundDecorator, MethodNotAllowedInterface, };
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
use Zend\Diactoros\ServerRequestFactory;
|
||||
use Zend\HttpHandlerRunner\Emitter\EmitterInterface,
|
||||
Zend\HttpHandlerRunner\Emitter\SapiEmitter;
|
||||
use Laminas\{ Diactoros\ServerRequestFactory, HttpHandlerRunner\Emitter\EmitterInterface, HttpHandlerRunner\Emitter\SapiEmitter };
|
||||
use Lean\Factory\{ HttpFactory, HttpFactoryInterface };
|
||||
use Picea\Picea;
|
||||
use Storage\Cookie,
|
||||
Storage\Session,
|
||||
Storage\SessionMiddleware;
|
||||
|
||||
use function DI\{ autowire, create, get };
|
||||
|
||||
return [
|
||||
ServerRequestInterface::class => function ($c) {
|
||||
@@ -16,4 +22,37 @@ return [
|
||||
},
|
||||
|
||||
EmitterInterface::class => create(SapiEmitter::class),
|
||||
ThrowableHandlerInterface::class => autowire(ThrowableHandler::class),
|
||||
NotFoundDecoratorInterface::class => autowire(NotFoundDecorator::class),
|
||||
# MethodNotAllowedInterface::class => autowire(MethodNotAllowedDecorator::class),
|
||||
|
||||
HttpFactoryInterface::class => create(HttpFactory::class),
|
||||
|
||||
Cookie::class => create(Cookie::class)->constructor([
|
||||
'secure' => true,
|
||||
'path' => getenv('URL_BASE') ?: '/',
|
||||
'httponly' => true,
|
||||
'samesite'=> "Lax",
|
||||
], getenv("LEAN_RANDOM")),
|
||||
|
||||
Session::class => create(Session::class)->constructor(get(Cookie::class), [ 'path' => getenv('URL_BASE') ?: '/', ]),
|
||||
|
||||
SessionMiddleware::class => create(SessionMiddleware::class)->constructor(get(Cookie::class), [
|
||||
'name' => "lean_sess_" . substr(md5(getenv("LEAN_RANDOM")), 0, 12),
|
||||
'path' => getenv('URL_BASE') ?: '/',
|
||||
'samesite' => getenv("LEAN_COOKIE_SAMESITE") ?? "Lax",
|
||||
'secure' => (bool) getenv('LEAN_COOKIE_SECURE') ?? false
|
||||
]),
|
||||
|
||||
'error.401' => function(ContainerInterface $c, Picea $picea) {
|
||||
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml("lean/error/401", []), 401);
|
||||
},
|
||||
|
||||
'error.404' => function(ContainerInterface $c, Picea $picea) {
|
||||
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml("lean/error/404", []), 404);
|
||||
},
|
||||
|
||||
'error.500' => function(ContainerInterface $c, Picea $picea) {
|
||||
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml("lean/error/500", []), 500);
|
||||
},
|
||||
];
|
||||
@@ -1,31 +1,36 @@
|
||||
<?php
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
use Tell\I18n;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
use Notes\Tell\LanguageHandler;
|
||||
use function DI\autowire, DI\create, DI\get, DI\add;
|
||||
|
||||
return [
|
||||
Tell\I18n::class => create( Tell\I18n::class ) ->constructor(
|
||||
get(Tell\Reader\JsonReader::class),
|
||||
get(Tell\Reader\PhpReader::class),
|
||||
getenv("DEBUG") ? create(Tell\PrintMissingKey::class) : get('tell.fallback')
|
||||
),
|
||||
I18n::class => function(ContainerInterface $container) {
|
||||
$i18n = new I18n($container->get(Tell\Reader\JsonReader::class), $container->get(Tell\Reader\PhpReader::class), $container->get('tell.fallback'));
|
||||
$i18n->locale(getenv('DEFAULT_LOCAL'));
|
||||
$i18n->initialize(! getenv("DEBUG"));
|
||||
|
||||
'tell.fallback' => function($c) {
|
||||
$tell = new Tell\I18n( $c->get(Tell\Reader\PhpReader::class) );
|
||||
$tell->locale = "en_US";
|
||||
|
||||
return $tell;
|
||||
return $i18n;
|
||||
},
|
||||
|
||||
# TODO -- accept folders from Lean Apps
|
||||
Tell\Reader\PhpReader::class => function($c) {
|
||||
return new Tell\Reader\PhpReader($c->get(Lean\Lean::class)->getI18n('php'), true);
|
||||
'tell.fallback' => function(ContainerInterface $container) {
|
||||
$i18n = new Tell\I18n( $container->get(Tell\Reader\JsonReader::class), $container->get(Tell\Reader\PhpReader::class), new Tell\PrintMissingKey() );
|
||||
$i18n->locale(getenv('DEFAULT_LOCAL_FALLBACK') ?: "en_US");
|
||||
$i18n->initialize(true);
|
||||
|
||||
return $i18n;
|
||||
},
|
||||
|
||||
Tell\Reader\JsonReader::class => function($c) {
|
||||
return new Tell\Reader\JsonReader($c->get(Lean\Lean::class)->getI18n('json'), true);
|
||||
Tell\Reader\PhpReader::class => function(ContainerInterface $container) {
|
||||
return new Tell\Reader\PhpReader($container->get(Lean\Lean::class)->getI18n('php'), false);
|
||||
},
|
||||
|
||||
LanguageHandler::class => autowire(LanguageHandler::class),
|
||||
Tell\Reader\JsonReader::class => function(ContainerInterface $container) {
|
||||
return new Tell\Reader\JsonReader($container->get(Lean\Lean::class)->getI18n('json'), true, \JSON_PRETTY_PRINT);
|
||||
},
|
||||
|
||||
'lean.autoload' => add([
|
||||
I18n::class,
|
||||
]),
|
||||
];
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
use Ulmus\Container\AdapterProxy;
|
||||
use Ulmus\ConnectionAdapter;
|
||||
|
||||
use function DI\autowire, DI\create, DI\get, DI\add;
|
||||
|
||||
return [
|
||||
'lean.autoload' => add([
|
||||
AdapterProxy::class,
|
||||
]),
|
||||
|
||||
AdapterProxy::class => function (Psr\Container\ContainerInterface $c) {
|
||||
$proxy = new AdapterProxy();
|
||||
|
||||
$autoload = $c->get(\Lean\Lean::class)->getConnectionAdapters();
|
||||
|
||||
$proxy->push($autoload);
|
||||
|
||||
return $proxy;
|
||||
},
|
||||
];
|
||||
+38
-46
@@ -1,35 +1,32 @@
|
||||
<?php
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
use function DI\autowire, DI\create, DI\get, DI\add;
|
||||
|
||||
use League\Route\Strategy\ApplicationStrategy,
|
||||
League\Route\Http\Exception\NotFoundException,
|
||||
League\Route\Router;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface,
|
||||
Psr\Container\ContainerInterface,
|
||||
Psr\Http\Message\ResponseInterface,
|
||||
Psr\Http\Message\ServerRequestInterface,
|
||||
Psr\Http\Server\MiddlewareInterface,
|
||||
Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
use TheBugs\JavascriptMiddleware;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
use Cronard\CronardMiddleware;
|
||||
|
||||
use Tuupola\Middleware\HttpBasicAuthentication;
|
||||
|
||||
use Notes\Route\RouteFetcher;
|
||||
|
||||
use Ulmus\User\Lib\Authenticate;
|
||||
use Notes\Breadcrumb\Breadcrumb;
|
||||
|
||||
use Storage\SessionMiddleware;
|
||||
|
||||
use Ulmus\User\Middleware\{
|
||||
HeaderAuthenticationMiddleware,
|
||||
PostRequestAuthenticationMiddleware
|
||||
};
|
||||
|
||||
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
|
||||
|
||||
return [
|
||||
Lean\Routing::class => autowire(Lean\Routing::class),
|
||||
|
||||
RouteFetcher::class => function($c) {
|
||||
$fetcher = new RouteFetcher();
|
||||
$fetcher = new RouteFetcher(null, [], null, $c->get('routes.caching'));
|
||||
|
||||
$fetcher->setFolderList(array_map(function($item) {
|
||||
return $item;
|
||||
@@ -38,50 +35,45 @@ return [
|
||||
return $fetcher;
|
||||
},
|
||||
|
||||
ApplicationStrategy::class => function($c) {
|
||||
return new class($c->get(Picea\Picea::class)) extends ApplicationStrategy {
|
||||
Breadcrumb::class => function($c) {
|
||||
$breadcrumb = new Breadcrumb(null, [], null, $c->get('breadcrumbs.caching'));
|
||||
|
||||
public Picea\Picea $picea;
|
||||
$breadcrumb->setFolderList(array_map(function($item) {
|
||||
return $item;
|
||||
}, $c->get(Lean\Lean::class)->getRoutable()));
|
||||
|
||||
public function __construct(Picea\Picea $picea) {
|
||||
$this->picea = $picea;
|
||||
}
|
||||
|
||||
public function getNotFoundDecorator(NotFoundException $exception): MiddlewareInterface
|
||||
{
|
||||
return new class($this->picea) implements MiddlewareInterface {
|
||||
|
||||
protected Picea\Picea $picea;
|
||||
|
||||
public function __construct(Picea\Picea $picea) {
|
||||
$this->picea = $picea;
|
||||
}
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
return new Zend\Diactoros\Response\HtmlResponse($this->picea->renderHtml("lean/error/404", [], $this), 404);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
return $breadcrumb;
|
||||
},
|
||||
|
||||
'routes.middlewares' => [ "dump", "errorHandler", SessionMiddleware::class, CronardMiddleware::class, HttpBasicAuthentication::class, JavascriptMiddleware::class ],
|
||||
ApplicationStrategy::class => autowire(\Lean\ApplicationStrategy::class),
|
||||
|
||||
'routes.list' => function($c) {
|
||||
'routes.middlewares' => [
|
||||
"dump", "errorHandler", SessionMiddleware::class, CronardMiddleware::class, Mcnd\Event\EventMiddleware::class, Mcnd\CLI\CliMiddleware::class,
|
||||
],
|
||||
|
||||
'app.middlewares' => add([
|
||||
# HeaderAuthenticationMiddleware::class,
|
||||
# PostRequestAuthenticationMiddleware::class,
|
||||
]),
|
||||
|
||||
Lean\Routing\RouteDefinitionInterface::class => function($c) {
|
||||
return function (ContainerInterface $container) {
|
||||
$router = $container->get(Router::class);
|
||||
|
||||
foreach($container->get('routes.middlewares') as $middleware) {
|
||||
if ( $container->has($middleware) ) {
|
||||
$router->middleware($container->get($middleware));
|
||||
foreach([ 'routes.middlewares', 'app.middlewares' ] as $key) {
|
||||
if ($container->has($key)) {
|
||||
foreach ($container->get($key) as $i => $middleware) {
|
||||
if ($container->has($middleware)) {
|
||||
$router->middleware($container->get($middleware));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$routing = $container->get(Lean\Routing::class);
|
||||
$router->addPatternMatcher('email', '[^@ \t\r\n/]+@[^@ \t\r\n/]+\.[^@ \t\r\n/]+');
|
||||
$router->addPatternMatcher('ids', '\b\d[\d,]*\b');
|
||||
|
||||
$routing = $container->get(Lean\Routing::class);
|
||||
$routing->registerRoute($container, getenv('URL_BASE'));
|
||||
|
||||
return $router;
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
<?php
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
|
||||
use Storage\Session;
|
||||
|
||||
use Taxus\{ Privilege, Taxus, PermissionGrantInterface, DefaultPermissionGrant };
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
return [
|
||||
Taxus::class => function ($c) {
|
||||
return ( new Taxus( $c->get(PermissionGrantInterface::class) ) )->add(
|
||||
[ new Privilege("dev", "Is a developper of this application."), "is_dev" ],
|
||||
[ new Privilege("admin", "Can manage mostly everything from this application."), "is_admin" ],
|
||||
[ new Privilege("user", "Is an authenticated user."), "is_user" ],
|
||||
[ new Privilege("anonymous", "Is an anonymous (unauthenticated) user."), "is_anonymous" ],
|
||||
);
|
||||
},
|
||||
|
||||
PermissionGrantInterface::class => create(DefaultPermissionGrant::class)->constructor(get(ServerRequestInterface::class), get(Session::class)),
|
||||
];
|
||||
@@ -1,26 +1,20 @@
|
||||
<?php
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
|
||||
use Zend\Diactoros\Response\TextResponse;
|
||||
use Tell\I18n;
|
||||
|
||||
use TheBugs\JavascriptMiddleware;
|
||||
|
||||
use Cronard\CronardMiddleware,
|
||||
Notes\Cronard\TaskFetcher;
|
||||
|
||||
use Lean\Lean;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
use Storage\Cookie,
|
||||
Storage\Session,
|
||||
Storage\SessionMiddleware;
|
||||
use function DI\autowire, DI\create, DI\get, DI\add;
|
||||
|
||||
$dir = dirname(__DIR__, 2);
|
||||
|
||||
return [
|
||||
'lean.default' => [
|
||||
'picea' => [
|
||||
'context' => "Lean\\View",
|
||||
'context' => \Lean\View::class,
|
||||
|
||||
'view' => [
|
||||
[
|
||||
@@ -28,11 +22,18 @@ return [
|
||||
'order' => 10,
|
||||
],
|
||||
[
|
||||
'path' => getenv("PROJECT_PATH") . "/vendor/mcnd/lean/view/",
|
||||
'order' => 99,
|
||||
'path' => implode(DIRECTORY_SEPARATOR, [ $dir, "view", '' ]),
|
||||
'order' => 1000,
|
||||
],
|
||||
],
|
||||
|
||||
'asset' => [
|
||||
[
|
||||
'path' => implode(DIRECTORY_SEPARATOR, [ getenv("PROJECT_PATH"), "asset", '' ]),
|
||||
'order' => 10
|
||||
]
|
||||
],
|
||||
|
||||
'extensions' => [],
|
||||
],
|
||||
|
||||
@@ -64,34 +65,16 @@ return [
|
||||
'cronard' => [],
|
||||
],
|
||||
|
||||
'lean.autoload' => add([
|
||||
Lean::class,
|
||||
]),
|
||||
|
||||
\Lean\Factory\HttpFactoryInterface::class => autowire(\Lean\Factory\HttpFactory::class),
|
||||
|
||||
Lean::class => autowire(Lean::class),
|
||||
|
||||
CronardMiddleware::class => function($c) {
|
||||
$cronardMiddleware = new CronardMiddleware($c, getenv('CRON_KEY'), function() : ResponseInterface {
|
||||
return new TextResponse(sprintf("%s - cron task begin...", date('Y-m-d H:i:s')));
|
||||
});
|
||||
|
||||
return $cronardMiddleware->fromFile(getenv("META_PATH")."/crontab.php")->fromAnnotations($c->get(TaskFetcher::class));
|
||||
},
|
||||
|
||||
TaskFetcher::class => function($c) {
|
||||
$fetcher = new TaskFetcher();
|
||||
|
||||
$fetcher->setFolderList(array_map(function($item) {
|
||||
return $item;
|
||||
}, $c->get(Lean::class)->getCronard()));
|
||||
|
||||
return $fetcher;
|
||||
},
|
||||
|
||||
JavascriptMiddleware::class => create(JavascriptMiddleware::class),
|
||||
|
||||
Cookie::class => create(Cookie::class)->constructor([ 'secure' => true, 'path' => getenv('URL_BASE'), ], getenv("LEAN_RANDOM")),
|
||||
|
||||
Session::class => create(Session::class)->constructor(get(Cookie::class), [ 'path' => getenv('URL_BASE'), ]),
|
||||
|
||||
SessionMiddleware::class => create(SessionMiddleware::class)->constructor(get(Cookie::class), [ 'path' => getenv('URL_BASE'), 'name' => "lean_sess_" . substr(md5(getenv("LEAN_RANDOM")), 0, 12) ]),
|
||||
|
||||
'git.commit' => function($c) {
|
||||
if ( getenv("DEBUG") ) {
|
||||
return uniqid("debug_");
|
||||
@@ -100,14 +83,16 @@ return [
|
||||
$gitdir = getenv("PROJECT_PATH") . DIRECTORY_SEPARATOR . ".git" . DIRECTORY_SEPARATOR;
|
||||
|
||||
if ( file_exists($gitdir . "HEAD") ) {
|
||||
if (false !== ($currentBranch = file_get_contents($gitdir . "HEAD"))) {
|
||||
if (false !== ($currentBranch = file_get_contents($gitdir . "HEAD"))) {
|
||||
$file = explode(": ", $currentBranch)[1];
|
||||
$path = $gitdir . str_replace("/", DIRECTORY_SEPARATOR, trim($file, " \t\n\r"));
|
||||
|
||||
return trim(file_get_contents($path), " \t\n\r");
|
||||
return file_exists($path) ? trim(file_get_contents($path), " \t\n\r") : null;
|
||||
}
|
||||
}
|
||||
|
||||
return "gitless-project";
|
||||
},
|
||||
|
||||
Kash\CacheInvalidator::class => create(Kash\CacheInvalidator::class)->constructor(getenv('CACHE_PATH')."/version.cache", (bool) getenv('DEBUG')),
|
||||
];
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
use Ulmus\{ ConnectionAdapter, Container\AdapterProxy };
|
||||
use Lean\Lean;
|
||||
|
||||
return [
|
||||
'lean:adapter.sqlite' => function($c) {
|
||||
$adapter = new ConnectionAdapter('sqlite', $c->get('config')['ulmus'], true);
|
||||
$adapter->resolveConfiguration();
|
||||
|
||||
return $adapter;
|
||||
}
|
||||
];
|
||||
@@ -2,84 +2,13 @@
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
|
||||
use Zend\Diactoros\Response\HtmlResponse;
|
||||
use Picea\{ Language\LanguageRegistration, };
|
||||
|
||||
use Picea\Picea,
|
||||
Picea\Caching\Cache,
|
||||
Picea\Caching\Opcache,
|
||||
Picea\Compiler,
|
||||
Picea\Compiler\Context,
|
||||
Picea\Compiler\BaseContext,
|
||||
Picea\Extension\LanguageHandler,
|
||||
Picea\Extension\LanguageExtension,
|
||||
Picea\Extension\TitleExtension,
|
||||
Picea\Extension\UrlExtension,
|
||||
Picea\FileFetcher,
|
||||
Picea\Language\DefaultRegistrations,
|
||||
Picea\Method\Request,
|
||||
Picea\Ui\Method,
|
||||
Picea\Ui\Ui;
|
||||
use Picea\Extension\{ LanguageHandlerInterface, };
|
||||
|
||||
use Picea\Ui\{ Ui };
|
||||
|
||||
return [
|
||||
Picea::class => function($c) {
|
||||
return new Picea(function($html) {
|
||||
return new HtmlResponse( $html );
|
||||
}, $c->get(Context::class), $c->get(Cache::class), $c->get(Compiler::class), null, $c->get(FileFetcher::class), null, getenv("DEBUG"));
|
||||
},
|
||||
|
||||
Context::class => function($c) {
|
||||
return new BaseContext( $c->get(Lean\Lean::class)->getPiceaContext() );
|
||||
},
|
||||
|
||||
Compiler::class => function($c) {
|
||||
return new Compiler(new class(array_merge([
|
||||
$c->get(LanguageExtension::class),
|
||||
$c->get(TitleExtension::class),
|
||||
$c->get(UrlExtension::class),
|
||||
$c->get(Method\Form::class),
|
||||
$c->get(Method\Pagination::class),
|
||||
$c->get(Request::class),
|
||||
], array_map(fn($class) => $c->get($class), $c->get(Lean\Lean::class)->getPiceaExtensions() ))) extends DefaultRegistrations {
|
||||
|
||||
public function registerAll(Compiler $compiler) : void
|
||||
{
|
||||
parent::registerAll($compiler);
|
||||
( new Ui() )->registerFormExtension($compiler);
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
Request::class => autowire(Request::class),
|
||||
|
||||
Method\Form::class => autowire(Method\Form::class),
|
||||
|
||||
Method\Pagination::class => autowire(Method\Pagination::class),
|
||||
|
||||
LanguageExtension::class => create(LanguageExtension::class)->constructor(get(LanguageHandler::class)),
|
||||
|
||||
LanguageHandler::class => function($c) {
|
||||
return new class( $c->get(Tell\I18n::class) ) implements LanguageHandler {
|
||||
public Tell\I18n $tell;
|
||||
|
||||
public function __construct(Tell\I18n $tell) {
|
||||
$this->tell = $tell;
|
||||
}
|
||||
|
||||
public function languageFromKey(string $key, array $variables = []) #: array|string
|
||||
{
|
||||
return $this->tell->fromKey($key, $variables) ?: "";
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
TitleExtension::class => autowire(TitleExtension::class),
|
||||
|
||||
UrlExtension::class => create(UrlExtension::class)->constructor(get(Context::class), getenv("URL_BASE"), get('git.commit')),
|
||||
|
||||
Cache::class => create(Opcache::class)->constructor(getenv("CACHE_PATH"), get(Context::class)),
|
||||
|
||||
FileFetcher::class => function($c) {
|
||||
return new FileFetcher($c->get(Lean\Lean::class)->getViewPaths());
|
||||
},
|
||||
LanguageHandlerInterface::class => autowire(\Lean\LanguageHandler::class),
|
||||
LanguageRegistration::class => create(\Lean\PiceaDefaultRegistration::class)->constructor(get('picea.extensions'), [], [], get(Ui::class), null),
|
||||
];
|
||||
@@ -1,4 +1,19 @@
|
||||
{
|
||||
|
||||
"moment": {
|
||||
"today": "today",
|
||||
"week": "week"
|
||||
},
|
||||
|
||||
"interval": {
|
||||
"years": "{$years} year(s)",
|
||||
"months": "{$months} month(s)",
|
||||
"days": "{$days} day(s)",
|
||||
"hours": "{$hours} hour(s)",
|
||||
"minutes": "{$minutes} minutes(s)",
|
||||
"seconds": "{$seconds} second(s)"
|
||||
},
|
||||
|
||||
"month": {
|
||||
"list": [
|
||||
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "Décember"
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
{
|
||||
"moment": {
|
||||
"today": "aujourd'hui",
|
||||
"week": "semaine"
|
||||
},
|
||||
|
||||
"interval": {
|
||||
"years": "{$years} années(s)",
|
||||
"months": "{$months} mois",
|
||||
"days": "{$days} jour(s)",
|
||||
"hours": "{$hours} heure(s)",
|
||||
"minutes": "{$minutes} minutes(s)",
|
||||
"seconds": "{$seconds} seconde(s)"
|
||||
},
|
||||
|
||||
"month": {
|
||||
"list": [
|
||||
"Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"back": "Revenir à la page précédente"
|
||||
},
|
||||
"500": {
|
||||
"title": "Une erreur semble s'être produite",
|
||||
"title": "Une erreur s'est produite",
|
||||
"page-title": "Une erreur semble s'être produite",
|
||||
"subtitle": "L'action que vous avez tenté d'effectué semble avoir échoué.",
|
||||
"message": "Un message d'erreur a été envoyé au développeur de cette application.",
|
||||
|
||||
+17
-19
@@ -1,11 +1,12 @@
|
||||
APP_ENV = dev
|
||||
DEBUG = 1
|
||||
KEYS = "dev:dev"
|
||||
KEYS = "dev"
|
||||
CRON_KEY = dev
|
||||
|
||||
# Route
|
||||
URL_BASE = ""
|
||||
APP_URL = "dev.cslsj.qc.ca"
|
||||
APP_URL = "myapp.ca"
|
||||
ASSET_PATH = "static/"
|
||||
|
||||
# Path
|
||||
CACHE_DIR = "var/cache"
|
||||
@@ -13,6 +14,7 @@ META_DIR = "meta"
|
||||
LOGS_DIR = "var/logs"
|
||||
PUBLIC_DIR = "public"
|
||||
PRIVATE_DIR = "private"
|
||||
TMP_DIR = "var/tmp"
|
||||
VIEW_DIR = "view"
|
||||
I18N_DIR = "i18n"
|
||||
|
||||
@@ -21,23 +23,19 @@ DEFAULT_LOCAL = "fr_CA.UTF-8"
|
||||
DEFAULT_TIME = "fr.UTF-8"
|
||||
DEFAULT_TIME_FALLBACK = "french.UTF-8"
|
||||
|
||||
# MS Authentication
|
||||
# MS_OAUTH_CLIENT_ID = ""
|
||||
# MS_OAUTH_CLIENT_SECRET = ""
|
||||
|
||||
# MS Graph
|
||||
# MS_GRAPH_CLIENT_ID = ""
|
||||
# MS_GRAPH_CLIENT_SECRET = ""
|
||||
# MS_GRAPH_ACCESS_TOKEN_URL = ""
|
||||
|
||||
# Database
|
||||
DATABASE_PORT = "3306"
|
||||
DATABASE_HOST = "appsdb.cslsj.qc.ca"
|
||||
DATABASE_NAME = ""
|
||||
DATABASE_USERNAME = ""
|
||||
DATABASE_PASSWORD = ""
|
||||
DATABASE_CHARSET = "utf8"
|
||||
DATABASE_ADAPTER = "MariaDB"
|
||||
SQLITE_PATH = "var/myapp.sqlite3"
|
||||
SQLITE_ADAPTER = "SQLite"
|
||||
SQLITE_PRAGMA_BEGIN = "foreign_keys=ON,synchronous=NORMAL"
|
||||
SQLITE_PRAGMA_DEBUG_BEGIN = "journal_mode=WAL"
|
||||
SQLITE_PRAGMA_CLOSE = "analysis_limit=500,optimize"
|
||||
|
||||
LEAN_RANDOM = "%RAND64%"
|
||||
#DATABASE_PORT = ""
|
||||
#DATABASE_HOST = ""
|
||||
#DATABASE_NAME = ""
|
||||
#DATABASE_USERNAME = ""
|
||||
#DATABASE_PASSWORD = ""
|
||||
#DATABASE_CHARSET = "utf8"
|
||||
#DATABASE_ADAPTER = "MariaDB"
|
||||
|
||||
LEAN_RANDOM = "%RAND64%"
|
||||
@@ -0,0 +1,8 @@
|
||||
.idea
|
||||
.env
|
||||
composer.lock
|
||||
/private/
|
||||
/public/static/
|
||||
/public/.htaccess
|
||||
/vendor/
|
||||
/var/
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/php
|
||||
<?php
|
||||
|
||||
try {
|
||||
require_once(__DIR__."/src/Kernel.php");
|
||||
}
|
||||
catch(\Throwable $t) {
|
||||
echo sprintf("%s [ %s ]" , $t->getMessage(), var_export($t->getTrace(), true));
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
{
|
||||
"name": "my_namespace/my_project",
|
||||
"description": "",
|
||||
"type": "app",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"php": "^8.4",
|
||||
"ext-apcu": "*",
|
||||
"ext-sodium": "*",
|
||||
"ext-bcmath": "*",
|
||||
"ext-json": "*",
|
||||
"ext-readline": "*",
|
||||
"league/route": "^5.0.0-dev",
|
||||
"laminas/laminas-diactoros": "2.24.x-dev",
|
||||
"laminas/laminas-httphandlerrunner": "2.5.x-dev",
|
||||
"vlucas/phpdotenv": "^3.4@dev",
|
||||
"middlewares/whoops": "dev-master",
|
||||
"ralouphie/getallheaders": "3.x",
|
||||
"guzzlehttp/guzzle": "7.9.x-dev",
|
||||
"symfony/mailer": "6.3.x-dev",
|
||||
"league/oauth2-client": "dev-master",
|
||||
"php-di/php-di": "dev-master",
|
||||
"league/commonmark": "^2.7@dev",
|
||||
"assets-package/ace": "1.0.0",
|
||||
"mcnd/dump": "dev-master",
|
||||
"mcnd/storage": "dev-master",
|
||||
"mcnd/cli": "dev-master",
|
||||
"mcnd/event": "dev-master",
|
||||
"mcnd/ulmus": "^1.0 || ^2.0",
|
||||
"mcnd/ulmus-api": "^1.3.0",
|
||||
"mcnd/ulmus-user": "~1.1.2",
|
||||
"mcnd/picea": "dev-master",
|
||||
"mcnd/picea-ui": "dev-master",
|
||||
"mcnd/picea-asset": "~1.0.2",
|
||||
"mcnd/cronard": "dev-master",
|
||||
"mcnd/lean-console": "~1.3.2",
|
||||
"mcnd/kash": "dev-master",
|
||||
"mcnd/taxus": "dev-master",
|
||||
"mcnd/notes": "dev-master",
|
||||
"mcnd/thebugs": "dev-master",
|
||||
"mcnd/negundo-client": "dev-master"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://git.mcnd.ca/api/packages/mcndave/composer"
|
||||
},
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://git.mcnd.ca/api/packages/assets-package/composer"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MyProject\\Namespace": "src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"Lean\\Composer::postInstall"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Lean\\Composer::postUpdate"
|
||||
]
|
||||
},
|
||||
"extra" : {
|
||||
"lean" : {
|
||||
"autoload": {
|
||||
"order": 1000,
|
||||
"definitions" : [
|
||||
"meta/definitions/definitions.php",
|
||||
"meta/definitions/storage.php",
|
||||
"meta/definitions/security.php",
|
||||
"meta/definitions/auth.php",
|
||||
"meta/definitions/env.php"
|
||||
],
|
||||
"config" : [
|
||||
"meta/config.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,7 @@ return [
|
||||
],
|
||||
|
||||
'keys' => (function() {
|
||||
foreach(explode(',', getenv('KEYS') ?? "") as $item) {
|
||||
list($var, $value) = explode(":", trim($item));
|
||||
$list[$var] = $value;
|
||||
}
|
||||
|
||||
return $list;
|
||||
return explode(',', getenvonce('KEYS') ?? "") ;
|
||||
})(),
|
||||
|
||||
'meta' => [
|
||||
@@ -24,7 +19,7 @@ return [
|
||||
|
||||
'ulmus' => [
|
||||
'connections' => [
|
||||
'default' => [
|
||||
/*'default' => [
|
||||
'adapter' => getenv("DATABASE_ADAPTER"),
|
||||
'host' => getenv("DATABASE_HOST"),
|
||||
'port' => getenv("DATABASE_PORT"),
|
||||
@@ -34,6 +29,18 @@ return [
|
||||
'settings' => [
|
||||
'charset' => getenv("DATABASE_CHARSET"),
|
||||
],
|
||||
],*/
|
||||
|
||||
'default' => [
|
||||
'adapter' => getenv("SQLITE_ADAPTER"),
|
||||
'path' => getenv('PROJECT_PATH') . DIRECTORY_SEPARATOR . getenv("SQLITE_PATH"),
|
||||
'pragma' => explode(',', getenv("SQLITE_PRAGMA")),
|
||||
'pragma_begin' => array_merge(
|
||||
explode(',', getenv("SQLITE_PRAGMA_BEGIN")), explode(',', getenv('DEBUG') ? getenv("SQLITE_PRAGMA_DEBUG_BEGIN") : "")
|
||||
),
|
||||
'pragma_close' => array_merge(
|
||||
explode(',', getenv("SQLITE_PRAGMA_CLOSE")), explode(',', getenv('DEBUG') ? getenv("SQLITE_PRAGMA_DEBUG_CLOSE") : "")
|
||||
),
|
||||
],
|
||||
]
|
||||
]
|
||||
|
||||
@@ -4,6 +4,9 @@ use function DI\autowire, DI\create, DI\get;
|
||||
|
||||
use %NAMESPACE%\Entity;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
use Ulmus\Entity\Field\Datetime,
|
||||
Ulmus\User\Lib\Authenticate;
|
||||
|
||||
@@ -17,30 +20,20 @@ use Picea\Picea;
|
||||
|
||||
use TheBugs\Email\{ EmailConfiguration, MailerInterface, SwiftMailer };
|
||||
|
||||
use Taxus\Taxus;
|
||||
use Taxus\{ Taxus, PermissionGrantInterface };
|
||||
|
||||
use Lean\Factory\HttpFactoryInterface;
|
||||
|
||||
return [
|
||||
Entity\User::class => autowire(Entity\User::class),
|
||||
\Ulmus\User\Entity\UserInterface::class => autowire(Entity\User::class),
|
||||
|
||||
Authenticate::class => create(Authenticate::class)->constructor(get(Session::class), get(Cookie::class), get('authentication.method')),
|
||||
|
||||
SecurityHandler::class => create(SecurityHandler::class)->constructor(function() {
|
||||
return new RedirectResponse(getenv("URL_BASE")."/connexion");
|
||||
SecurityHandler::class => create(SecurityHandler::class)->constructor(function(ContainerInterface $c) {
|
||||
return $c->get(HttpFactoryInterface::class)::createRedirectResponse(getenv("URL_BASE")."/login");
|
||||
}, get('authentication.unauthorize'), get(Taxus::class)),
|
||||
|
||||
'authentication.error' => function($c, Picea $picea) {
|
||||
return function($message) use ($picea) {
|
||||
return new HtmlResponse($picea->renderHtml('lean/error/500', [
|
||||
'title' => "",
|
||||
'subtitle' => "",
|
||||
'message' => $message,
|
||||
]));
|
||||
};
|
||||
},
|
||||
|
||||
'authentication.unauthorize' => function($c, Picea $picea) {
|
||||
return function($message) use ($picea) {
|
||||
return new HtmlResponse($picea->renderHtml('lean/error/401', [
|
||||
'authentication.unauthorize' => function(ContainerInterface $c, Picea $picea) {
|
||||
return function($message) use ($c, $picea) {
|
||||
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/401', [
|
||||
'title' => "",
|
||||
'subtitle' => "",
|
||||
'message' => $message,
|
||||
@@ -61,6 +54,4 @@ return [
|
||||
|
||||
return $email;
|
||||
},
|
||||
|
||||
PermissionGrantInterface::class => create(%NAMESPACE%\PrivilegeGrantAccess::class)->constructor(get(ServerRequestInterface::class), get(Session::class)),
|
||||
];
|
||||
@@ -1,36 +1,53 @@
|
||||
<?php
|
||||
|
||||
use function DI\autowire, DI\create, DI\get;
|
||||
use Taxus\Privilege;
|
||||
|
||||
$dir = getenv("META_PATH") . "/definitions";
|
||||
use function DI\{ autowire, add, create, get };
|
||||
|
||||
return array_merge(
|
||||
Lean\Lean::definitions(),
|
||||
$dir = dirname(__DIR__, 2);
|
||||
|
||||
Lean\Console\Lean::definitions(),
|
||||
return [
|
||||
\%NAMESPACE%\Factory\FormFactoryInterface::class => autowire(\%NAMESPACE%\Factory\FormFactory::class),
|
||||
\Lean\Api\Factory\MessageFactoryInterface::class => autowire(\%NAMESPACE%\Lib\Message::class),
|
||||
|
||||
[
|
||||
'%APPKEY%' => [
|
||||
'picea' => [
|
||||
'context' => "%ESCAPED_NAMESPACE%\\View",
|
||||
|
||||
'extensions' => [
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
'ulmus' => [
|
||||
'entities' => [ '%ESCAPED_NAMESPACE%\\Entity' => getenv("PROJECT_PATH") . '/src/Entity/' ],
|
||||
],
|
||||
|
||||
'routes' => [
|
||||
'%ESCAPED_NAMESPACE%\\Controller' => getenv("PROJECT_PATH") . '/src/Controller/',
|
||||
],
|
||||
'%APPKEY%' => [
|
||||
'picea' => [
|
||||
'context' => "%ESCAPED_NAMESPACE%\\View",
|
||||
'asset' => [],
|
||||
'extensions' => [],
|
||||
],
|
||||
|
||||
'ulmus' => [
|
||||
'entities' => [ '%ESCAPED_NAMESPACE%\\Entity' => implode(DIRECTORY_SEPARATOR, [ $dir, 'src', 'Entity', '' ]) ],
|
||||
],
|
||||
|
||||
'routes' => [
|
||||
'%ESCAPED_NAMESPACE%\\Controller' => implode(DIRECTORY_SEPARATOR, [ $dir, 'src', 'Controller', '' ]),
|
||||
],
|
||||
|
||||
'events' => [
|
||||
'%ESCAPED_NAMESPACE%\\Controller' => implode(DIRECTORY_SEPARATOR, [ $dir, 'src', 'Controller', '' ]),
|
||||
],
|
||||
|
||||
'cronard' => [
|
||||
'%ESCAPED_NAMESPACE%\\Controller' => implode(DIRECTORY_SEPARATOR, [ $dir, 'src', 'Controller', '' ]),
|
||||
],
|
||||
|
||||
'cli' => [
|
||||
'%ESCAPED_NAMESPACE%\\Controller' => implode(DIRECTORY_SEPARATOR, [ $dir, 'src', 'Controller', '' ]),
|
||||
],
|
||||
|
||||
'taxus' => [
|
||||
'is_dev' => [ 'dev' => "Can manage almost everything in this application."],
|
||||
'is_admin' => [ 'admin' => "Can manage almost everything in this application."],
|
||||
'is_moderator' => [ 'moderator' => "Can moderate this application."],
|
||||
'is_user' => [ 'user' => "Is an authenticated user."],
|
||||
'is_anonymous' => [ 'anonymous' => "Is an anonymous (unauthenticated) user."],
|
||||
]
|
||||
],
|
||||
|
||||
require("$dir/auth.php"),
|
||||
require("$dir/storage.php"),
|
||||
require("$dir/env/" . getenv('APP_ENV') . ".php"),
|
||||
[ 'config' => function () { return require(getenv("META_PATH")."/config.php"); } ]
|
||||
);
|
||||
'app.middlewares' => \DI\add([
|
||||
Ulmus\User\Middleware\RememberMeMiddleware::class,
|
||||
]),
|
||||
];
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
if (! function_exists('getenvonce')) {
|
||||
function getenvonce(string $name) : array|string|false
|
||||
{
|
||||
$value = getenv($name);
|
||||
unset($_ENV[$name], $_SERVER[$name]);
|
||||
putenv($name . "=");
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
|
||||
return require("env/" . ( getenv('APP_ENV') ?? 'prod' ) . ".php");
|
||||
+6
@@ -11,4 +11,10 @@ if ($_GET['sql'] ?? false) {
|
||||
return [
|
||||
"dump" => create(DumpMiddleware::class),
|
||||
"errorHandler" => create(Middlewares\Whoops::class),
|
||||
'routes.caching' => create(Kash\ArrayCache::class)->constructor(get(Kash\CacheInvalidator::class), "lean.routes", 30),
|
||||
'breadcrumbs.caching' => create(Kash\ArrayCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.breadcrumbs", 30),
|
||||
'ulmus.caching' => create(Kash\ArrayCache::class)->constructor( get(Kash\CacheInvalidator::class), "ulmus.entities", 30),
|
||||
'cronard.caching' => create(Kash\ArrayCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.cronards", 30),
|
||||
'events.caching' => create(Kash\ArrayCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.events", 30),
|
||||
'cli.caching' => create(Kash\ArrayCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.cli", 30),
|
||||
];
|
||||
+20
-9
@@ -2,9 +2,9 @@
|
||||
|
||||
use Picea\Picea;
|
||||
|
||||
use CSLSJ\Debogueur\DebogueurMiddleware;
|
||||
use Negundo\Client\{ NegundoMiddleware, SoftwareConfig };
|
||||
|
||||
use Laminas\Diactoros\Response\HtmlResponse;
|
||||
use Lean\Factory\HttpFactoryInterface;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface,
|
||||
Psr\Http\Message\ServerRequestInterface,
|
||||
@@ -13,7 +13,7 @@ use Psr\Http\Server\MiddlewareInterface,
|
||||
|
||||
use function DI\{ create, get, autowire };
|
||||
|
||||
error_reporting(getenv('ERROR_REPORTING') ?: ( E_ALL & ~E_USER_DEPRECATED & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE ));
|
||||
error_reporting(getenv('ERROR_REPORTING') ?: ( E_ALL & ~E_USER_DEPRECATED & ~E_DEPRECATED & ~E_NOTICE ));
|
||||
|
||||
return [
|
||||
/* do nothing in production !*/
|
||||
@@ -27,16 +27,27 @@ return [
|
||||
};
|
||||
},
|
||||
|
||||
"errorHandler" => create(DebogueurMiddleware::class)->constructor(getenv('DEBOGUEUR_HASH') ?: "", null, get('app.errorhandler.html')),
|
||||
SoftwareConfig::class => create(SoftwareConfig::class)->constructor(getenv('NEGUNDO_HASH'), getenv('NEGUNDO_SERVER')),
|
||||
|
||||
'app.errorhandler.html' => function($c, Picea $picea) {
|
||||
return function(\Throwable $exception) use ($picea) {
|
||||
return new HtmlResponse($picea->renderHtml('lean/error/500', [
|
||||
"errorHandler" => create(NegundoMiddleware::class)->constructor(get(SoftwareConfig::class), null, get('app.errorhandler.html')),
|
||||
|
||||
'app.errorhandler.html' => function(\Psr\Container\ContainerInterface $c, Picea $picea) {
|
||||
return function(\Throwable $exception) use ($c, $picea) {
|
||||
error_log($exception->getMessage());
|
||||
|
||||
return $c->get(HttpFactoryInterface::class)::createHtmlResponse($picea->renderHtml('lean/error/500', [
|
||||
'title' => "Une erreur s'est produite lors de l'exécution du script.",
|
||||
'subtitle' => "Êtes-vous connecté avec le bon compte ?",
|
||||
'message' => $exception->getMessage(),
|
||||
'exception' => $exception,
|
||||
]));
|
||||
]), 500);
|
||||
};
|
||||
},
|
||||
];
|
||||
|
||||
'routes.caching' => create(Kash\ApcuCache::class)->constructor(get(Kash\CacheInvalidator::class), "lean.routes", random_int(3600, 7200)),
|
||||
'breadcrumbs.caching' => create(Kash\ApcuCache::class)->constructor(get(Kash\CacheInvalidator::class), "lean.breadcrumbs", random_int(3600, 7200)),
|
||||
'ulmus.caching' => create(Kash\ApcuCache::class)->constructor( get(Kash\CacheInvalidator::class), "ulmus.entities", random_int(3600, 7200)),
|
||||
'cronard.caching' => create(Kash\ApcuCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.cronards", random_int(3600, 7200)),
|
||||
'events.caching' => create(Kash\ApcuCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.events", random_int(3600, 7200)),
|
||||
'cli.caching' => create(Kash\ApcuCache::class)->constructor( get(Kash\CacheInvalidator::class), "lean.cli", random_int(3600, 7200)),
|
||||
];
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Storage\Session;
|
||||
|
||||
use Taxus\{ Privilege, Taxus, PermissionGrantInterface, DefaultPermissionGrant };
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
use function DI\{ create, get, add };
|
||||
|
||||
return [
|
||||
Taxus::class => function ($c) {
|
||||
$taxus = new Taxus( ... $c->get('taxus.gates') );
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach($c->get(Lean\Lean::class)->getTaxusPrivileges() as $key => $privilege) {
|
||||
foreach($privilege as $name => $description) {
|
||||
$list[] = [ new Privilege($name, $description), $key ];
|
||||
}
|
||||
}
|
||||
|
||||
$taxus->add(... $list);
|
||||
|
||||
return $taxus;
|
||||
},
|
||||
|
||||
'taxus.gates' => add([
|
||||
get(PermissionGrantInterface::class),
|
||||
]),
|
||||
|
||||
PermissionGrantInterface::class => create(%NAMESPACE%\PrivilegeGrantAccess::class)->constructor(get(ServerRequestInterface::class), get(Session::class)),
|
||||
];
|
||||
@@ -14,14 +14,6 @@ return [
|
||||
$adapter = new ConnectionAdapter('default', $c->get('config')['ulmus'], true);
|
||||
$adapter->resolveConfiguration();
|
||||
|
||||
$adapter->connect();
|
||||
|
||||
return $adapter;
|
||||
},
|
||||
|
||||
AdapterProxy::class => function (ContainerInterface $c) {
|
||||
return new AdapterProxy(
|
||||
$c->get(ConnectionAdapter::class)
|
||||
);
|
||||
},
|
||||
];
|
||||
@@ -1,8 +1,22 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
# RewriteBase /
|
||||
|
||||
# Force HTTPS (disabled by default)
|
||||
# RewriteCond %{HTTPS} off
|
||||
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
|
||||
# Reverse Proxy ; Force HTTPS (disabled by default)
|
||||
# RewriteCond %{HTTP:X-Forwarded-SSL} off
|
||||
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
|
||||
|
||||
# Remove trailing slashes from request URL
|
||||
#RewriteCond %{REQUEST_FILENAME} !-d
|
||||
#RewriteCond %{REQUEST_URI} (.+)/$
|
||||
#RewriteRule ^ %1 [R=301,L]
|
||||
|
||||
# Redirect everything into app
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [R=301,L]
|
||||
RewriteRule ^([^?]*)$ %1 [NC,L,QSA]
|
||||
</IfModule>
|
||||
@@ -1,3 +1,14 @@
|
||||
<?php
|
||||
|
||||
require_once("../src/Kernel.php");
|
||||
try {
|
||||
$projectPath = dirname(__DIR__);
|
||||
|
||||
require_once $projectPath . '/vendor/autoload.php';
|
||||
|
||||
new class($projectPath) extends %NAMESPACE%\Kernel {};
|
||||
}
|
||||
catch(\Throwable $t) {
|
||||
echo sprintf("%s<hr><small>%s:%d</small><br><pre>%s</pre>", $t->getMessage(), $t->getFile(), $t->getLine(), getenv('DEBUG') ? var_export($t->getTrace(), true) : "");
|
||||
error_log($t->getMessage());
|
||||
header("HTTP/1.1 500 Internal Server Error");
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="Remove trailing slash" stopProcessing="true">
|
||||
<match url="(.*)/$" />
|
||||
<conditions>
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
|
||||
</conditions>
|
||||
<action type="Redirect" redirectType="Permanent" url="{R:1}" />
|
||||
</rule>
|
||||
<rule name="Framework Routing" stopProcessing="true">
|
||||
<match url="." ignoreCase="false" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="index.php" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
<staticContent>
|
||||
<remove fileExtension=".js" />
|
||||
<mimeMap fileExtension=".js" mimeType="application/x-javascript; charset=UTF-8" />
|
||||
<remove fileExtension=".css" />
|
||||
<mimeMap fileExtension=".css" mimeType="text/css; charset=UTF-8" />
|
||||
<remove fileExtension=".woff" />
|
||||
<remove fileExtension=".eot" />
|
||||
<remove fileExtension=".ttf" />
|
||||
<remove fileExtension=".svg" />
|
||||
|
||||
<mimeMap fileExtension=".eot" mimeType="application/vnd.ms-fontobject" />
|
||||
<mimeMap fileExtension=".ttf" mimeType="application/font-sfnt" />
|
||||
<mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
|
||||
<mimeMap fileExtension=".woff" mimeType="application/font-woff" />
|
||||
</staticContent>
|
||||
<handlers>
|
||||
<remove name="PHP7.3" />
|
||||
<add name="PHP7.3" path="*.php" verb="*" modules="FastCgiModule" scriptProcessor="C:\php\7.4.0\php-cgi.exe" resourceType="File" requireAccess="Script" />
|
||||
</handlers>
|
||||
<httpErrors errorMode="Detailed" />
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
@@ -4,19 +4,16 @@ namespace %NAMESPACE%\Controller;
|
||||
|
||||
use Psr\Http\Message\{ServerRequestInterface, ResponseInterface };
|
||||
|
||||
use Notes\Route\Attribute\Method\Route;
|
||||
|
||||
use %NAMESPACE%\{ Lib, Entity, Form, };
|
||||
|
||||
use function %NAMESPACE%\View\{ _, lang, url, route, form };
|
||||
|
||||
/**
|
||||
* @Language("%APPKEY%.home")
|
||||
*/
|
||||
class Home {
|
||||
use Lib\ControllerTrait;
|
||||
|
||||
/**
|
||||
* @Route("/", "name" => "home")
|
||||
*/
|
||||
#[Route("/", name: "home")]
|
||||
public function index(ServerRequestInterface $request, array $arguments) : ResponseInterface
|
||||
{
|
||||
form(new Form\Form(), $this->pushContext(new Lib\FormContext($request, "form.name")));
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
namespace %NAMESPACE%\Entity;
|
||||
|
||||
use Ulmus\Entity\Field\Datetime;
|
||||
use Ulmus\{Attribute\Obj\Table};
|
||||
|
||||
use %NAMESPACE%\Lib;
|
||||
|
||||
/**
|
||||
* @Table('name' => "user")
|
||||
*/
|
||||
#[Table(name: "user")]
|
||||
class User extends \Ulmus\User\Entity\User implements \JsonSerializable
|
||||
{
|
||||
use Lib\EntityTrait;
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace %NAMESPACE%\Factory;
|
||||
|
||||
use Lean\Api\Factory\MessageFactoryInterface;
|
||||
use Lean\LanguageHandler;
|
||||
use Picea\Ui\Method\FormContextInterface;
|
||||
use Picea\Ui\Method\FormInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Storage\Session;
|
||||
use Ulmus\Entity\EntityInterface;
|
||||
use Ulmus\User\Entity\UserInterface;
|
||||
use Ulmus\User\Lib\Authenticate;
|
||||
|
||||
class FormFactory implements FormFactoryInterface
|
||||
{
|
||||
/*
|
||||
public function __construct(
|
||||
protected ServerRequestInterface $request,
|
||||
protected Authenticate $authenticate,
|
||||
protected Session $session,
|
||||
protected LanguageHandler $languageHandler,
|
||||
protected MessageFactoryInterface $messageFactory,
|
||||
) {}
|
||||
|
||||
public function save(EntityInterface $entity): FormInterface
|
||||
{
|
||||
return new Form\Save($this->languageHandler, $this->messageFactory, $entity);
|
||||
}
|
||||
|
||||
public function saveContext(? ServerRequestInterface $request = null, ? string $formName = null): FormContextInterface
|
||||
{
|
||||
return new Form\SaveContext($request ?: $this->request, $formName);
|
||||
}
|
||||
|
||||
public function delete(EntityInterface $entity): FormInterface
|
||||
{
|
||||
return new Form\Delete($this->languageHandler, $this->messageFactory, $entity);
|
||||
}
|
||||
|
||||
public function deleteContext(?ServerRequestInterface $request = null, ?string $formName = null): FormContextInterface
|
||||
{
|
||||
return new Lib\FormContext($request, $formName);
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace %NAMESPACE%\Factory;
|
||||
|
||||
use Lean\Api\Factory\MessageFactoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Picea\Ui\Method\{ FormInterface, FormContextInterface };
|
||||
use Storage\Session;
|
||||
use Lean\LanguageHandler;
|
||||
use Ulmus\Entity\EntityInterface;
|
||||
use Ulmus\User\Entity\UserInterface;
|
||||
use Ulmus\User\Lib\Authenticate;
|
||||
|
||||
interface FormFactoryInterface
|
||||
{
|
||||
/* public function __construct(ServerRequestInterface $request, Authenticate $authenticate, Session $session, LanguageHandler $languageHandler, MessageFactoryInterface $messageFactory,);
|
||||
|
||||
public function save(EntityInterface $entity): FormInterface;
|
||||
|
||||
public function saveContext(?ServerRequestInterface $request = null, ?string $formName = null): FormContextInterface;
|
||||
|
||||
public function delete(EntityInterface $entity): FormInterface;
|
||||
|
||||
public function deleteContext(?ServerRequestInterface $request = null, ?string $formName = null): FormContextInterface;*/
|
||||
}
|
||||
@@ -23,7 +23,7 @@ class Form implements \Picea\Ui\Method\FormInterface {
|
||||
|
||||
public function execute(FormContextInterface $context) : void
|
||||
{
|
||||
$context->pushMessage(Message::generateSuccess(
|
||||
$context->pushMessage(Lib\Message::generateSuccess(
|
||||
lang("form.success")
|
||||
));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace %NAMESPACE%;
|
||||
|
||||
use Lean\Lean;
|
||||
use Ulmus\ConnectionAdapter;
|
||||
|
||||
require_once dirname(__DIR__) . '/vendor/autoload.php';
|
||||
@@ -14,6 +15,7 @@ new class(dirname(__DIR__)) extends \Lean\Kernel {
|
||||
'META_PATH' => "META_DIR",
|
||||
'PUBLIC_PATH' => "PUBLIC_DIR",
|
||||
'PRIVATE_PATH' => "PRIVATE_DIR",
|
||||
'TMP_PATH' => "TMP_DIR",
|
||||
'VIEW_PATH' => "VIEW_DIR",
|
||||
];
|
||||
|
||||
@@ -21,15 +23,11 @@ new class(dirname(__DIR__)) extends \Lean\Kernel {
|
||||
{
|
||||
$this->errorLogPath = getenv("LOGS_PATH") . DIRECTORY_SEPARATOR. date("Y-m").".log";
|
||||
|
||||
$this->definitionFilePath = implode(DIRECTORY_SEPARATOR, [ getenv('META_PATH'), 'definitions', 'definitions.php' ]);
|
||||
|
||||
return parent::initializeEngine();
|
||||
}
|
||||
|
||||
protected function serviceContainer() : self
|
||||
{
|
||||
# $this->container->get(ConnectionAdapter::class);
|
||||
|
||||
return parent::serviceContainer();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace %NAMESPACE%\Lib;
|
||||
|
||||
use Picea\Picea;
|
||||
use Storage\Session;
|
||||
use Mcnd\Event\EventManager;
|
||||
use Notes\{ Attribute\Ignore, Route\Attribute\Object\Route, Security\Attribute\Security };
|
||||
use Ulmus\User\Entity\{ User, UserInterface };
|
||||
|
||||
use %NAMESPACE%\{ Entity, Lib, Form, Factory };
|
||||
|
||||
#[Security(locked: false)]
|
||||
#[Route(base: "/api", method: [ "GET", "POST", "PUT", "PATCH", "DELETE", ])]
|
||||
trait ApiTrait {
|
||||
use \Lean\ControllerTrait, \Lean\Api\LeanApiTrait {
|
||||
\Lean\Api\LeanApiTrait::renderMarkdown insteadof \Lean\ControllerTrait;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public UserInterface $user;
|
||||
|
||||
#[Ignore]
|
||||
protected EventManager $eventManager;
|
||||
|
||||
#[Ignore]
|
||||
protected Factory\FormFactoryInterface $formFactory;
|
||||
}
|
||||
@@ -5,29 +5,23 @@ namespace %NAMESPACE%\Lib;
|
||||
use Picea\Picea;
|
||||
use Storage\Session;
|
||||
use Ulmus\User\Entity\User;
|
||||
use Ulmus\User\Lib\Authenticate;
|
||||
use %NAMESPACE%\Entity;
|
||||
use Notes\Route\Attribute\Object\Route;
|
||||
use Notes\Security\Attribute\Security;
|
||||
use DI\Attribute\Inject;
|
||||
|
||||
/**
|
||||
* @Security("locked" => false)
|
||||
* @RouteParam("methods" => [ "GET", "POST" ])
|
||||
*/
|
||||
use %NAMESPACE%\{ Entity, Lib, Form };
|
||||
|
||||
use Mcnd\Event\EventManager;
|
||||
use Notes\Attribute\Ignore;use Ulmus\User\Entity\UserInterface;
|
||||
|
||||
#[Security(locked: false)]
|
||||
#[Route(method: ['GET', 'POST', ])]
|
||||
trait ControllerTrait {
|
||||
use \Lean\ControllerTrait;
|
||||
|
||||
public ? \Ulmus\User\Entity\User $user;
|
||||
|
||||
protected Authenticate $authenticate;
|
||||
|
||||
public function __construct(Picea $picea, Session $session, Authenticate $authenticate) {
|
||||
$this->initializeController($picea, $session, $authenticate);
|
||||
}
|
||||
|
||||
public function initializeController(Picea $picea, Session $session, Authenticate $authenticate) {
|
||||
$this->picea = $picea;
|
||||
$this->authenticate = $authenticate;
|
||||
$this->session = $session;
|
||||
$this->user = $authenticate->rememberMe( Entity\User::repository() ) ?: new Entity\User();
|
||||
}
|
||||
#[Inject]
|
||||
public ? UserInterface $user;
|
||||
|
||||
#[Inject]
|
||||
protected EventManager $eventManager;
|
||||
}
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
namespace %NAMESPACE%\Lib;
|
||||
|
||||
use Lean\Api\Factory\MessageFactoryInterface;
|
||||
use Picea\Ui\Method\FormMessage;
|
||||
|
||||
class Message implements FormMessage {
|
||||
class Message implements FormMessage, MessageFactoryInterface {
|
||||
|
||||
const MESSAGE_TYPE = [
|
||||
'success' => [
|
||||
@@ -34,7 +35,7 @@ class Message implements FormMessage {
|
||||
|
||||
public ? string $header = null;
|
||||
|
||||
public function __construct(string $message, string $type = "error", ? string $header = null, ? string $class = null)
|
||||
public function __construct(string $message = "", string $type = "error", ? string $header = null, ? string $class = null)
|
||||
{
|
||||
$this->message = $message;
|
||||
|
||||
@@ -86,18 +87,33 @@ class Message implements FormMessage {
|
||||
return $this->type === "error";
|
||||
}
|
||||
|
||||
public static function generateSuccess(string $message, ? string $header = null, ? string $class = null) : self
|
||||
public static function generateSuccess(string $message, ? string $header = null, ? string $class = null) : MessageFactoryInterface
|
||||
{
|
||||
return new static($message, 'success', $header, $class);
|
||||
}
|
||||
|
||||
public static function generateError(string $message, ? string $header = null, ? string $class = null) : self
|
||||
public static function generateError(string $message, ? string $header = null, ? string $class = null) : MessageFactoryInterface
|
||||
{
|
||||
return new static($message, 'error', $header, $class);
|
||||
}
|
||||
|
||||
public static function generateWarning(string $message, ? string $header = null, ? string $class = null) : self
|
||||
public static function generateWarning(string $message, ? string $header = null, ? string $class = null) : MessageFactoryInterface
|
||||
{
|
||||
return new static($message, 'warning', $header, $class);
|
||||
}
|
||||
|
||||
public static function generateInformation(string $message, ?string $header = null, ?string $class = null): MessageFactoryInterface
|
||||
{
|
||||
return new static($message, 'information', $header, $class);
|
||||
}
|
||||
|
||||
public static function generateDebug(string $message, ?string $header = null, ?string $class = null): MessageFactoryInterface
|
||||
{
|
||||
return new static($message, 'debug', $header, $class);
|
||||
}
|
||||
|
||||
public static function generateTrace(string $message, ?string $header = null, ?string $class = null): MessageFactoryInterface
|
||||
{
|
||||
return new static($message, 'trace', $header, $class);
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
namespace %NAMESPACE%\Middleware;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
use Psr\Http\Server\{ RequestHandlerInterface, MiddlewareInterface };
|
||||
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
|
||||
use Storage\Session;
|
||||
|
||||
class Authentication implements MiddlewareInterface {
|
||||
@@ -23,13 +20,6 @@ class Authentication implements MiddlewareInterface {
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware request handling
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param RequestHandlerInterface $handler
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
|
||||
{
|
||||
$user = $this->session->get( $this->sessionUserVariable() );
|
||||
@@ -41,12 +31,6 @@ class Authentication implements MiddlewareInterface {
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter / setter of Session User Variable
|
||||
*
|
||||
* @param string|null sessionUserVariable
|
||||
* @return mixed
|
||||
*/
|
||||
public function sessionUserVariable(?string $set = null) : string
|
||||
{
|
||||
return $set !== null ? $this->sessionUserVariable = $set : $this->sessionUserVariable;
|
||||
|
||||
@@ -25,7 +25,7 @@ class PrivilegeGrantAccess implements PermissionGrantInterface {
|
||||
*/
|
||||
public function is_dev() : bool
|
||||
{
|
||||
return false;
|
||||
return getenv('DEBUG');
|
||||
}
|
||||
|
||||
public function is_admin(User $user) : bool
|
||||
|
||||
Whitespace-only changes.
@@ -5,28 +5,6 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>{% section "head.title" %}{{ title() }} — {{ lang('application_name') }}{% endsection %}</title>
|
||||
<link rel="stylesheet" href="{% asset 'static/css/main.e39066da.css' %}">
|
||||
<style>
|
||||
@font-face{
|
||||
font-family:FontAwesome;
|
||||
src:url({% asset 'static/fonts/fontawesome-webfont.674f50d2.eot' %});
|
||||
src:url({% asset 'static/fonts/fontawesome-webfont.674f50d2.eot' %}) format("embedded-opentype"),
|
||||
url({% asset 'static/fonts/fontawesome-webfont.af7ae505.woff2' %}) format("woff2"),
|
||||
url({% asset 'static/fonts/fontawesome-webfont.fee66e71.woff' %}) format("woff"),
|
||||
url({% asset 'static/fonts/fontawesome-webfont.b06871f2.ttf' %}) format("truetype"),
|
||||
url({% asset 'static/images/fontawesome-webfont.912ec66d.svg' %}) format("svg");
|
||||
font-weight:400;
|
||||
font-style:normal;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% asset 'static/favicon/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% asset 'static/favicon/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% asset 'static/favicon/favicon-16x16.png' %}">
|
||||
<link rel="manifest" href="{% asset 'static/favicon/site.webmanifest' %}">
|
||||
<link rel="mask-icon" href="{% asset 'static/favicon/safari-pinned-tab.svg' %}" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#2d89ef">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
</head>
|
||||
{% endsection %}
|
||||
|
||||
|
||||
@@ -6,4 +6,8 @@
|
||||
<div class="main">
|
||||
Hello World :) !
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<a href="{% route 'lean.console:home' %}">Accéder à la console LEAN</a>
|
||||
</div>
|
||||
{% endsection %}
|
||||
+52
-10
@@ -1,30 +1,55 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Lean;
|
||||
|
||||
|
||||
class Application
|
||||
{
|
||||
public string $piceaContext;
|
||||
public string $name;
|
||||
|
||||
public /*string|array*/ $piceaContext;
|
||||
|
||||
public array $piceaExtensions;
|
||||
|
||||
public array $piceaAssets;
|
||||
|
||||
public array $views;
|
||||
|
||||
public array $routes;
|
||||
|
||||
public array $cli;
|
||||
|
||||
public array $cronard;
|
||||
|
||||
public array $entities;
|
||||
|
||||
public array $connectionAdapters;
|
||||
|
||||
public array $events;
|
||||
|
||||
public array $tellJson;
|
||||
|
||||
public array $tellPhp;
|
||||
|
||||
public array $taxus;
|
||||
|
||||
public array $data = [];
|
||||
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function fromArray(array $data) : self
|
||||
{
|
||||
if (is_array($picea = $data['picea'] ?? false)) {
|
||||
$this->data = array_replace($this->data, $data);
|
||||
|
||||
$this->loadData();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function loadData() : static
|
||||
{
|
||||
if (is_array($picea = $this->data['picea'] ?? false)) {
|
||||
if ($picea['context'] ?? false ) {
|
||||
$this->piceaContext = $picea['context'];
|
||||
}
|
||||
@@ -33,18 +58,23 @@ class Application
|
||||
$this->piceaExtensions = $picea['extensions'];
|
||||
}
|
||||
|
||||
if ($picea['asset'] ?? false ) {
|
||||
$this->piceaAssets = $picea['asset'];
|
||||
}
|
||||
|
||||
if ($picea['view'] ?? false) {
|
||||
$this->views = $picea['view'];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($ulmus = $data['ulmus'] ?? false)) {
|
||||
if (is_array($ulmus = $this->data['ulmus'] ?? false)) {
|
||||
if ($ulmus['entities'] ?? false) {
|
||||
$this->entities = $ulmus['entities'];
|
||||
$this->connectionAdapters = $ulmus['adapters'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($tell = $data['tell'] ?? false)) {
|
||||
if (is_array($tell = $this->data['tell'] ?? false)) {
|
||||
if ($tell['json'] ?? false) {
|
||||
$this->tellJson = $tell['json'];
|
||||
}
|
||||
@@ -54,12 +84,24 @@ class Application
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($data['routes'] ?? false)) {
|
||||
$this->routes = $data['routes'];
|
||||
if (is_array($this->data['routes'] ?? false)) {
|
||||
$this->routes = $this->data['routes'];
|
||||
}
|
||||
|
||||
if (is_array($data['cronard'] ?? false)) {
|
||||
$this->cronard = $data['cronard'];
|
||||
if (is_array($this->data['cronard'] ?? false)) {
|
||||
$this->cronard = $this->data['cronard'];
|
||||
}
|
||||
|
||||
if (is_array($this->data['events'] ?? false)) {
|
||||
$this->events = $this->data['events'];
|
||||
}
|
||||
|
||||
if (is_array($this->data['cli'] ?? false)) {
|
||||
$this->cli = $this->data['cli'];
|
||||
}
|
||||
|
||||
if (is_array($this->data['taxus'] ?? false)) {
|
||||
$this->taxus = $this->data['taxus'];
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Lean;
|
||||
|
||||
use League\Route\Http\Exception\MethodNotAllowedException;
|
||||
use League\Route\Strategy;
|
||||
use League\Route\Http\Exception\NotFoundException;
|
||||
use Lean\ApplicationStrategy\MethodNotAllowedInterface;
|
||||
use Lean\ApplicationStrategy\NotFoundDecoratorInterface;
|
||||
use Lean\ApplicationStrategy\ThrowableHandlerInterface;
|
||||
use Lean\Factory\HttpFactoryInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use function DI\get;
|
||||
|
||||
class ApplicationStrategy extends Strategy\ApplicationStrategy {
|
||||
|
||||
public function __construct(
|
||||
ContainerInterface $di,
|
||||
) {
|
||||
$this->setContainer($di);
|
||||
}
|
||||
|
||||
public function getNotFoundDecorator(NotFoundException $exception): MiddlewareInterface
|
||||
{
|
||||
return $this->getContainer()->get(NotFoundDecoratorInterface::class);
|
||||
}
|
||||
|
||||
public function getThrowableHandler(): MiddlewareInterface
|
||||
{
|
||||
return $this->getContainer()->get(ThrowableHandlerInterface::class);
|
||||
}
|
||||
|
||||
public function getMethodNotAllowedDecorator(MethodNotAllowedException $exception): MiddlewareInterface
|
||||
{
|
||||
return $this->getContainer()->has(MethodNotAllowedInterface::class) ? $this->getContainer()->get(MethodNotAllowedInterface::class) : parent::getMethodNotAllowedDecorator($exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\ApplicationStrategy;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
|
||||
interface MethodNotAllowedInterface extends MiddlewareInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\ApplicationStrategy;
|
||||
|
||||
use DI\Attribute\Inject;
|
||||
use Lean\ApplicationStrategy;
|
||||
use Lean\Factory\HttpFactoryInterface;
|
||||
use Picea\Picea;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class NotFoundDecorator implements NotFoundDecoratorInterface
|
||||
{
|
||||
#[Inject]
|
||||
protected Picea $picea;
|
||||
|
||||
#[Inject]
|
||||
protected ContainerInterface $container;
|
||||
|
||||
#[Inject]
|
||||
protected HttpFactoryInterface $httpFactory;
|
||||
|
||||
public const ASSET_TRIGGER_UPDATE = [
|
||||
"js", "mjs", "manifest", "webmanifest", "css", "png", "ico",
|
||||
"jpg", "jpeg", "gif", "webp", "woff", "woff2", "eot", "svg",
|
||||
"ttf"
|
||||
];
|
||||
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
if (php_sapi_name() !== 'cli' and ! defined('STDIN')) {
|
||||
return $this->throw404($request);
|
||||
}
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
||||
public function throw404(ServerRequestInterface $request) : ResponseInterface
|
||||
{
|
||||
return $this->checkAssetTrigger($request) ?: $this->container->get('error.404');
|
||||
}
|
||||
|
||||
protected function checkAssetTrigger(ServerRequestInterface $request) : false|ResponseInterface
|
||||
{
|
||||
if (getenv('DEBUG') && $this->container->has(\Picea\Asset\Asset::class)) {
|
||||
$params = $request->getServerParams();
|
||||
|
||||
$scpName = basename(explode('?', $params['REQUEST_URI'] ?? "", 2)[0]);
|
||||
list(, $ext) = array_pad(explode('.', $scpName), 2, null);
|
||||
|
||||
if ($ext && in_array($ext, static::ASSET_TRIGGER_UPDATE)) {
|
||||
$this->container->get(\Picea\Asset\Asset::class)->launchInstall();
|
||||
|
||||
return $this->httpFactory->createTextResponse("Asset updated");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\ApplicationStrategy;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
|
||||
interface NotFoundDecoratorInterface extends MiddlewareInterface
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\ApplicationStrategy;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
class ThrowableHandler implements ThrowableHandlerInterface
|
||||
{
|
||||
public function process(
|
||||
ServerRequestInterface $request,
|
||||
RequestHandlerInterface $handler
|
||||
): ResponseInterface {
|
||||
try {
|
||||
return $handler->handle($request);
|
||||
} catch (Throwable $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\ApplicationStrategy;
|
||||
|
||||
use Psr\Http\Server\MiddlewareInterface;
|
||||
|
||||
interface ThrowableHandlerInterface extends MiddlewareInterface
|
||||
{
|
||||
|
||||
}
|
||||
+50
-6
@@ -3,6 +3,7 @@
|
||||
namespace Lean;
|
||||
|
||||
use Composer\EventDispatcher\Event;
|
||||
use Kash\CacheInvalidator;
|
||||
|
||||
class Composer
|
||||
{
|
||||
@@ -26,7 +27,8 @@ class Composer
|
||||
$source = static::createPath('vendor', 'mcnd', 'lean', 'skeleton');
|
||||
$dest = static::createPath('skeleton');
|
||||
|
||||
`cp -a $source $dest`;
|
||||
shell_exec("cp -a $source $dest");
|
||||
#`cp -a $source $dest`;
|
||||
|
||||
$iterator = new \RecursiveDirectoryIterator($dest);
|
||||
$iterator->setFlags(\RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
@@ -48,6 +50,8 @@ class Composer
|
||||
rmdir($dest);
|
||||
|
||||
$event->getIO()->write("Installation completed.");
|
||||
|
||||
static::postUpdate($event);
|
||||
}
|
||||
else {
|
||||
$event->getIO()->writeError(sprintf("The user you are running this script with doesn't seem to have a writable permission on directory %s", static::createPath()));
|
||||
@@ -55,16 +59,56 @@ class Composer
|
||||
}
|
||||
}
|
||||
|
||||
protected static function readComposerJson(Event $event) : ? array
|
||||
public static function postUpdate(Event $event) : void
|
||||
{
|
||||
$event->getIO()->write("post-update script executing ...");
|
||||
|
||||
$path = static::createPath('var/cache/version.cache');
|
||||
|
||||
if ( is_writeable(dirname($path)) ) {
|
||||
$event->getIO()->write("regenerating cache file version to invalidate cache for path '$path'");
|
||||
|
||||
new CacheInvalidator($path, true);
|
||||
}
|
||||
|
||||
if ( file_exists($container = static::createPath('var/cache/di/CompiledContainer.php')) ) {
|
||||
unlink($container);
|
||||
}
|
||||
|
||||
if (function_exists("apcu_clear_cache")) {
|
||||
apcu_clear_cache();
|
||||
}
|
||||
}
|
||||
|
||||
public static function readComposerLock() : false|array
|
||||
{
|
||||
static $content = null;
|
||||
|
||||
$path = static::createPath('composer.lock');
|
||||
|
||||
if (! file_exists($path) ) {
|
||||
throw new \UnexpectedValueException("Composer file 'composer.lock' could not be found within your project's root folder.");
|
||||
}
|
||||
|
||||
return $content ??= file_exists($path) ? json_decode(file_get_contents($path), true) : false;
|
||||
}
|
||||
|
||||
public static function readComposerJson() : false|array
|
||||
{
|
||||
static $content = null;
|
||||
|
||||
$path = static::createPath('composer.json');
|
||||
|
||||
return file_exists($path) ? json_decode(file_get_contents($path), true) : null;
|
||||
if (! file_exists($path) ) {
|
||||
throw new \UnexpectedValueException("Composer file 'composer.json' could not be found within your project's root folder.");
|
||||
}
|
||||
|
||||
return $content ??= file_exists($path) ? json_decode(file_get_contents($path), true, 512, \JSON_THROW_ON_ERROR) : false;
|
||||
}
|
||||
|
||||
protected static function getNamespaceFromAutoload(Event $event) : ? string
|
||||
{
|
||||
if ( null !== $composerJson = static::readComposerJson($event) ) {
|
||||
if ( false !== $composerJson = static::readComposerJson() ) {
|
||||
if ( $psr4 = $composerJson['autoload']['psr-4'] ?? false ) {
|
||||
foreach($psr4 as $ns => $directory) {
|
||||
if ($directory === 'src/') {
|
||||
@@ -82,11 +126,11 @@ class Composer
|
||||
|
||||
$event->getIO()->writeError("Your composer file do not seem to contains a valid psr-4 project pointing to the src folder.");
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected static function createPath(... $path) : string
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, array_merge([ $_SERVER['PWD'] ], $path));
|
||||
return implode(DIRECTORY_SEPARATOR, array_merge([ $_SERVER['PWD'] ?? getenv('PROJECT_PATH') ], $path));
|
||||
}
|
||||
}
|
||||
+129
-42
@@ -2,43 +2,39 @@
|
||||
|
||||
namespace Lean;
|
||||
|
||||
use CSLSJ\Common\RequestResponse\{ PdfResponse, ImageResponse, DownloadResponse };
|
||||
|
||||
use Picea,
|
||||
Picea\Ui\Method\FormContext;
|
||||
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Storage\Session;
|
||||
|
||||
use Laminas\Diactoros\Response\{ HtmlResponse, TextResponse, RedirectResponse, JsonResponse, EmptyResponse };
|
||||
|
||||
use Ulmus\EntityCollection;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
use DI\Attribute\Inject;
|
||||
use Lean\Factory\HttpFactory;
|
||||
use Notes\Attribute\Ignore;
|
||||
use Notes\Route\Attribute\Object\Route;
|
||||
use Notes\Security\Attribute\Security;
|
||||
use Picea\Ui\Method\FormContextInterface;
|
||||
use TheBugs\Email\MailerInterface;
|
||||
use Storage\{ Cookie, Session };
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
|
||||
use Notes\Route\Annotation\Object\Route as RouteParam,
|
||||
Notes\Route\Annotation\Method\Route,
|
||||
Notes\Security\Annotation\Security,
|
||||
Notes\Security\Annotation\Taxus,
|
||||
Notes\Tell\Annotation\Language;
|
||||
use Psr\Http\Message\{ ServerRequestInterface, ResponseInterface };
|
||||
|
||||
use function file_get_contents;
|
||||
|
||||
/**
|
||||
* @Security("locked" => true)
|
||||
* @RouteParam("methods" => [ "GET", "POST", "DELETE" ])
|
||||
*/
|
||||
#[Security(locked: false, realm: "Protected Area")]
|
||||
#[Route(method: [ "GET", "POST" ])]
|
||||
trait ControllerTrait {
|
||||
public ? Session $session;
|
||||
|
||||
public ? Picea\Picea $picea;
|
||||
#[Inject]
|
||||
public Session $session;
|
||||
|
||||
public ? MailerInterface $mailer;
|
||||
#[Inject]
|
||||
public Cookie $cookie;
|
||||
|
||||
#[Inject]
|
||||
public Picea\Picea $picea;
|
||||
|
||||
#[Inject]
|
||||
public MailerInterface $mailer;
|
||||
|
||||
public array $contextList = [];
|
||||
|
||||
#[Ignore]
|
||||
public function exportJson(ServerRequestInterface $request, string $entityClass, bool $includeRelations = true, ? callable $callback = null) : ResponseInterface
|
||||
{
|
||||
foreach($entityClass::repository()->filterServerRequest( $entityClass::searchRequest()->fromRequest($request->withQueryParams($request->getQueryParams() + ['limit' => PHP_INT_MAX,])) )->loadAll() as $entity) {
|
||||
@@ -48,11 +44,17 @@ trait ControllerTrait {
|
||||
return $this->renderJson( array_filter($data ?? [], function($row) { return $row !== null; }));
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function renderRawView(string $view, ?array $variables = null) : string
|
||||
{
|
||||
return $this->picea->renderHtml($view, $variables ?? [], $this);
|
||||
if ( null === $content = $this->picea->renderHtml($view, $variables ?? [], $this) ) {
|
||||
throw new \RuntimeException("Picea's renderHtml() returned NULL as result ; an error occurred within your template `$view`.");
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function renderView(string $view, ?array $variables = null) : ResponseInterface
|
||||
{
|
||||
return static::renderHtml(
|
||||
@@ -60,54 +62,116 @@ trait ControllerTrait {
|
||||
);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function renderError(string $errorCode, ?array $variables = null) : ResponseInterface
|
||||
{
|
||||
return static::renderHtml(
|
||||
$this->renderRawView("lean/error/{$errorCode}", $variables ?? [])
|
||||
);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function renderDocumentation(string $filename) : ResponseInterface
|
||||
{
|
||||
return $this->renderMarkdown( file_get_contents(getenv("PROJECT_PATH") . "/meta/doc/$filename.md") );
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
protected function redirect(string $url, int $code = 302, array $headers = []) {
|
||||
return new RedirectResponse($url, $code, $headers);
|
||||
return HttpFactory::createRedirectResponse($url, $code, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
protected function refresh(int $code = 204, array $headers = []) {
|
||||
return static::renderNothing($code, [
|
||||
'Refresh' => 0
|
||||
] + $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public static function renderNothing(int $code = 204, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new EmptyResponse($code, $headers);
|
||||
return HttpFactory::createEmptyResponse($code, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public static function renderText(string $text, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new TextResponse($text, $code, $headers);
|
||||
return HttpFactory::createTextResponse($text, $code, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public static function renderHtml(string $html, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new HtmlResponse($html, $code, $headers);
|
||||
return HttpFactory::createHtmlResponse($html, $code, $headers);
|
||||
}
|
||||
|
||||
public static function renderJson(array $data, int $code = 200, array $headers = []) : ResponseInterface
|
||||
#[Ignore]
|
||||
public static function renderJson(mixed $data, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new JsonResponse($data, $code, $headers);
|
||||
return HttpFactory::createJsonResponse($data, $code, $headers);
|
||||
}
|
||||
|
||||
public function renderPdf($rawdata, int $status = 200, array $headers = []) : PdfResponse
|
||||
#[Ignore]
|
||||
public function renderPdf($rawdata, int $status = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new PdfResponse($rawdata, $status, $headers);
|
||||
return HttpFactory::createPdfResponse($rawdata, $status, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public static function renderDownloadable(string $data, string $filename, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new DownloadResponse($data, $filename, $code, $headers);
|
||||
return HttpFactory::createDownloadableResponse($data, $filename, $code, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public static function renderImage(string $data, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new ImageResponse($data, $code, $headers);
|
||||
return HttpFactory::createImageResponse($data, $code, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public static function renderAsset(string $path, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return HttpFactory::createFileDownloadResponse($path, $code, $headers);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function renderMarkdown(string $filepath, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
if ( ! class_exists(CommonMarkConverter::class)) {
|
||||
throw new \BadFunctionCallException("League\CommonMark seems to be missing, please install dependency before trying to render Markdown content");
|
||||
}
|
||||
|
||||
$markdown = ( new CommonMarkConverter() )->convert(file_get_contents($filepath));
|
||||
|
||||
return $this->renderView("lean/layout/docs", get_defined_vars());
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function renderCLI(ServerRequestInterface $request, mixed $data) : ResponseInterface
|
||||
{
|
||||
if (! $data instanceof \Stringable) {
|
||||
if ($data instanceof \JsonSerializable) {
|
||||
return $this->renderJson(
|
||||
$data
|
||||
);
|
||||
}
|
||||
elseif (is_iterable($data) || is_object($data)) {
|
||||
return $this->renderText(var_export($data, true));
|
||||
}
|
||||
}
|
||||
|
||||
return $this->renderText(
|
||||
$data . PHP_EOL
|
||||
);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function fromResponse(ResponseInterface $response)
|
||||
{
|
||||
if ( $response->getStatusCode() === 200 ) {
|
||||
if ( $response instanceof \Zend\Diactoros\Response\JsonResponse) {
|
||||
if ( $response instanceof \Laminas\Diactoros\Response\JsonResponse) {
|
||||
return $response->getPayload();
|
||||
}
|
||||
}
|
||||
@@ -115,6 +179,7 @@ trait ControllerTrait {
|
||||
return null;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function asset(string $url, array $parameters = []) : string
|
||||
{
|
||||
if (! $this->picea ) {
|
||||
@@ -124,6 +189,7 @@ trait ControllerTrait {
|
||||
return $this->picea->compiler->getExtensionFromToken('asset')->buildAssetUrl($url, $parameters);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function url(string $url, array $parameters = []) : string
|
||||
{
|
||||
if (! $this->picea ) {
|
||||
@@ -133,6 +199,7 @@ trait ControllerTrait {
|
||||
return $this->picea->compiler->getExtensionFromToken('url')->buildUrl($url, $parameters);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function route(string $name, array $parameters = []) : string
|
||||
{
|
||||
if (! $this->picea ) {
|
||||
@@ -142,19 +209,39 @@ trait ControllerTrait {
|
||||
return $this->picea->compiler->getExtensionFromToken('route')->buildRouteUrl($name, $parameters);
|
||||
}
|
||||
|
||||
public function json($data, int $flags = 0) {
|
||||
#[Ignore]
|
||||
public function form(FormInterface $form, ? FormContextInterface $formContext = null) : Picea\Ui\Method\FormHandler
|
||||
{
|
||||
if (! $this->picea ) {
|
||||
throw new \Exception("A picea object must be provided on class initialization to use this method.");
|
||||
}
|
||||
|
||||
return $this->picea->compiler->getExtensionInstanceOf(Picea\Ui\Method\Form::class)->formClass($form, $formContext);
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function json($data, int $flags = 0) : string
|
||||
{
|
||||
return htmlentities(json_encode($data, $flags), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
public function pushContext(FormContext $context) : FormContext
|
||||
#[Ignore]
|
||||
public function pushContext(FormContextInterface $context) : FormContextInterface
|
||||
{
|
||||
$this->contextList[$context->formName ?? uniqid("context_")] = $context;
|
||||
|
||||
return $context;
|
||||
}
|
||||
|
||||
public function context(string $name) : FormContext
|
||||
#[Ignore]
|
||||
public function context(? string $name = null) : ? FormContextInterface
|
||||
{
|
||||
return $this->contextList[$name];
|
||||
return $name ? $this->contextList[$name] : array_values($this->contextList)[0] ?? null;
|
||||
}
|
||||
|
||||
#[Ignore]
|
||||
public function isRoute(mixed $name, ServerRequestInterface $request) : bool
|
||||
{
|
||||
return $request->getAttribute('lean.route')->match($name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\Event;
|
||||
|
||||
use Lean\Routing;
|
||||
use Notes\Route\Attribute\Method\Route;
|
||||
|
||||
interface RoutingCompileRoutes {
|
||||
public function execute(Routing $routing, Route $attribute) : void;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\Event;
|
||||
|
||||
use Lean\Routing;
|
||||
use Notes\Route\Attribute\Method\Route;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface };
|
||||
use Ulmus\User\Entity\UserInterface;
|
||||
|
||||
interface RoutingMapRoutes {
|
||||
public function execute(Routing $routing, ContainerInterface $container, ServerRequestInterface & $request, Route $attribute) : void;
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\Factory;
|
||||
|
||||
use Laminas\Diactoros\Response\{EmptyResponse, HtmlResponse, JsonResponse, RedirectResponse, TextResponse};
|
||||
use Lean\Response\{ DownloadResponse, ImageResponse, FileDownloadResponse, PdfResponse };
|
||||
use Laminas\Diactoros\Response;
|
||||
use Laminas\Diactoros\StreamFactory;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
class HttpFactory implements HttpFactoryInterface
|
||||
{
|
||||
public static function createResponse(mixed $body = 'php://memory', int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new Response($body, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createRedirectResponse(string $url, int $code = 302, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new RedirectResponse($url, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createHtmlResponse(string $html, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new HtmlResponse($html, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createTextResponse(string $text, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new TextResponse($text, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createJsonResponse(mixed $data, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new JsonResponse($data, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createEmptyResponse(int $code = 204, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new EmptyResponse($code, $headers);
|
||||
}
|
||||
|
||||
public static function createPdfResponse(\Stringable|string $binary, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new PdfResponse((string) $binary, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createDownloadableResponse(string $data, string $filename, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new DownloadResponse($data, $filename, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createImageResponse(string $data, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new ImageResponse($data, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createFileDownloadResponse(string $path, int $code = 200, array $headers = []) : ResponseInterface
|
||||
{
|
||||
return new FileDownloadResponse($path, $code, $headers);
|
||||
}
|
||||
|
||||
public static function createStream(string $content): StreamInterface
|
||||
{
|
||||
return new StreamFactory()->createStream($content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\Factory;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
interface HttpFactoryInterface
|
||||
{
|
||||
public static function createResponse(string $url, int $code = 302, array $headers = []) : ResponseInterface;
|
||||
public static function createRedirectResponse(string $url, int $code = 302, array $headers = []) : ResponseInterface;
|
||||
public static function createHtmlResponse(string $html, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createTextResponse(string $text, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createJsonResponse(mixed $data, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createEmptyResponse(int $code = 204, array $headers = []) : ResponseInterface;
|
||||
public static function createPdfResponse(\Stringable|string $binary, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createDownloadableResponse(string $data, string $filename, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createImageResponse(string $data, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createFileDownloadResponse(string $path, int $code = 200, array $headers = []) : ResponseInterface;
|
||||
public static function createStream(string $content) : StreamInterface;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace Lean;
|
||||
|
||||
class File
|
||||
{
|
||||
public static function tail(string $path, int $lines = 10) : false|array
|
||||
{
|
||||
$handle = fopen($path, "r");
|
||||
|
||||
$counter = $lines;
|
||||
|
||||
$pos = -2;
|
||||
$beginning = false;
|
||||
$text = array();
|
||||
|
||||
while ($counter && !$beginning) {
|
||||
$t = "";
|
||||
|
||||
while ($t !== PHP_EOL) {
|
||||
if (fseek($handle, $pos, SEEK_END) == -1) {
|
||||
$beginning = true;
|
||||
break;
|
||||
}
|
||||
|
||||
$t = fgetc($handle);
|
||||
$pos--;
|
||||
}
|
||||
|
||||
$counter--;
|
||||
|
||||
if ($beginning) {
|
||||
rewind($handle);
|
||||
}
|
||||
|
||||
$text[$lines - $counter - 1] = fgets($handle);
|
||||
}
|
||||
|
||||
fclose($handle);
|
||||
|
||||
return array_reverse($text);
|
||||
}
|
||||
|
||||
public static function sanitizeFilePath($path) : string
|
||||
{
|
||||
// Remove null bytes
|
||||
$path = str_replace("\0", "", $path);
|
||||
|
||||
$path = str_replace([ "\\", "/" ], DIRECTORY_SEPARATOR, $path);
|
||||
|
||||
$path = preg_replace('#' . DIRECTORY_SEPARATOR . '+#', DIRECTORY_SEPARATOR, $path);
|
||||
|
||||
// Remove . and .. directory traversal attempts
|
||||
$path = preg_replace('#\.{1,2}/#', '', $path);
|
||||
|
||||
$path = trim($path, '/\\');
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
public static function assertFileExists(string $filepath) : void
|
||||
{
|
||||
if (! is_file($filepath)) {
|
||||
throw new \InvalidArgumentException("File does not exist: {$filepath}");
|
||||
}
|
||||
}
|
||||
|
||||
public static function assertFolderExists(string $path) : void
|
||||
{
|
||||
if (! is_dir($path)) {
|
||||
throw new \InvalidArgumentException("Folder does not exist: {$path}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+76
-35
@@ -14,8 +14,10 @@ use Psr\Http\Message\ServerRequestInterface;
|
||||
use Tell\I18n;
|
||||
use Ulmus\Container\AdapterProxy;
|
||||
|
||||
use Zend\Diactoros\ServerRequestFactory,
|
||||
Zend\HttpHandlerRunner\Emitter\EmitterInterface;
|
||||
use Laminas\Diactoros\ServerRequestFactory,
|
||||
Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
|
||||
|
||||
use Ulmus\Ulmus;
|
||||
|
||||
class Kernel {
|
||||
|
||||
@@ -25,19 +27,21 @@ class Kernel {
|
||||
|
||||
public array $paths = [];
|
||||
|
||||
public int $errorReporting = E_ALL & ~E_USER_DEPRECATED & ~E_DEPRECATED & ~E_STRICT & ~E_NOTICE;
|
||||
public int $errorReportingDev = -1;
|
||||
|
||||
public string $definitionFilePath;
|
||||
public int $errorReportingProd = E_ALL & ~E_USER_DEPRECATED & ~E_DEPRECATED /*& ~E_NOTICE*/;
|
||||
|
||||
public array $definitionFilePaths;
|
||||
|
||||
public string $errorLogPath;
|
||||
|
||||
public string $projectPath;
|
||||
public function __construct(
|
||||
public string $projectPath
|
||||
) {
|
||||
# Still not sure about adding a folder check each call just to add a basic project setup error ...
|
||||
# File::assertFolderExists($this->projectPath);
|
||||
|
||||
public string $routeDefinitionList = 'routes.list';
|
||||
|
||||
public function __construct(string $projectPath)
|
||||
{
|
||||
$this->projectPath = $projectPath;
|
||||
define('IS_CLI', php_sapi_name() === 'cli' OR defined('STDIN'));
|
||||
|
||||
$this->setEnvironment();
|
||||
$this->initializeEngine();
|
||||
@@ -46,22 +50,30 @@ class Kernel {
|
||||
$this->handleRequest();
|
||||
}
|
||||
|
||||
public static function putenv($var, $value)
|
||||
public static function putenv($var, $value) : void
|
||||
{
|
||||
putenv("$var=$value");
|
||||
$_ENV[$var] = $value;
|
||||
}
|
||||
|
||||
protected function setEnvironment() {
|
||||
protected function setEnvironment() : void
|
||||
{
|
||||
static::putenv('PROJECT_PATH', $this->projectPath);
|
||||
|
||||
// Environment vars (accessible from \DI\env(), getenv(), $_ENV and $_SERVER)
|
||||
Dotenv::create(getenv("PROJECT_PATH"))->load();
|
||||
|
||||
// Override using headers
|
||||
foreach(['APP_ENV', 'DEBUG', ] as $env) {
|
||||
if ( null !== $value = $_SERVER["HTTP_$env"] ?? null ) {
|
||||
static::putenv($env, $value);
|
||||
if (($keys = getenv('KEYS')) && ($auth = $_SERVER["HTTP_X_DEV_AUTH"] ?? null) && in_array($auth, explode(',', $keys))) {
|
||||
unset($_SERVER["HTTP_X_DEV_AUTH"]);
|
||||
|
||||
unset($_ENV['KEYS'], $_SERVER['KEYS']);
|
||||
putenv("KEYS=");
|
||||
|
||||
foreach(['APP_ENV', 'DEBUG',] as $env) {
|
||||
if (null !== $value = $_SERVER["HTTP_X_$env"] ?? null) {
|
||||
static::putenv($env, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +83,11 @@ class Kernel {
|
||||
$path = getenv("PROJECT_PATH") . DIRECTORY_SEPARATOR . getenv($envkey);
|
||||
|
||||
if (getenv('DEBUG') && ! file_exists($path)) {
|
||||
mkdir($path, 0755, true);
|
||||
if (false === @mkdir($path, 0755, true)) {
|
||||
if (! IS_CLI) {
|
||||
throw new \Exception(sprintf("Folder '%s' could not be created", $path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static::putenv($name, realpath($path));
|
||||
@@ -91,11 +107,16 @@ class Kernel {
|
||||
\Locale::setDefault($this->locale);
|
||||
}
|
||||
|
||||
ini_set("log_errors", "1");
|
||||
ini_set("error_log", $this->errorLogPath);
|
||||
ini_set('display_errors', getenv("DEBUG") ? 'on' : 'on');
|
||||
ini_set("error_log", $this->errorLogPath ?? getenv("LOGS_PATH") . DIRECTORY_SEPARATOR. date("Y-m").".log");
|
||||
ini_set("log_errors", true);
|
||||
ini_set('display_errors', getenv("DEBUG") ? 'on' : 'off');
|
||||
|
||||
error_reporting($this->errorReporting);
|
||||
if (getenv("DEBUG")) {
|
||||
error_reporting(getenv('ERROR_REPORTING_DEV') ?: $this->errorReportingDev);
|
||||
}
|
||||
else {
|
||||
error_reporting(getenv('ERROR_REPORTING_PROD') ?: $this->errorReportingProd);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -104,19 +125,40 @@ class Kernel {
|
||||
{
|
||||
$containerBuilder = new ContainerBuilder();
|
||||
|
||||
$containerBuilder->useAttributes(true);
|
||||
|
||||
if (getenv("APP_ENV") === "prod") {
|
||||
if (getenv("CACHE_PATH")) {
|
||||
# $containerBuilder->enableCompilation(getenv("CACHE_PATH") . "/di/");
|
||||
# $containerBuilder->writeProxiesToFile(true);
|
||||
# check if ACPU is there first, $containerBuilder->enableDefinitionCache();
|
||||
|
||||
$containerBuilder->enableCompilation(getenv("CACHE_PATH") . "/di/");
|
||||
# No proxies yet... $containerBuilder->writeProxiesToFile(true, getenv("CACHE_PATH") . "/di/");
|
||||
}
|
||||
}
|
||||
else {
|
||||
$compiled = getenv("CACHE_PATH") . "/di/CompiledContainer.php";
|
||||
|
||||
if (file_exists($compiled)) {
|
||||
unlink($compiled);
|
||||
}
|
||||
}
|
||||
|
||||
$containerBuilder->useAnnotations(false);
|
||||
|
||||
if ($this->definitionFilePath ?? false) {
|
||||
$containerBuilder->addDefinitions(require($this->definitionFilePath));
|
||||
foreach(Lean::getDefinitionsPathsFromComposer() as $path) {
|
||||
$containerBuilder->addDefinitions($path);
|
||||
}
|
||||
|
||||
foreach($this->definitionFilePaths ?? [] as $path) {
|
||||
$containerBuilder->addDefinitions($path);
|
||||
}
|
||||
|
||||
$containerBuilder->addDefinitions([
|
||||
'config' => function () {
|
||||
return array_merge_recursive(
|
||||
Lean::autoloadConfigFromComposerExtra(), []
|
||||
);
|
||||
}
|
||||
]);
|
||||
|
||||
$this->container = $containerBuilder->build();
|
||||
|
||||
return $this;
|
||||
@@ -124,15 +166,15 @@ class Kernel {
|
||||
|
||||
protected function serviceContainer() : self
|
||||
{
|
||||
$this->container->has(AdapterProxy::class) and $this->container->get(AdapterProxy::class);
|
||||
$this->container->has(Lean::class) and $this->container->get(Lean::class);
|
||||
|
||||
if ($this->container->has(I18n::class)) {
|
||||
$i18n = $this->container->get(I18n::class);
|
||||
$i18n->locale($this->locale);
|
||||
$i18n->initialize(!getenv("DEBUG"));
|
||||
if ($this->container->has('lean.autoload')) {
|
||||
foreach($this->container->get('lean.autoload') as $class) {
|
||||
$this->container->get($class);
|
||||
}
|
||||
}
|
||||
|
||||
# Must be removed from KERNEL !
|
||||
$this->container->has('ulmus.caching') and (Ulmus::$cache = $this->container->get('ulmus.caching'));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -140,8 +182,7 @@ class Kernel {
|
||||
{
|
||||
ServerRequestFactory::fromGlobals($_SERVER, $_GET, $_POST, $_COOKIE, $_FILES);
|
||||
|
||||
// Router
|
||||
$routeFactory = $this->container->get($this->routeDefinitionList);
|
||||
$routeFactory = $this->container->get(Routing\RouteDefinitionInterface::class);
|
||||
|
||||
$router = $routeFactory($this->container);
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Lean;
|
||||
|
||||
use Picea\Extension\LanguageHandlerInterface;
|
||||
use Tell;
|
||||
|
||||
class LanguageHandler implements LanguageHandlerInterface {
|
||||
|
||||
public function __construct(
|
||||
public Tell\I18n $tell
|
||||
) {}
|
||||
|
||||
public function languageFromKey(string $key, array $variables = []) #: array|string
|
||||
{
|
||||
return $this->tell->fromKey($key, $variables) ?: "";
|
||||
}
|
||||
}
|
||||
+159
-25
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Lean;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
@@ -22,15 +21,18 @@ class Lean
|
||||
|
||||
protected function loadApplications() : void
|
||||
{
|
||||
$list = $this->container->get('config')['lean']['autoload'] ?? [];
|
||||
$list = array_filter($this->container->get('config')['lean']['autoload'] ?? []);
|
||||
|
||||
if (! $list ) {
|
||||
throw new \Exception("You must provide at least one application to autoload within your config file ( 'lean' => 'autoload' => [] )");
|
||||
}
|
||||
|
||||
foreach(array_filter($list) as $application) {
|
||||
# Allows 'lean.default' and [ 'lean.default', 100 ] which allows ordering of apps loading.
|
||||
usort($list, fn($e1, $e2) => ( is_array($e1) ? $e1[1] : 0 ) <=> ( is_array($e2) ? $e2[1] : 0 ));
|
||||
|
||||
foreach(array_map(fn($item) => is_array($item) ? $item[0] : $item, $list) as $application) {
|
||||
if ( $this->container->has($application) ) {
|
||||
$this->applications[] = ( new Application() )->fromArray($this->container->get($application));
|
||||
$this->applications[] = ( new Application($application) )->fromArray($this->container->get($application));
|
||||
}
|
||||
else {
|
||||
throw new \RuntimeException("Trying to load an application '$application' which have not been configured yet");
|
||||
@@ -38,7 +40,18 @@ class Lean
|
||||
}
|
||||
}
|
||||
|
||||
public function getPiceaContext() : string
|
||||
public function getApplication(string $name) : ? Application
|
||||
{
|
||||
foreach($this->applications as $app) {
|
||||
if ($app->name === $name) {
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPiceaContext() /*: string|array */
|
||||
{
|
||||
foreach(array_reverse($this->applications) as $apps) {
|
||||
if ( $apps->piceaContext ?? null ) {
|
||||
@@ -64,38 +77,80 @@ class Lean
|
||||
|
||||
public function getRoutable() : array
|
||||
{
|
||||
return array_merge(...array_map(fn($app) => $app->routes ?? [], $this->applications));
|
||||
return array_merge(...array_map(fn(Application $app) => $app->routes ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getCronard() : array
|
||||
{
|
||||
return array_merge(...array_map(fn($app) => $app->cronard ?? [], $this->applications));
|
||||
return array_merge(...array_map(fn(Application $app) => $app->cronard ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getCLI() : array
|
||||
{
|
||||
return array_merge(...array_map(fn(Application $app) => $app->cli ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getEvents() : array
|
||||
{
|
||||
return array_merge(...array_map(fn(Application $app) => $app->events ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getTaxusPrivileges() : array
|
||||
{
|
||||
return array_merge(...array_map(fn(Application $app) => $app->taxus ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getEntities() : array
|
||||
{
|
||||
return array_merge(...array_map(fn($app) => $app->entities ?? [], $this->applications));
|
||||
return array_merge(...array_map(fn(Application $app) => $app->entities ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getConnectionAdapters() : array
|
||||
{
|
||||
return array_merge(...array_map(fn(Application $app) => $app->connectionAdapters ?? [], $this->applications));
|
||||
}
|
||||
|
||||
public function getViewPaths() : array
|
||||
{
|
||||
$list = array_merge(...array_map(fn($app) => $app->views ?? [], $this->applications));
|
||||
$list = array_merge(...array_map(fn(Application $app) => $app->views ?? [], $this->applications));
|
||||
|
||||
$this->verifyPathList($list);
|
||||
|
||||
uasort($list, fn($i1, $i2) => $i1['order'] <=> $i2['order'] );
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function getAssetPaths() : array
|
||||
{
|
||||
$list = array_merge(...array_map(fn(Application $app) => $app->piceaAssets ?? [], $this->applications));
|
||||
|
||||
$this->verifyPathList($list);
|
||||
|
||||
uasort($list, fn($i1, $i2) => $i1['order'] <=> $i2['order'] );
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected function verifyPathList(array $list) : void
|
||||
{
|
||||
foreach($list as $item) {
|
||||
if (! isset($item['order']) ) {
|
||||
throw new \RuntimeException(sprintf("An error occured while verifying a path list (%s)", json_encode($item, \JSON_PRETTY_PRINT)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getI18n(string $reader) : ? array
|
||||
{
|
||||
switch($reader) {
|
||||
case "php":
|
||||
$list = array_merge(...array_map(fn($app) => $app->tellPhp ?? [], $this->applications));
|
||||
break;
|
||||
$list = array_merge(...array_map(fn(Application $app) => $app->tellPhp ?? [], $this->applications));
|
||||
break;
|
||||
|
||||
case "json":
|
||||
$list = array_merge(...array_map(fn($app) => $app->tellJson ?? [], $this->applications));
|
||||
break;
|
||||
$list = array_merge(...array_map(fn(Application $app) => $app->tellJson ?? [], $this->applications));
|
||||
break;
|
||||
}
|
||||
|
||||
if ( $list ?? false ) {
|
||||
@@ -107,18 +162,97 @@ class Lean
|
||||
return null;
|
||||
}
|
||||
|
||||
#[\Deprecated("Definitions are now loaded automatically from composer extra array.")]
|
||||
public static function definitions() : array
|
||||
{
|
||||
$path = dirname(__DIR__) . "/meta/definitions/";
|
||||
|
||||
return array_merge(
|
||||
require($path . "email.php"),
|
||||
require($path . "http.php"),
|
||||
require($path . "language.php"),
|
||||
require($path . "routes.php"),
|
||||
require($path . "security.php"),
|
||||
require($path . "software.php"),
|
||||
require($path . "template.php"),
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public static function getDefinitionsPathsFromComposer() : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach(Composer::readComposerLock()['packages'] as $package) {
|
||||
$order = $package['extra']['lean']['autoload']['order'] ?? 0;
|
||||
|
||||
foreach($package['extra']['lean']['autoload']['definitions'] ?? [] as $autoload) {
|
||||
$list[] = [ static::pathFromPackage($package, $autoload), $order ];
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Composer::readComposerJson()['extra']['lean']['autoload']['definitions'] ?? [] as $autoload) {
|
||||
$order = $package['extra']['lean']['autoload']['order'] ?? 1000;
|
||||
|
||||
$list[] = [ getenv('PROJECT_PATH') . DIRECTORY_SEPARATOR . $autoload, $order ];
|
||||
}
|
||||
|
||||
# Allows 'lean.default' and [ 'lean.default', 100 ] which allows ordering of apps loading.
|
||||
usort($list, fn($e1, $e2) => $e1[1] <=> $e2[1]);
|
||||
|
||||
return array_column($list, 0);
|
||||
}
|
||||
|
||||
public static function autoloadConfigFromComposerExtra() : array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
foreach(Composer::readComposerLock()['packages'] as $package) {
|
||||
foreach($package['extra']['lean']['autoload']['config'] ?? [] as $autoload) {
|
||||
$list = array_merge_recursive($list, static::loadFromPackage($package, $autoload));
|
||||
}
|
||||
}
|
||||
|
||||
foreach(Composer::readComposerJson()['extra']['lean']['autoload']['config'] ?? [] as $autoload) {
|
||||
$list = array_merge_recursive($list, static::loadFromPackage(null, $autoload));
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
protected static function pathFromPackage(?array $package, array|string $autoload) : string
|
||||
{
|
||||
if ($package === null) {
|
||||
$filepath = getenv('PROJECT_PATH') . DIRECTORY_SEPARATOR . $autoload;
|
||||
}
|
||||
else {
|
||||
$vendor = getenv('VENDOR_DIR') ? getenv('VENDOR_PATH') : dirname(__DIR__, 3);
|
||||
$filepath = $vendor . DIRECTORY_SEPARATOR . $package['name'] . DIRECTORY_SEPARATOR . $autoload;
|
||||
}
|
||||
|
||||
if (! file_exists($filepath)) {
|
||||
throw new \InvalidArgumentException("Given definition filepath do not exists '$filepath'");
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
protected static function loadFromPackage(? array $package, array|string $autoload) : false|array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
if (is_string($autoload)) {
|
||||
$file = static::pathFromPackage($package, $autoload);
|
||||
|
||||
if ( ! file_exists($file) ) {
|
||||
throw new \InvalidArgumentException(sprintf("Given autoload file `%s` from package `%s` was not found or is unreachable", $autoload, $package['name']));
|
||||
}
|
||||
|
||||
return require($file);
|
||||
}
|
||||
|
||||
$func = implode('::', array_merge([ key($autoload) ], $autoload));
|
||||
|
||||
return call_user_func($func);
|
||||
}
|
||||
|
||||
public static function path(...$parts) : string
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, $parts);
|
||||
}
|
||||
|
||||
|
||||
public static function rawPath(...$parts) : string
|
||||
{
|
||||
return implode(DIRECTORY_SEPARATOR, $parts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace Lean;
|
||||
|
||||
use Picea\Compiler;
|
||||
use Picea\Language\DefaultRegistrations;
|
||||
use Picea\{ Ui\Ui, Asset\Asset };
|
||||
|
||||
class PiceaDefaultRegistration extends DefaultRegistrations {
|
||||
|
||||
public function __construct(
|
||||
protected array $extensions,
|
||||
protected array $syntaxes,
|
||||
protected array $controlStructures,
|
||||
public null|Ui $ui,
|
||||
public null|Asset $asset,
|
||||
) {
|
||||
parent::__construct($this->extensions, $this->syntaxes, $this->controlStructures);
|
||||
}
|
||||
|
||||
public function registerAll(Compiler $compiler) : void
|
||||
{
|
||||
parent::registerAll($compiler);
|
||||
|
||||
$this->ui AND $this->ui->registerFormExtension($compiler);
|
||||
$this->asset AND $this->asset->registerExtension($compiler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Lean\Response;
|
||||
|
||||
use function get_class, gettype, is_object, is_string, sprintfm, pathinfo, PATHINFO_EXTENSION;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
use Laminas\Diactoros\Exception,
|
||||
Laminas\Diactoros\Response,
|
||||
Laminas\Diactoros\Stream,
|
||||
Laminas\Diactoros\Response\InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* PDF Response
|
||||
*
|
||||
* Allows creating a response by passing a string to the constructor;
|
||||
* by default, sets a status code of 200 and sets the Content-Type header to
|
||||
* application/pdf.
|
||||
*/
|
||||
class DownloadResponse extends Response
|
||||
{
|
||||
use InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* Create a PDF response
|
||||
*
|
||||
* Produces a pdf response with a Content-Type of application/json and a default
|
||||
* status of 200.
|
||||
*
|
||||
* @param string|StreamInterface $content String or stream for the message body.
|
||||
* @param int $status Integer status code for the response; 200 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
* @throws Exception\InvalidArgumentException if $filepath is neither a string or stream.
|
||||
*/
|
||||
public function __construct(string $content, string $filename, int $status = 200, array $headers = [])
|
||||
{
|
||||
parent::__construct(
|
||||
$this->createBody($content),
|
||||
$status,
|
||||
$this->injectFilename($filename, $this->injectContentType("application/octet-stream", $headers))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the provided Content-Type, if none is already present.
|
||||
*
|
||||
* @return array Headers with injected Content-Type
|
||||
*/
|
||||
private function injectFilename(string $filename, array $headers) : array
|
||||
{
|
||||
$hasContentType = array_reduce(array_keys($headers), function ($carry, $item) {
|
||||
return $carry ?: (strtolower($item) === 'content-disposition');
|
||||
}, false);
|
||||
|
||||
if (! $hasContentType) {
|
||||
$headers['content-disposition'] = ['Content-Disposition:attachment; filename="' . $filename . '"'];
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the message body.
|
||||
*
|
||||
* @param string|StreamInterface $content
|
||||
* @throws Exception\InvalidArgumentException if $content is neither a string or stream.
|
||||
*/
|
||||
private function createBody(string $content) : StreamInterface
|
||||
{
|
||||
if ($content instanceof StreamInterface) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$body = new Stream('php://temp', 'wb+');
|
||||
$body->write($content);
|
||||
$body->rewind();
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Lean\Response;
|
||||
|
||||
use function get_class, gettype, is_object, is_string, sprintfm, pathinfo, PATHINFO_EXTENSION;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
use Laminas\Diactoros\Exception,
|
||||
Laminas\Diactoros\Response,
|
||||
Laminas\Diactoros\Stream,
|
||||
Laminas\Diactoros\Response\InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* PDF Response
|
||||
*
|
||||
* Allows creating a response by passing a string to the constructor;
|
||||
* by default, sets a status code of 200 and sets the Content-Type header to
|
||||
* application/pdf.
|
||||
*/
|
||||
class FileDownloadResponse extends Response
|
||||
{
|
||||
use InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* Create a PDF response
|
||||
*
|
||||
* Produces a pdf response with a Content-Type of application/json and a default
|
||||
* status of 200.
|
||||
*
|
||||
* @param string|StreamInterface $filepath String or stream for the message body.
|
||||
* @param int $status Integer status code for the response; 200 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
* @throws Exception\InvalidArgumentException if $filepath is neither a string or stream.
|
||||
*/
|
||||
public function __construct($filepath, int $status = 200, array $headers = [])
|
||||
{
|
||||
$body = $this->createBody($filepath);
|
||||
|
||||
if (class_exists(\Mimey\MimeTypes::class)) {
|
||||
$mime = (new \Mimey\MimeTypes())->getMimeType(pathinfo($filepath, PATHINFO_EXTENSION));
|
||||
}
|
||||
else {
|
||||
$finfo = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = finfo_file($finfo, $filepath);
|
||||
}
|
||||
|
||||
parent::__construct(
|
||||
$body,
|
||||
$status,
|
||||
$this->injectContentType($mime, $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the message body.
|
||||
*
|
||||
* @param string|StreamInterface $filepath
|
||||
* @throws Exception\InvalidArgumentException if $filepath is neither a string or stream.
|
||||
*/
|
||||
private function createBody($filepath) : StreamInterface
|
||||
{
|
||||
if ($filepath instanceof StreamInterface) {
|
||||
return $filepath;
|
||||
}
|
||||
|
||||
if (! is_string($filepath)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'Invalid content (%s) provided to %s',
|
||||
(is_object($filepath) ? get_class($filepath) : gettype($filepath)),
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
if ( ! file_exists($filepath) ) {
|
||||
throw new Exception\InvalidArgumentException("Given file (%s) do not look like a valid file path.", $filepath);
|
||||
}
|
||||
|
||||
if ( ! is_readable($filepath) ) {
|
||||
throw new Exception\InvalidArgumentException("Given file (%s) do not seem to be readable. This could indicate a permission problem", $filepath);
|
||||
}
|
||||
|
||||
return new Stream(fopen($filepath, "r"), 'r');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Lean\Response;
|
||||
|
||||
use function get_class, gettype, is_object, is_string, sprintf;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
use Laminas\Diactoros\Exception,
|
||||
Laminas\Diactoros\Response,
|
||||
Laminas\Diactoros\Stream,
|
||||
Laminas\Diactoros\Response\InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* PDF Response
|
||||
*
|
||||
* Allows creating a response by passing a string to the constructor;
|
||||
* by default, sets a status code of 200 and sets the Content-Type header to
|
||||
* application/pdf.
|
||||
*/
|
||||
class ImageResponse extends Response
|
||||
{
|
||||
use InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* Create a PDF response
|
||||
*
|
||||
* Produces a pdf response with a Content-Type of application/json and a default
|
||||
* status of 200.
|
||||
*
|
||||
* @param string|StreamInterface $content String or stream for the message body.
|
||||
* @param int $status Integer status code for the response; 200 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
* @throws Exception\InvalidArgumentException if $content is neither a string or stream.
|
||||
*/
|
||||
public function __construct($content, int $status = 200, array $headers = [])
|
||||
{
|
||||
if ( false === ( array_change_key_case($headers)['content-disposition'] ?? false )) {
|
||||
$headers['Content-Disposition'] = 'inline';
|
||||
}
|
||||
|
||||
parent::__construct(
|
||||
$this->createBody($content),
|
||||
$status,
|
||||
$this->injectContentType('image', $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the message body.
|
||||
*
|
||||
* @param string|StreamInterface $content
|
||||
* @throws Exception\InvalidArgumentException if $content is neither a string or stream.
|
||||
*/
|
||||
private function createBody($content) : StreamInterface
|
||||
{
|
||||
if ($content instanceof StreamInterface) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
if (! is_string($content)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'Invalid content (%s) provided to %s',
|
||||
(is_object($content) ? get_class($content) : gettype($content)),
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
$body = new Stream('php://temp', 'wb+');
|
||||
$body->write($content);
|
||||
$body->rewind();
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Lean\Response;
|
||||
|
||||
use Laminas\Diactoros\Response\HtmlResponse;
|
||||
use League\CommonMark\CommonMarkConverter;
|
||||
|
||||
/**
|
||||
* Markdown response.
|
||||
*
|
||||
* Permet de convertir un fichier Markdown en html, et utilise en suite le HtmlResponse
|
||||
* pour pousser le résultat en ligne, avec un code 200 par défault et un Content-Type approprié
|
||||
*/
|
||||
class MarkdownResponse extends HtmlResponse
|
||||
{
|
||||
public function __construct(string $markdown, int $status = 200, array $headers = [])
|
||||
{
|
||||
parent::__construct(( new CommonMarkConverter() )->convertToHtml($markdown), $status, $headers);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Lean\Response;
|
||||
|
||||
use function get_class, gettype, is_object, is_string, sprintf;
|
||||
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
|
||||
use Laminas\Diactoros\Exception,
|
||||
Laminas\Diactoros\Response,
|
||||
Laminas\Diactoros\Stream,
|
||||
Laminas\Diactoros\Response\InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* PDF Response
|
||||
*
|
||||
* Allows creating a response by passing a string to the constructor;
|
||||
* by default, sets a status code of 200 and sets the Content-Type header to
|
||||
* application/pdf.
|
||||
*/
|
||||
class PdfResponse extends Response
|
||||
{
|
||||
use InjectContentTypeTrait;
|
||||
|
||||
/**
|
||||
* Create a PDF response
|
||||
*
|
||||
* Produces a pdf response with a Content-Type of application/json and a default
|
||||
* status of 200.
|
||||
*
|
||||
* @param string|StreamInterface $content String or stream for the message body.
|
||||
* @param int $status Integer status code for the response; 200 by default.
|
||||
* @param array $headers Array of headers to use at initialization.
|
||||
* @throws Exception\InvalidArgumentException if $content is neither a string or stream.
|
||||
*/
|
||||
public function __construct($content, int $status = 200, array $headers = [])
|
||||
{
|
||||
parent::__construct(
|
||||
$this->createBody($content),
|
||||
$status,
|
||||
$this->injectContentType('application/pdf; charset=utf-8', $headers)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the message body.
|
||||
*
|
||||
* @param string|StreamInterface $content
|
||||
* @throws Exception\InvalidArgumentException if $content is neither a string or stream.
|
||||
*/
|
||||
private function createBody($content) : StreamInterface
|
||||
{
|
||||
if ($content instanceof StreamInterface) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
if (! is_string($content)) {
|
||||
throw new Exception\InvalidArgumentException(sprintf(
|
||||
'Invalid content (%s) provided to %s',
|
||||
(is_object($content) ? get_class($content) : gettype($content)),
|
||||
__CLASS__
|
||||
));
|
||||
}
|
||||
|
||||
$body = new Stream('php://temp', 'wb+');
|
||||
$body->write($content);
|
||||
$body->rewind();
|
||||
return $body;
|
||||
}
|
||||
}
|
||||
+60
-79
@@ -2,112 +2,93 @@
|
||||
|
||||
namespace Lean;
|
||||
|
||||
use Taxus\Taxus;
|
||||
|
||||
use League\Route\RouteGroup,
|
||||
League\Route\Router;
|
||||
|
||||
use Notes\Route\Attribute\Method\Route;
|
||||
use Psr\Http\Message\ServerRequestInterface,
|
||||
Psr\Http\Message\ResponseInterface,
|
||||
Psr\Container\ContainerInterface;
|
||||
|
||||
use Notes\Route\RouteFetcher;
|
||||
|
||||
use Notes\Security\SecurityHandler;
|
||||
|
||||
use Notes\Tell\LanguageHandler;
|
||||
|
||||
use Picea\Picea,
|
||||
Picea\Extension\UrlExtension;
|
||||
|
||||
use Storage\Cookie,
|
||||
Storage\Session;
|
||||
|
||||
use function DI\autowire, DI\create;
|
||||
use Mcnd\Event\EventManager;
|
||||
|
||||
class Routing {
|
||||
|
||||
protected Session $session;
|
||||
public ResponseInterface $response;
|
||||
|
||||
protected Cookie $cookie;
|
||||
|
||||
protected UrlExtension $extension;
|
||||
|
||||
protected RouteFetcher $fetcher;
|
||||
|
||||
protected SecurityHandler $security;
|
||||
|
||||
protected LanguageHandler $language;
|
||||
|
||||
protected Taxus $taxus;
|
||||
public array $overriddenRoutes = [];
|
||||
|
||||
public function __construct(
|
||||
Session $session,
|
||||
Cookie $cookie,
|
||||
UrlExtension $extension,
|
||||
Router $router,
|
||||
RouteFetcher $routeFetcher,
|
||||
SecurityHandler $security,
|
||||
LanguageHandler $language,
|
||||
Taxus $taxus
|
||||
)
|
||||
{
|
||||
$this->session = $session;
|
||||
$this->cookie = $cookie;
|
||||
$this->extension = $extension;
|
||||
$this->fetcher = $routeFetcher;
|
||||
$this->security = $security;
|
||||
$this->language = $language;
|
||||
$this->router = $router;
|
||||
$this->taxus = $taxus;
|
||||
}
|
||||
public Router $router,
|
||||
public RouteFetcher $fetcher,
|
||||
public EventManager $eventManager,
|
||||
) { }
|
||||
|
||||
public function registerRoute(ContainerInterface $container, string $urlBase) {
|
||||
$this->router->group(rtrim($urlBase, "/"), function (RouteGroup $route) use ($container) {
|
||||
foreach($this->fetcher->compile() as $annotation) {
|
||||
# Register routes to the UrlExtension from picea (handling url, route and asset extensions)
|
||||
if ( null !== ( $name = $annotation->name ?? null ) ) {
|
||||
$this->extension->registerRoute($name, $annotation->getRoute(), $annotation->class, $annotation->classMethod, $annotation->methods ?? (array) $annotation->method);
|
||||
}
|
||||
|
||||
foreach((array) ( $annotation->method ?? $annotation->methods ) as $method) {
|
||||
# Mapping every URLs from annotations in searched folders (Api, Controller, etc...)
|
||||
$route->map(strtoupper($method), $annotation->getRoute(), function (ServerRequestInterface $request, array $arguments) use (
|
||||
$container, $route, $annotation
|
||||
) : ResponseInterface
|
||||
{
|
||||
$class = $annotation->class;
|
||||
$method = $annotation->classMethod;
|
||||
|
||||
# $container->set($class, autowire($class)->method($method, $request));
|
||||
|
||||
if ( null !== ( $languageAnnotation = $this->language->verify($class) ) ) {
|
||||
if ( $languageAnnotation->key ) {
|
||||
# TODO !!! $language
|
||||
}
|
||||
}
|
||||
foreach($this->fetchRoutesAttributes() as $attribute) {
|
||||
$this->eventManager->execute(Event\RoutingCompileRoutes::class, $this, $attribute);
|
||||
|
||||
# Mapping every URLs from attributes in searched folders (Api, Controller, etc...)
|
||||
foreach((array) $attribute->method as $method) {
|
||||
$route->map(strtoupper($method), $attribute->getRegistrableRoute(), function (ServerRequestInterface $request, array $arguments) use (
|
||||
$container, $route, $attribute
|
||||
) : ResponseInterface {
|
||||
$class = $attribute->class;
|
||||
$method = $attribute->classMethod;
|
||||
$object = $container->get($class);
|
||||
|
||||
# Checking if user needs to be logged
|
||||
if ( ( $redirect = $this->security->verify($class, $method) ) && ( empty($object->user) || ! $object->user->logged ) ) {
|
||||
$this->session->redirectedFrom = (string) $request->getUri();
|
||||
$this->eventManager->execute(Event\RoutingMapRoutes::class, $this, $container, $request, $attribute);
|
||||
|
||||
return $redirect;
|
||||
$container->set(ServerRequestInterface::class, $request);
|
||||
|
||||
# Decoding arguments received from the router
|
||||
foreach($arguments as &$arg) {
|
||||
$arg = urldecode($arg);
|
||||
}
|
||||
|
||||
if ( $forbidden = $this->security->taxus($class, $method, $object->user) ) {
|
||||
return $forbidden;
|
||||
}
|
||||
|
||||
if ( $container->has(Picea::class) ) {
|
||||
$container->get(Picea::class)->globalVariables['route'] = $annotation;
|
||||
}
|
||||
|
||||
return $object->$method($request->withAttribute('lean.route', $annotation), $arguments);
|
||||
return $this->response ?? $object->$method($request, $arguments);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function fetchRoutesAttributes() : iterable
|
||||
{
|
||||
static $list = [];
|
||||
|
||||
if ($list) {
|
||||
return $list;
|
||||
}
|
||||
|
||||
foreach($this->fetcher->compile() as $routeAttribute) {
|
||||
# Routes with differents methods can have the same name
|
||||
$key = sprintf('{%s}~{%s}', $routeAttribute->name, $this->serializeMethods((array) $routeAttribute->method));
|
||||
|
||||
if ( isset($list[$key]) ) {
|
||||
$this->overriddenRoutes[$key] = $list[$key];
|
||||
}
|
||||
|
||||
$list[$key] = $routeAttribute;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function findOverriddenRoute(Route $route) : false|iterable
|
||||
{
|
||||
return array_filter($this->overriddenRoutes, fn($e) => $route->name === $e->name);
|
||||
}
|
||||
|
||||
protected function serializeMethods(array $methods) : string
|
||||
{
|
||||
$methods = array_map('strtolower', $methods);
|
||||
|
||||
sort($methods);
|
||||
|
||||
return implode(':', $methods);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Lean\Routing;
|
||||
|
||||
interface RouteDefinitionInterface {}
|
||||
@@ -0,0 +1,11 @@
|
||||
{% arguments array $contextList %}
|
||||
|
||||
{% if $contextList %}
|
||||
<div class="message-wrapper">{% php
|
||||
foreach($contextList as $name => $context) {
|
||||
foreach($context->formContextMessages ?? [] as $message) {
|
||||
echo $message->render();
|
||||
}
|
||||
}
|
||||
%}</div>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,23 @@
|
||||
{% arguments \DateInterval $interval %}
|
||||
|
||||
{% if $interval->y %}
|
||||
{% lang "lean.date.interval.years", [ 'years' => $interval->y ] %},
|
||||
{% endif %}
|
||||
|
||||
{% if $interval->m %}
|
||||
{% lang "lean.date.interval.months", [ 'months' => $interval->m ] %},
|
||||
{% endif %}
|
||||
|
||||
{% if $interval->d %}
|
||||
{% lang "lean.date.interval.days", [ 'days' => $interval->d ] %},
|
||||
{% endif %}
|
||||
|
||||
{% if $interval->h %}
|
||||
{% lang "lean.date.interval.hours", [ 'hours' => $interval->h ] %},
|
||||
{% endif %}
|
||||
|
||||
{% if $interval->i %}
|
||||
{% lang "lean.date.interval.minutes", [ 'minutes' => $interval->i ] %}
|
||||
{% else %}
|
||||
{% lang "lean.date.interval.seconds", [ 'seconds' => $interval->s ] %}
|
||||
{% endif %}
|
||||
@@ -9,7 +9,7 @@
|
||||
<div class="title">{% _ "title" %}</div>
|
||||
<div class="subtitle">{% _ "subtitle" %}</div>
|
||||
<div class="content">{% _ "message" %}</div>
|
||||
<u><a href="#" onclick="history.back()">{% _ "back" %}</a></u>
|
||||
<u><a href="{% url %}">{% _ "back" %}</a></u>
|
||||
</div>
|
||||
{% endsection %}
|
||||
|
||||
@@ -22,5 +22,5 @@
|
||||
{% section "head.css" %}
|
||||
.title {font-size:2rem}
|
||||
.subtitle {font-size:1.25rem; padding-top: 1rem;}
|
||||
.content {padding-top:1rem}
|
||||
.content {background: #f7f7f7;padding: 7px;color: #8c3c3c;max-height:300px;overflow-y:auto}
|
||||
{% endsection %}
|
||||
@@ -0,0 +1,26 @@
|
||||
{% extends "lean/layout/error" %}
|
||||
|
||||
{% language.set "lean.error.401" %}
|
||||
|
||||
{% title _('page-title') %}
|
||||
|
||||
{% section "content-right" %}
|
||||
<div>
|
||||
<div class="title">{% _ "title" %}</div>
|
||||
<div class="subtitle">{% _ "subtitle" %}</div>
|
||||
<div class="content">{% _ "message" %}</div>
|
||||
<u><a href="#" onclick="history.back()">{% _ "back" %}</a></u>
|
||||
</div>
|
||||
{% endsection %}
|
||||
|
||||
{% section "content-left" %}
|
||||
<div class="picto-login">
|
||||
{% view "lean/picto/undraw_forbidden" %}
|
||||
</div>
|
||||
{% endsection %}
|
||||
|
||||
{% section "head.css" %}
|
||||
.title {font-size:2rem}
|
||||
.subtitle {font-size:1.25rem; padding-top: 1rem;}
|
||||
.content {padding-top:1rem}
|
||||
{% endsection %}
|
||||
@@ -18,9 +18,3 @@
|
||||
{% view "lean/picto/undraw_lost" %}
|
||||
</div>
|
||||
{% endsection %}
|
||||
|
||||
{% section "head.css" %}
|
||||
.title {font-size:2rem}
|
||||
.subtitle {font-size:1.25rem; padding-top: 1rem;}
|
||||
.content {padding-top:1rem}
|
||||
{% endsection %}
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
{% section "content-right" %}
|
||||
<div>
|
||||
<div class="title">{% _ "title" %}</div>
|
||||
<div class="title">{{ $title ?? title() }}</div>
|
||||
<div class="subtitle">{{= isset($subtitle) ? nl2br($subtitle) : _("subtitle") }}</div>
|
||||
<div class="content">{{= isset($message) ? nl2br($message) : _("message") }}</div>
|
||||
<u><a href="#" onclick="history.back()">{% _ "back" %}</a></u>
|
||||
@@ -14,13 +14,7 @@
|
||||
{% endsection %}
|
||||
|
||||
{% section "content-left" %}
|
||||
<div class="picto-login">
|
||||
{% view "lean/picto/undraw_error" %}
|
||||
</div>
|
||||
{% endsection %}
|
||||
|
||||
{% section "head.css" %}
|
||||
.title {font-size:2rem}
|
||||
.subtitle {font-size:1.25rem; padding-top: 1rem;}
|
||||
.content {padding-top:1rem}
|
||||
{% endsection %}
|
||||
<div class="picto-login">
|
||||
{% view "lean/picto/undraw_error" %}
|
||||
</div>
|
||||
{% endsection %}
|
||||
@@ -0,0 +1,53 @@
|
||||
{% title "Documentation" %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
{% section "head" %}
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>{% section "head.title" %}{{ title() }}{% endsection %}</title>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% asset 'static/favicon/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% asset 'static/favicon/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% asset 'static/favicon/favicon-16x16.png' %}">
|
||||
<link rel="manifest" href="{% asset 'static/favicon/site.webmanifest' %}">
|
||||
<link rel="mask-icon" href="{% asset 'static/favicon/safari-pinned-tab.svg' %}" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#2d89ef">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<style>
|
||||
a{color:#e44a00}h3{background:#e1e1e1;padding:6px 12px}ul{background:#e9e9fd;padding-top:20px;padding-bottom:20px;border:1px solid #e3e3ec}ul li + li{margin-top:8px}li > em{font-size:0.75rem;color:gray}body{font-family:Helvetica, 'Helvetica Neuve', Arial, Tahoma, sans-serif;font-size:17px;color:#333}h1,h2,h3,h4,h5,h6{color:#222;margin:0 0 20px}dl,ol,p,pre,table,ul{margin:0 0 20px}h1,h2,h3{line-height:1.1}h1{font-size:20px;text-align:right;color:#387eea;font-weight:bold}h2{color:#393939}h3,h4,h5,h6{color:#494949}h3{display:flex}h3 > code{margin-right:5px;color:#b52dac}h3 > strong{margin-left:auto}a{color:#39c;font-weight:400;text-decoration:none}a small{font-size:11px;color:#777;margin-top:-0.6em;display:block}.wrapper{width:860px;margin:0 auto}blockquote{border-left:1px solid #e5e5e5;margin:0;padding:0 0 0 20px;font-style:italic}code,pre{font-size:12px}pre{padding:8px 15px;background:#f8f8f8;border-radius:5px;border:1px solid #e5e5e5;overflow-x:auto}table{width:100%;border-collapse:collapse}td,th{text-align:left;padding:5px 10px;border-bottom:1px solid #e5e5e5}dt{color:#444;font-weight:700}th{color:#444}img{max-width:100%}header{width:270px;float:left;position:fixed}header ul{list-style:none;height:40px;padding:0;background:#eee;background:-moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%);background:-webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd));background:-webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:-o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:-ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%);background:linear-gradient(top, #f8f8f8 0%,#dddddd 100%);border-radius:5px;border:1px solid #d2d2d2;box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0;width:270px}header li{width:89px;float:left;border-right:1px solid #d2d2d2;height:40px}header ul a{line-height:1;font-size:11px;color:#999;display:block;text-align:center;padding-top:6px;height:40px}strong{color:#222;font-weight:700}header ul li + li{width:88px;border-left:1px solid #fff}header ul li + li + li{border-right:none;width:89px}header ul a strong{font-size:14px;display:block;color:#222}section{width:500px;float:right;padding-bottom:50px}small{font-size:11px}hr{border:0;background:#e5e5e5;height:1px;margin:0 0 20px}footer{width:270px;float:left;position:fixed;bottom:50px}@media print, screen and (max-width: 960px){div.wrapper{width:auto;margin:0}footer,header,section{float:none;position:static;width:auto}header{padding-right:320px}section{border:1px solid #e5e5e5;border-width:1px 0;padding:20px 0;margin:0 0 20px}header a small{display:inline}header ul{position:absolute;right:50px;top:52px}}@media print, screen and (max-width: 720px){body{word-wrap:break-word}header{padding:0}header p.view,header ul{position:static}code,pre{word-wrap:normal}}@media print, screen and (max-width: 480px){body{padding:15px}header ul{display:none}}@media print{body{padding:0.4in;font-size:12pt;color:#444}}#wrapper{margin-left:auto;margin-right:auto;background-color:white}.ca-menu{list-style:none;padding:0;margin:20px auto}#navi{padding-top:15px;padding-right:15px;float:right;width:420px}#title{padding-left:15px;width:460px;float:left}div.clear{clear:both}h2{font-size:2em}h3{font-size:1.5em}h4{font-size:1.2em}h5{font-size:1em;font-weight:bold}h6{font-size:1em;font-weight:bold}h1,h2,h3,h4,h5,h6{font-weight:normal;line-height:2.5rem;margin:1rem 0}.post p{max-width:580px}ol.list,ul.list{padding-left:3.333em;max-width:580px}.post h2{border-bottom:1px solid #EDEDED}h1:nth-child(1),h2:nth-child(1),h3:nth-child(1),h4:nth-child(1),h5:nth-child(1),h6:nth-child(1){margin-top:0}body{padding:1em}#wrapper{padding:1em}@media (min-width: 43.75em){body{padding:2em}#wrapper{padding:2em}}@media (min-width: 62em){body{padding:3em}#wrapper{max-width:740px;padding:3em}}
|
||||
ol{background: #eff4f2;padding-top:20px;padding-bottom:20px;border:1px solid #e3e3ec}
|
||||
h4{background:#e0f7ed;padding:6px 12px; font-weight: bold!important;font-size:100%;margin-top:0}
|
||||
h5{text-decoration: underline}
|
||||
li.odd-even{border-top:1px solid #ccc;margin:10px 0;padding:15px 15px 10px 5px}
|
||||
li.odd-even:first-child{border:0}
|
||||
input, button {padding:5px; font-size:1em;margin-top:10px}
|
||||
ul ul {margin: 0;border: 0;padding: 5px 30px;}
|
||||
</style>
|
||||
</head>
|
||||
{% endsection %}
|
||||
|
||||
{% section "body" %}
|
||||
<body>
|
||||
{% section "body.header" %}{% endsection %}
|
||||
|
||||
{% section "header" %}{% endsection %}
|
||||
|
||||
{% section "message" %}
|
||||
{% view "lean/widget/message" %}
|
||||
{% endsection %}
|
||||
|
||||
<main role="main" class="content">
|
||||
{% section "main" %}
|
||||
<div>{{= $markdown }}</div>
|
||||
{% endsection %}
|
||||
</main>
|
||||
|
||||
<footer>{% section "footer" %}{% endsection %}</footer>
|
||||
|
||||
{% section "body.footer" %}{% endsection %}
|
||||
</body>
|
||||
{% endsection %}
|
||||
</html>
|
||||
@@ -2,25 +2,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html; ">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<title>{{ title() }}</title>
|
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato|Ubuntu%20Mono">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css">
|
||||
<link rel="stylesheet" href='{% asset "asset/css/bulma.extension.css" %}'>
|
||||
<link rel="stylesheet" href="https://cdn.eckinox.net/fontawesome/latest/css/fontawesome-all.min.css">
|
||||
|
||||
|
||||
<style>
|
||||
body {background:#272822}
|
||||
body {background:#272822;min-height:100vh;}
|
||||
#main-content {min-height: 100vh;width: 100vw;align-items:center;justify-content: center;}
|
||||
#wrapper-content {max-width:960px; max-height:65vh;flex-direction:row-reverse; margin:0 10vw}
|
||||
#wrapper-content .content-right {background:#efefef;padding:5vh 2vw;align-items:center;display:flex;justify-content:center; width:100%;border-radius:6px 0 0 6px}
|
||||
#wrapper-content .content-left {background:#ffffff;padding:5vh 2vw;border-radius:0 6px 6px 0;display:flex;align-items:center;justify-content: center}
|
||||
|
||||
.form-user-login {width:80%;}
|
||||
.picto-login svg {max-width:100%;}
|
||||
.picto-login svg {max-width:100%;width:100%;max-height:33vh}
|
||||
|
||||
{% section "head.css" %}{% endsection %}
|
||||
</style>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<svg id="e7dedc5b-fca6-4313-b5a0-e6297481e8ae" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1022.7" height="785.81" viewBox="0 0 1022.7 785.81"><defs><linearGradient id="af83dc26-9572-4816-b7a1-1af4f72ff554" x1="678.2" y1="821.79" x2="678.2" y2="493.4" gradientTransform="translate(-20.24 29.65) rotate(-2.31)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="gray" stop-opacity="0.25"/><stop offset="0.54" stop-color="gray" stop-opacity="0.12"/><stop offset="1" stop-color="gray" stop-opacity="0.1"/></linearGradient></defs><title>bug fixing</title><ellipse cx="468.63" cy="660.88" rx="425" ry="33" fill="#6c63ff" opacity="0.1"/><g opacity="0.1"><path d="M933.7,529.93c-2.54-7.71-12.84-11.26-23-7.92a24.76,24.76,0,0,0-4.23,1.83c-.65-.18-1.32-.34-2-.46a22.42,22.42,0,0,0,.63-6.79A24.6,24.6,0,0,0,916,495.12a24.63,24.63,0,0,0,10.86-21.47,23.76,23.76,0,0,0,8.23-9.32c4.9-9.7,2.87-20.6-4.54-24.35s-17.4,1.08-22.3,10.78a23.69,23.69,0,0,0-2.63,12.15,24.63,24.63,0,0,0-10.86,21.47,24.63,24.63,0,0,0-10.86,21.47A24.64,24.64,0,0,0,873,527.33a23.76,23.76,0,0,0-8.23,9.32,25.46,25.46,0,0,0-2.08,5.74,21.18,21.18,0,0,0-4.44-4.73,25.38,25.38,0,0,0-1-4.5c-3.34-10.17-12.3-16.37-20-13.83s-11.26,12.83-7.92,23a23.07,23.07,0,0,0,7.56,11.08,25.38,25.38,0,0,0,1,4.5c1.94,5.9,5.77,10.46,10.15,12.75a23.23,23.23,0,0,0-.83,3.9,25.29,25.29,0,0,0-7.54,14.91,25.27,25.27,0,0,0-7.54,14.92,24.63,24.63,0,0,0-5,6.81c-4.91,9.7-2.88,20.61,4.53,24.35s17.4-1.07,22.31-10.78a24.62,24.62,0,0,0,2.5-8.09,25.29,25.29,0,0,0,7.54-14.91,25.27,25.27,0,0,0,7.54-14.92A25.21,25.21,0,0,0,879,571.94,25.21,25.21,0,0,0,886.56,557a24.11,24.11,0,0,0,3.39-4,23.46,23.46,0,0,0,12.27-.77,24.76,24.76,0,0,0,4.23-1.83,23.2,23.2,0,0,0,13.42-.48C930,546.61,936.23,537.65,933.7,529.93Z" transform="translate(-88.65 -57.09)" fill="#3f3d56"/><g opacity="0.1"><path d="M915.77,499.53a21.5,21.5,0,0,1-3.44,2.75,23,23,0,0,1-1.11,8.43,26.18,26.18,0,0,0,2.1-3.43A24.64,24.64,0,0,0,915.77,499.53Z" transform="translate(-88.65 -57.09)"/><path d="M926.63,478.06a21.5,21.5,0,0,1-3.44,2.75,23,23,0,0,1-1.11,8.43,25.65,25.65,0,0,0,2.1-3.44A24.85,24.85,0,0,0,926.63,478.06Z" transform="translate(-88.65 -57.09)"/><path d="M902,470.07a21.91,21.91,0,0,1,.17-4.41,24.93,24.93,0,0,0-4.78,6.57,25.63,25.63,0,0,0-1.52,3.73A22.75,22.75,0,0,1,902,470.07Z" transform="translate(-88.65 -57.09)"/><path d="M833.07,600a23.59,23.59,0,0,1,2.95-3.38,22.92,22.92,0,0,1,.87-4,25.09,25.09,0,0,0-2.29,3.71A25.59,25.59,0,0,0,833.07,600Z" transform="translate(-88.65 -57.09)"/><path d="M840.61,585.1a23.59,23.59,0,0,1,2.95-3.38,23,23,0,0,1,.83-3.9l0,0a25.68,25.68,0,0,0-2.2,3.59A26.76,26.76,0,0,0,840.61,585.1Z" transform="translate(-88.65 -57.09)"/><path d="M904.9,521a20.82,20.82,0,0,1-3.43,2.74,22.45,22.45,0,0,1-.63,6.79,18.75,18.75,0,0,1,2,.46,24.76,24.76,0,0,1,4.23-1.83c10.18-3.34,20.47.21,23,7.92a11.68,11.68,0,0,1,.32,6c3.35-3.94,4.74-8.74,3.3-13.14-2.54-7.71-12.84-11.26-23-7.92a24.76,24.76,0,0,0-4.23,1.83c-.65-.18-1.32-.34-2-.46C904.65,522.59,904.79,521.8,904.9,521Z" transform="translate(-88.65 -57.09)"/><path d="M829.35,607.49a25.09,25.09,0,0,0-2.29,3.71,26.56,26.56,0,0,0-1.54,3.74,24.48,24.48,0,0,1,3-3.4A22.92,22.92,0,0,1,829.35,607.49Z" transform="translate(-88.65 -57.09)"/><path d="M891.09,491.54a22,22,0,0,1,.17-4.41,24.93,24.93,0,0,0-4.78,6.57,25.63,25.63,0,0,0-1.52,3.73A22.75,22.75,0,0,1,891.09,491.54Z" transform="translate(-88.65 -57.09)"/><path d="M875.4,579.1a24.29,24.29,0,0,1-1,4.38,26.67,26.67,0,0,0,2.09-3.44,25.49,25.49,0,0,0,1.63-4A24.23,24.23,0,0,1,875.4,579.1Z" transform="translate(-88.65 -57.09)"/><path d="M860.32,608.92a24.15,24.15,0,0,1-1,4.38,25.65,25.65,0,0,0,2.1-3.44,26.47,26.47,0,0,0,1.63-4A23.57,23.57,0,0,1,860.32,608.92Z" transform="translate(-88.65 -57.09)"/><path d="M867.86,594a24.11,24.11,0,0,1-1,4.38A26.67,26.67,0,0,0,869,595a25.49,25.49,0,0,0,1.63-4A24.23,24.23,0,0,1,867.86,594Z" transform="translate(-88.65 -57.09)"/><path d="M882.94,564.19a24,24,0,0,1-1,4.37,25.89,25.89,0,0,0,2.09-3.43,25.48,25.48,0,0,0,1.64-4.07A23.22,23.22,0,0,1,882.94,564.19Z" transform="translate(-88.65 -57.09)"/><path d="M833.57,526.48c7.72-2.53,16.68,3.66,20,13.83a25.68,25.68,0,0,1,1,4.51,21.18,21.18,0,0,1,4.44,4.73,25.53,25.53,0,0,1,2.08-5.75c.35-.68.73-1.33,1.12-2a21.07,21.07,0,0,0-4-4.18,25.38,25.38,0,0,0-1-4.5c-3.34-10.17-12.3-16.37-20-13.83-4.4,1.44-7.44,5.41-8.62,10.43A11.69,11.69,0,0,1,833.57,526.48Z" transform="translate(-88.65 -57.09)"/><path d="M869.37,534.48a21.85,21.85,0,0,1,.18-4.4,24.73,24.73,0,0,0-4.79,6.57,25.07,25.07,0,0,0-1.52,3.72A23,23,0,0,1,869.37,534.48Z" transform="translate(-88.65 -57.09)"/><path d="M880.23,513a21.85,21.85,0,0,1,.18-4.4,24.52,24.52,0,0,0-4.79,6.56,25.63,25.63,0,0,0-1.52,3.73A22.75,22.75,0,0,1,880.23,513Z" transform="translate(-88.65 -57.09)"/><path d="M852.78,623.83a24.25,24.25,0,0,1-1,4.39,25.73,25.73,0,0,0,3.73-7.49A23.57,23.57,0,0,1,852.78,623.83Z" transform="translate(-88.65 -57.09)"/></g><path d="M948.52,430.71a23.68,23Line truncated
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="799.031" height="618.112" viewBox="0 0 799.031 618.112" xmlns:xlink="http://www.w3.org/1999/xlink" role="img" artist="Katerina Limpitsouni" source="https://undraw.co/"><g transform="translate(-893 -197)"><path d="M15.18,488.763c0,.872.478,1.573,1.073,1.573h535.1c.6,0,1.073-.7,1.073-1.573s-.478-1.573-1.073-1.573H16.253C15.658,487.191,15.18,487.891,15.18,488.763Z" transform="translate(1007.711 324.776)" fill="#ccc"/><rect width="19.105" height="3.371" transform="translate(1198.162 808.354)" fill="#b6b3c5"/><rect width="19.105" height="3.371" transform="translate(1367.295 808.917)" fill="#b6b3c5"/><path d="M352.955,370.945a27.529,27.529,0,0,1-54.321,0H229.146V521.536h193.3V370.945Z" transform="translate(966.721 287.378)" fill="#d6d6e3"/><rect width="193.296" height="5.242" transform="translate(1196.43 796.983)" fill="#090814"/><path d="M788.255,487.17H10.776A10.788,10.788,0,0,1,0,476.394V32.688A10.788,10.788,0,0,1,10.776,21.911H788.255a10.789,10.789,0,0,1,10.776,10.776V476.394a10.789,10.789,0,0,1-10.776,10.776Z" transform="translate(893 175.089)" fill="#090814"/><rect width="760.822" height="429.297" transform="translate(911.104 213.968)" fill="#fff"/><g transform="translate(20.477 16.308)"><path d="M604.463,379.271H317.442a8.655,8.655,0,0,1-8.645-8.645V273.8a8.655,8.655,0,0,1,8.645-8.645H604.463a8.655,8.655,0,0,1,8.645,8.645v96.826a8.655,8.655,0,0,1-8.645,8.645Z" transform="translate(811.648 85.826)" fill="#6c63ff"/><rect width="76.078" height="8.645" rx="2" transform="translate(1165.4 380.374)" fill="#fff"/><ellipse cx="5.187" cy="5.187" rx="5.187" ry="5.187" transform="translate(1336.576 380.374)" fill="#090814"/><ellipse cx="5.187" cy="5.187" rx="5.187" ry="5.187" transform="translate(1353.865 380.374)" fill="#090814"/><ellipse cx="5.187" cy="5.187" rx="5.187" ry="5.187" transform="translate(1371.156 380.374)" fill="#090814"/></g><ellipse cx="40.952" cy="40.952" rx="40.952" ry="40.952" transform="translate(1404.281 440.452)" fill="#090814"/><path d="M10.863-57.7l-.524-29.6h8.246l-.554,29.6Zm3.613,14.307a4.7,4.7,0,0,1-3.409-1.3,4.368,4.368,0,0,1-1.34-3.278,4.39,4.39,0,0,1,1.34-3.322,4.732,4.732,0,0,1,3.409-1.282,4.732,4.732,0,0,1,3.409,1.282,4.39,4.39,0,0,1,1.34,3.322,4.368,4.368,0,0,1-1.34,3.278A4.7,4.7,0,0,1,14.476-43.394Z" transform="translate(1430.76 546.754)" fill="#fff"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 2.3 KiB |
@@ -1 +1 @@
|
||||
<svg id="a63b68c9-40cd-4f97-89ed-e7d494eb42f9" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="742.41705" height="712.57302" viewBox="0 0 742.41705 712.57302"><path id="aa38b2e7-2ea9-4025-adcf-83f87c0cfc4a" data-name="Path 968" d="M853.60644,262.60547h-3.9v-106.977a61.915,61.915,0,0,0-61.915-61.915h-226.65a61.915,61.915,0,0,0-61.916,61.914v586.884a61.915,61.915,0,0,0,61.915,61.915h226.648a61.915,61.915,0,0,0,61.915-61.915v-403.758h3.9Z" transform="translate(-228.79147 -93.71349)" fill="#3f3d56"/><path id="f27259c2-0089-4137-bfd8-cb22dff914f1" data-name="Path 969" d="M837.00647,151.48149v595.175a46.959,46.959,0,0,1-46.942,46.952h-231.3a46.966,46.966,0,0,1-46.973-46.952v-595.175a46.965,46.965,0,0,1,46.971-46.951h28.058a22.329,22.329,0,0,0,20.656,30.74h131.868a22.329,22.329,0,0,0,20.656-30.74h30.055a46.959,46.959,0,0,1,46.951,46.942Z" transform="translate(-228.79147 -93.71349)" fill="#fff"/><circle id="fc31cc31-f989-45cc-bd1e-521a0ee1871b" data-name="Ellipse 18" cx="445.56497" cy="200.129" r="96.565" fill="#6c63ff"/><path id="a573942c-3bf3-4c43-b474-689725a927f7" data-name="Path 39" d="M779.67245,494.30947h-205.537a3.81,3.81,0,0,1-3.806-3.806V439.51949a3.811,3.811,0,0,1,3.806-3.806h205.537a3.811,3.811,0,0,1,3.806,3.806v50.985a3.811,3.811,0,0,1-3.806,3.806Zm-205.537-57.074a2.286,2.286,0,0,0-2.284,2.284v50.985a2.286,2.286,0,0,0,2.284,2.284h205.537a2.286,2.286,0,0,0,2.284-2.284v-50.985a2.286,2.286,0,0,0-2.284-2.284Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="fb5feb1e-1fe2-48fa-806d-0a2b51d12b49" data-name="Path 40" d="M637.69945,454.07448a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.00221-.11713-.0019h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="bcb9932f-0864-4b7e-b0ed-f7254bb9b8bf" data-name="Path 41" d="M637.69945,470.05846a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.00219-.11713-.00189h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="f88e2a8b-becc-4da9-bd36-af011daaaaf2" data-name="Path 42" d="M779.67245,579.28947h-205.537a3.81,3.81,0,0,1-3.806-3.806V524.49949a3.811,3.811,0,0,1,3.806-3.806h205.537a3.811,3.811,0,0,1,3.806,3.806v50.985A3.811,3.811,0,0,1,779.67245,579.28947Zm-205.537-57.074a2.286,2.286,0,0,0-2.284,2.284v50.985a2.286,2.286,0,0,0,2.284,2.284h205.537a2.286,2.286,0,0,0,2.284-2.284v-50.985a2.286,2.286,0,0,0-2.284-2.284Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="e192d34f-2ccc-4f79-b47a-46430e4d87f6" data-name="Path 43" d="M637.69945,539.33047a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.087-5.328h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="b230677c-48cc-4ee6-87d6-a753204ffe47" data-name="Path 44" d="M637.69945,555.3185a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.087-5.328h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="b88a2a79-0ae3-4fd9-9894-02e0db9a2271" data-name="Path 39-2" d="M779.67245,664.54748h-205.537a3.81,3.81,0,0,1-3.806-3.806V609.7575a3.811,3.811,0,0,1,3.806-3.806h205.537a3.811,3.811,0,0,1,3.806,3.806v50.985a3.811,3.811,0,0,1-3.806,3.806Zm-205.537-57.074a2.286,2.286,0,0,0-2.284,2.284v50.985a2.286,2.286,0,0,0,2.284,2.284h205.537a2.286,2.286,0,0,0,2.284-2.284v-50.985a2.286,2.286,0,0,0-2.284-2.284Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="aa2394a9-8b56-4a12-a2cd-a583e6ea12eb" data-name="Path 40-2" d="M637.69945,624.59148a2.66449,2.66449,0,1,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.00219-.11713-.00189h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="bc352cb2-8959-4b51-bd18-df6817a9a47e" data-name="Path 41-2" d="M637.69945,640.57846a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.0022-.11713-.00189h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="b30ee82f-5b3a-4aa4-9db7-ead828191937" data-name="Path 970" d="M969.27042,806.28651h-738.541c-1.071,0-1.938-.468-1.938-1.045s.868-1.045,1.938-1.045H969.27054c1.06994,0,1.938.468,1.938,1.045S970.34146,806.28651,969.27042,806.28651Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><g id="f24cf2fd-107c-4150-a0f1-6f6b12d88bf3" data-name="Group 58"><path id="a2b576eb-bb95-4480-aa60-cd8aa023fe61" data-name="Path 438" d="M937.99248,765.52151a19.4741,19.4741,0,0,1-18.806-3.313c-6.587-5.528-8.652-14.636-10.332-23.07l-4.97-24.945,10.405,7.165c7.483,5.152,15.134,10.47,20.316,17.933s7.443,17.651,3.28,25.726" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="fe1c5433-44e0-47a6-8430-329cbd7c4dea" data-name="Path 439" d="M936.38547,797.45852c1.31-9.542,2.657-19.206,1.738-28.849-.816-8.565-3.429-16.93-8.749-23.789a39.57365,39.57365,0,0,0-10.153-9.2c-1.015-.641-1.95.968-.939,1.606a37.62192,37.62192,0,0,1,14.881,17.956c3.24,8.241,3.76,17.224,3.2,25.977-.338,5.294-1.053,10.553-1.774,15.805a.964.964,0,0,0,.6Line truncated
|
||||
<svg width="742" height="712" id="a63b68c9-40cd-4f97-89ed-e7d494eb42f9" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 742.41705 712.57302"><path id="aa38b2e7-2ea9-4025-adcf-83f87c0cfc4a" data-name="Path 968" d="M853.60644,262.60547h-3.9v-106.977a61.915,61.915,0,0,0-61.915-61.915h-226.65a61.915,61.915,0,0,0-61.916,61.914v586.884a61.915,61.915,0,0,0,61.915,61.915h226.648a61.915,61.915,0,0,0,61.915-61.915v-403.758h3.9Z" transform="translate(-228.79147 -93.71349)" fill="#3f3d56"/><path id="f27259c2-0089-4137-bfd8-cb22dff914f1" data-name="Path 969" d="M837.00647,151.48149v595.175a46.959,46.959,0,0,1-46.942,46.952h-231.3a46.966,46.966,0,0,1-46.973-46.952v-595.175a46.965,46.965,0,0,1,46.971-46.951h28.058a22.329,22.329,0,0,0,20.656,30.74h131.868a22.329,22.329,0,0,0,20.656-30.74h30.055a46.959,46.959,0,0,1,46.951,46.942Z" transform="translate(-228.79147 -93.71349)" fill="#fff"/><circle id="fc31cc31-f989-45cc-bd1e-521a0ee1871b" data-name="Ellipse 18" cx="445.56497" cy="200.129" r="96.565" fill="#6c63ff"/><path id="a573942c-3bf3-4c43-b474-689725a927f7" data-name="Path 39" d="M779.67245,494.30947h-205.537a3.81,3.81,0,0,1-3.806-3.806V439.51949a3.811,3.811,0,0,1,3.806-3.806h205.537a3.811,3.811,0,0,1,3.806,3.806v50.985a3.811,3.811,0,0,1-3.806,3.806Zm-205.537-57.074a2.286,2.286,0,0,0-2.284,2.284v50.985a2.286,2.286,0,0,0,2.284,2.284h205.537a2.286,2.286,0,0,0,2.284-2.284v-50.985a2.286,2.286,0,0,0-2.284-2.284Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="fb5feb1e-1fe2-48fa-806d-0a2b51d12b49" data-name="Path 40" d="M637.69945,454.07448a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.00221-.11713-.0019h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="bcb9932f-0864-4b7e-b0ed-f7254bb9b8bf" data-name="Path 41" d="M637.69945,470.05846a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.00219-.11713-.00189h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="f88e2a8b-becc-4da9-bd36-af011daaaaf2" data-name="Path 42" d="M779.67245,579.28947h-205.537a3.81,3.81,0,0,1-3.806-3.806V524.49949a3.811,3.811,0,0,1,3.806-3.806h205.537a3.811,3.811,0,0,1,3.806,3.806v50.985A3.811,3.811,0,0,1,779.67245,579.28947Zm-205.537-57.074a2.286,2.286,0,0,0-2.284,2.284v50.985a2.286,2.286,0,0,0,2.284,2.284h205.537a2.286,2.286,0,0,0,2.284-2.284v-50.985a2.286,2.286,0,0,0-2.284-2.284Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="e192d34f-2ccc-4f79-b47a-46430e4d87f6" data-name="Path 43" d="M637.69945,539.33047a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.087-5.328h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="b230677c-48cc-4ee6-87d6-a753204ffe47" data-name="Path 44" d="M637.69945,555.3185a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.087-5.328h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="b88a2a79-0ae3-4fd9-9894-02e0db9a2271" data-name="Path 39-2" d="M779.67245,664.54748h-205.537a3.81,3.81,0,0,1-3.806-3.806V609.7575a3.811,3.811,0,0,1,3.806-3.806h205.537a3.811,3.811,0,0,1,3.806,3.806v50.985a3.811,3.811,0,0,1-3.806,3.806Zm-205.537-57.074a2.286,2.286,0,0,0-2.284,2.284v50.985a2.286,2.286,0,0,0,2.284,2.284h205.537a2.286,2.286,0,0,0,2.284-2.284v-50.985a2.286,2.286,0,0,0-2.284-2.284Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="aa2394a9-8b56-4a12-a2cd-a583e6ea12eb" data-name="Path 40-2" d="M637.69945,624.59148a2.66449,2.66449,0,1,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.00219-.11713-.00189h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="bc352cb2-8959-4b51-bd18-df6817a9a47e" data-name="Path 41-2" d="M637.69945,640.57846a2.66449,2.66449,0,0,0,0,5.329h125.605a2.665,2.665,0,0,0,.2041-5.32611q-.0585-.0022-.11713-.00189h-125.692Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="b30ee82f-5b3a-4aa4-9db7-ead828191937" data-name="Path 970" d="M969.27042,806.28651h-738.541c-1.071,0-1.938-.468-1.938-1.045s.868-1.045,1.938-1.045H969.27054c1.06994,0,1.938.468,1.938,1.045S970.34146,806.28651,969.27042,806.28651Z" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><g id="f24cf2fd-107c-4150-a0f1-6f6b12d88bf3" data-name="Group 58"><path id="a2b576eb-bb95-4480-aa60-cd8aa023fe61" data-name="Path 438" d="M937.99248,765.52151a19.4741,19.4741,0,0,1-18.806-3.313c-6.587-5.528-8.652-14.636-10.332-23.07l-4.97-24.945,10.405,7.165c7.483,5.152,15.134,10.47,20.316,17.933s7.443,17.651,3.28,25.726" transform="translate(-228.79147 -93.71349)" fill="#e6e6e6"/><path id="fe1c5433-44e0-47a6-8430-329cbd7c4dea" data-name="Path 439" d="M936.38547,797.45852c1.31-9.542,2.657-19.206,1.738-28.849-.816-8.565-3.429-16.93-8.749-23.789a39.57365,39.57365,0,0,0-10.153-9.2c-1.015-.641-1.95.968-.939,1.606a37.62192,37.62192,0,0,1,14.881,17.956c3.24,8.241,3.76,17.224,3.2,25.977-.338,5.294-1.053,10.553-1.774,15.805a.964.964,0,0,0,.65,1.144.936.Line truncated
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,28 +1,33 @@
|
||||
<ol class="breadcrumb ">
|
||||
<li class="breadcrumb-item"><a href="{% url '' %}"><i class="fas fa-fw fa-tachometer-alt"></i> Tableau de bord</a></li>
|
||||
{% if isset($this->breadcrumb) %}
|
||||
{% php $routeTree = $this->breadcrumb->getRouteTree($this->session->get('lean.route')) %}
|
||||
|
||||
{% foreach $this->breadcrumb ?? [] as $crumb %}
|
||||
<li class="breadcrumb-item {{ ( $crumb['active'] ?? false ) ? 'active' : '' }}">
|
||||
{% if ! ($crumb['active'] ?? false) %}
|
||||
{? $last = $crumb ?}
|
||||
{% if count($routeTree) %}
|
||||
<ol class="breadcrumb mb-4 px-3 py-2">
|
||||
{% foreach $routeTree as $index => $crumb %}
|
||||
<li class="breadcrumb-item {% if $crumb->current %}active{% endif %}">
|
||||
{% if ! $crumb->current %}
|
||||
{% php $last = $crumb %}
|
||||
|
||||
<a href="{% url $crumb['url'] %}">
|
||||
{% if $crumb['icon'] ?? false %}
|
||||
<i class="fa fa-{{ $crumb['icon'] }}"></i>
|
||||
{% endif %}
|
||||
<a href="{% route $crumb->routeAnnotation->name %}">
|
||||
{% if $crumb->icon ?? false %}
|
||||
<i class="fa fa-{{ $crumb->icon }}"></i>
|
||||
{% endif %}
|
||||
|
||||
{{ $crumb['title'] }}
|
||||
</a>
|
||||
{% else %}
|
||||
{% if $crumb['icon'] ?? false %}
|
||||
<i class="fa fa-{{ $crumb['icon'] }}"></i>
|
||||
{% endif %}
|
||||
<span>{{ $crumb['title'] }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endforeach %}
|
||||
<span>{{ lang($crumb->lang, get_defined_vars()) }}</span>
|
||||
</a>
|
||||
{% else %}
|
||||
{% if $crumb->icon ?? false %}
|
||||
<i class="fa fa-{{ $crumb->icon }}"></i>
|
||||
{% endif %}
|
||||
|
||||
{% if $last ?? false %}
|
||||
<li class="breadcrumb-last-item ml-auto"><a href="{{ url( $last['url']) }}"><i class="fas fa-chevron-circle-left"></i> Retour</a></li>
|
||||
<span>{{ lang($crumb->lang, get_defined_vars()) }}</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endforeach %}
|
||||
|
||||
{# if $last ?? false %}
|
||||
<li class="breadcrumb-last-item ml-auto"><a href="{{ ( $last['route'] ?? false ) ? $last['route'] : url($last['url']) }}"><i class="fas fa-chevron-circle-left"></i> Retour</a></li>
|
||||
{% endif #}
|
||||
</ol>
|
||||
{% endif %}
|
||||
</ol>
|
||||
{% endif %}
|
||||
@@ -0,0 +1,67 @@
|
||||
{% use
|
||||
Ulmus\Entity\Field\Date
|
||||
%}
|
||||
|
||||
{% arguments int $month, int $year %}
|
||||
|
||||
{% define "week.heading", int $day, int $month, int $year %}
|
||||
{% define "day.content", \DateTime $date, bool $today %}
|
||||
{% define "day.empty", int $day, int $month, int $year %}
|
||||
|
||||
{% php
|
||||
$month = ltrim($month, "0");
|
||||
$datetime = ( new Date( "{$year}-{$month}-1" ) );
|
||||
$index = $datetime->format('w');
|
||||
$dayCount = $datetime->format('t');
|
||||
$currentDay = 0;
|
||||
%}
|
||||
|
||||
<div class="calendar-wrapper">
|
||||
<div class='week-heading'>
|
||||
{% foreach range(1, 7) as $day %}
|
||||
{% slot "week.heading", $day, $month, $year %}
|
||||
<div class="day-box">
|
||||
{# Random calendar date that starts on a sunday #}
|
||||
{{ new Date("2019-12-$day")->formatLocaleIntl(\IntlDateFormatter::SHORT, \IntlDateFormatter::NONE, 'EEE') }}
|
||||
</div>
|
||||
{% endslot %}
|
||||
{% endforeach %}
|
||||
</div>
|
||||
|
||||
{% foreach range(0, 5) as $week %}
|
||||
<div class="week">
|
||||
{% foreach range(0, 6) as $day %}
|
||||
{% php $dayType = in_array($day, [0, 6]) ? 'week-end' : 'week-day' %}
|
||||
|
||||
{% if ( $week * 6 + $day >= $index ) && ( $currentDay < $dayCount ) %}
|
||||
{% php $currentDay++; %}
|
||||
|
||||
{% slot "day.content", new \DateTime("$year-$month-$currentDay"), date("$year-$month-$currentDay") === date('Y-n-j') %}
|
||||
<div class="day-box {{ $today ? 'today' : '' }} {{ $dayType }}">{{ $date->format('d') }}</div>
|
||||
{% endslot %}
|
||||
{% else %}
|
||||
{% slot "day.empty", $day, $month, $year %}
|
||||
<div class="day-box {{ $dayType }}"></div>
|
||||
{% endslot %}
|
||||
{% endif %}
|
||||
{% endforeach %}
|
||||
</div>
|
||||
|
||||
{% if $currentDay >= $dayCount %}
|
||||
{% break %}
|
||||
{% endif %}
|
||||
{% endforeach %}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.legend {margin:6px 0 0 0; padding:5px 8px;background:#f9f2f2}
|
||||
.calendar-wrapper {border:1px solid #e3e6f0}
|
||||
.calendar-wrapper .day-box {position:relative;width:14.29%;border:2px solid #fff;height:60px;display:flex;flex-direction:column;justify-content:center;padding:6px 0; text-align:center}
|
||||
.calendar-wrapper .day-box.today {border:2px dashed #4e73df;}
|
||||
.calendar-wrapper .bg-conge,
|
||||
.calendar-wrapper .bg-pedagogique {position:absolute;top:0;bottom:0;left:0;right:0;opacity:0.5;pointer-events:none;}
|
||||
.calendar-wrapper .week,
|
||||
.calendar-wrapper .week-heading {display:flex;}
|
||||
.week-heading {background:#eaecf4;font-weight:bold;}
|
||||
.week:nth-child(even) {background:#f9f2f2; }
|
||||
</style>
|
||||
@@ -1,11 +1,8 @@
|
||||
{% php $messageList = array_filter(array_merge($this->contextList, [ $context ?? null ])) %}
|
||||
{% php
|
||||
$messageList = $this->contextList;
|
||||
$context ??= false;
|
||||
|
||||
{% if $messageList %}
|
||||
<div class="message-wrapper">{% php
|
||||
foreach($messageList as $name => $context) {
|
||||
foreach($context->messages ?? [] as $message) {
|
||||
echo $message->render();
|
||||
}
|
||||
}
|
||||
%}</div>
|
||||
{% endif %}
|
||||
if ($context && ! in_array($context, $messageList)) {
|
||||
$messageList[] = $context;
|
||||
}
|
||||
%}{% block "lean/block/message", $messageList %}{% endblock %}
|
||||
@@ -6,9 +6,11 @@
|
||||
|
||||
<li class="page-item {{ $page === 1 ? 'active' : '' }}"><a class="page-link" href="{% url.parameters $url, [ 'page' => 1 ] + get() %}">1</a></li>
|
||||
|
||||
{% foreach range(2, $pageCount < $maxPage ? $pageCount : $maxPage) as $pageIndex %}
|
||||
<li class="page-item {{ $page === $pageIndex ? 'active' : '' }}"><a class="page-link" href="{% url.parameters $url, [ 'page' => $pageIndex ] + get() %}">{{ $pageIndex }}</a></li>
|
||||
{% endforeach %}
|
||||
{% if $pageCount > 1%}
|
||||
{% foreach range(2, $pageCount < $maxPage ? $pageCount : $maxPage) as $pageIndex %}
|
||||
<li class="page-item {{ $page === $pageIndex ? 'active' : '' }}"><a class="page-link" href="{% url.parameters $url, [ 'page' => $pageIndex ] + get() %}">{{ $pageIndex }}</a></li>
|
||||
{% endforeach %}
|
||||
{% endif %}
|
||||
|
||||
<li class="page-item {{ $page === $pageCount ? 'disabled' : '' }}"><a class="page-link" href="{% url.parameters $url, [ 'page' => $page + 1 ] + get() %}">{% lang "lean.widget.pagination.next" %}</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<style>
|
||||
ui-popup {width:80vw; max-width:520px; box-shadow:0px 0px 0px 9999px rgba(0, 0, 0, 0.5);z-index:9999;transition:all 0.3s ease-out;background:#fff;position:fixed!important;top:0;left: 50%;transform:translate(-50%, 5%);max-height:calc(100vh - 10%);}
|
||||
ui-popup.large {max-width:640px;}
|
||||
ui-popup.x-large {max-width:830px;}
|
||||
ui-popup:not(.visible) {transform: translate(-50%, -200%);transition-timing-function: ease-in;box-shadow:none;}
|
||||
ui-popup .button-list {text-align:center;}
|
||||
ui-popup .button-list .button {padding: 0px 7%;}
|
||||
ui-popup .message {background: transparent}
|
||||
ui-popup .title {font-size:1.5rem}
|
||||
ui-popup:before {left:0;right:0;top:0;bottom:0;background:rgba(0,0,0,0.1);content:" ";}
|
||||
</style>
|
||||
|
||||
<template id="ui-popup">
|
||||
<div class="ui-popup">
|
||||
<slot name="title"></slot>
|
||||
<slot name="message"></slot>
|
||||
<slot name="buttons"></slot>
|
||||
</div>
|
||||
</template>
|
||||
Reference in new issue
Block a user