diff --git a/composer.json b/composer.json index f88b59a..7fd0d08 100644 --- a/composer.json +++ b/composer.json @@ -10,33 +10,58 @@ } ], "require": { - "vlucas/phpdotenv": "^3.4@dev", + "php": "^8.2", "php-di/php-di": "dev-master", - "league/route": "dev-master", + "ext-json": "*", + "ext-posix": "*", + "ext-xmlreader": "*", + "ext-fileinfo": "*", + "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": "dev-master", + "guzzlehttp/guzzle": "^6.3@dev", + "swiftmailer/swiftmailer": "^6.2@dev", "mcnd/storage": "dev-master", + "mcnd/lean": "dev-master", + "mcnd/lean-console": "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/event": "dev-master", + "mcnd/notes-breadcrumb": "dev-master", + "mcnd/notes-cronard": "dev-master", + "mcnd/notes-event": "dev-master", + "mcnd/notes-tell": "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", - "vlucas/phpdotenv": "^3.4@dev", - "middlewares/whoops": "dev-master", - "ralouphie/getallheaders": "dev-master" + "mcnd/ulmus-user": "dev-master", + "mcnd/thebugs": "dev-master", + "mcnd/taxus": "dev-master", + "psr/simple-cache": "*" }, "repositories": [ + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/lean-console.git" + }, { "type": "vcs", "url": "https://git.mcnd.ca/mcndave/cronard.git" }, + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/event.git" + }, + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/the-bugs.git" + }, { "type": "vcs", "url": "https://git.mcnd.ca/mcndave/storage.git" @@ -45,6 +70,10 @@ "type": "vcs", "url": "https://git.mcnd.ca/mcndave/ulmus.git" }, + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/ulmus-user.git" + }, { "type": "vcs", "url": "https://git.mcnd.ca/mcndave/picea.git" @@ -61,10 +90,22 @@ "type": "vcs", "url": "https://git.mcnd.ca/mcndave/notes-route.git" }, + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/notes-cronard.git" + }, { "type": "vcs", "url": "https://git.mcnd.ca/mcndave/notes-security.git" }, + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/notes-breadcrumb.git" + }, + { + "type": "vcs", + "url": "https://git.mcnd.ca/mcndave/notes-event.git" + }, { "type": "vcs", "url": "https://git.mcnd.ca/mcndave/notes-tell.git" diff --git a/meta/definitions/email.php b/meta/definitions/email.php index 22901ab..508c168 100644 --- a/meta/definitions/email.php +++ b/meta/definitions/email.php @@ -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), ]; diff --git a/meta/definitions/event.php b/meta/definitions/event.php index fa8cb15..2e5fdd6 100644 --- a/meta/definitions/event.php +++ b/meta/definitions/event.php @@ -1,17 +1,78 @@ <?php +use Picea\Picea; use function DI\autowire, DI\create, DI\get; -use Lean\Lean; +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 }; 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->fromAttributes($c->get(Notes\Event\EventFetcher::class)); + return $mw; + }, + + Event\EventDefinition::class => function($c) { + return new Event\EventDefinition([ + new class() implements RoutingCompileRoutes { + public function execute(Routing $routing, Route|\Notes\Route\Annotation\Method\Route $attribute) : void + { + if (null !== ($name = $attribute->name ?? null)) { + $routing->extension->registerRoute($name, $attribute->getRoute(), $attribute->class, $attribute->classMethod, $attribute->methods ?? (array)$attribute->method); + } + } + }, + + new class() implements RoutingMapRoutes { + public function execute(Routing $routing, ContainerInterface $container, ServerRequestInterface & $request, Route|\Notes\Route\Annotation\Method\Route $attribute) : void + { + $class = $attribute->class; + $method = $attribute->classMethod; + $object = $container->get($class); + + $request = $request->withAttribute('lean.route', $attribute); + + # Checking if user needs to be logged + if ( $container->has(SecurityHandler::class) ){ + if ( $redirect = $container->get(SecurityHandler::class)->verify($class, $method) ) { + if ( empty($object->user) || ! $object->user->logged ) { + $routing->session->set('redirectedFrom', (string) $request->getUri()); + $routing->response = $redirect; + + return; + } + } + + if ( $container->has(Taxus::class) ) { + if ( $forbidden = $container->get(SecurityHandler::class)->taxus($class, $method, $object->user ?? null) ) { + $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) { diff --git a/meta/definitions/template.php b/meta/definitions/template.php index 8aa3b7b..95d3d81 100644 --- a/meta/definitions/template.php +++ b/meta/definitions/template.php @@ -65,7 +65,7 @@ return [ NumberExtension::class => autowire(NumberExtension::class), - UrlExtension::class => create(UrlExtension::class)->constructor(getenv("URL_BASE"), get('git.commit'), getenv('APP_URL'), (bool) getenv('FORCE_SSL')), + UrlExtension::class => create(UrlExtension::class)->constructor(getenv("URL_BASE"), get('git.commit'), explode(',', getenv('APP_URL')), (bool) getenv('FORCE_SSL')), Cache::class => create(Opcache::class)->constructor(getenv("CACHE_PATH"), get(Context::class)), diff --git a/skeleton/.env b/skeleton/.env index 57ff9c5..b9cc2c8 100644 --- a/skeleton/.env +++ b/skeleton/.env @@ -1,11 +1,11 @@ APP_ENV = dev DEBUG = 1 -KEYS = "dev:dev" +KEYS = "dev" CRON_KEY = dev # Route URL_BASE = "" -APP_URL = "dev.cslsj.qc.ca" +APP_URL = "dev.mcnd.ca" # Path CACHE_DIR = "var/cache" @@ -22,18 +22,9 @@ 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_PORT = "" +DATABASE_HOST = "" DATABASE_NAME = "" DATABASE_USERNAME = "" DATABASE_PASSWORD = "" diff --git a/skeleton/meta/definitions/auth.php b/skeleton/meta/definitions/auth.php index c07a3d9..9405214 100644 --- a/skeleton/meta/definitions/auth.php +++ b/skeleton/meta/definitions/auth.php @@ -52,7 +52,7 @@ 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'); diff --git a/skeleton/meta/definitions/env/prod.php b/skeleton/meta/definitions/env/prod.php index 9afc93e..9bf09af 100644 --- a/skeleton/meta/definitions/env/prod.php +++ b/skeleton/meta/definitions/env/prod.php @@ -2,11 +2,8 @@ use Picea\Picea; -#use CSLSJ\Debogueur\DebogueurMiddleware; use Negundo\Client\{ NegundoMiddleware, SoftwareConfig }; -use - use Laminas\Diactoros\Response\HtmlResponse; use Psr\Http\Server\MiddlewareInterface, diff --git a/skeleton/src/Kernel.php b/skeleton/src/Kernel.php index adff807..7bfccd4 100644 --- a/skeleton/src/Kernel.php +++ b/skeleton/src/Kernel.php @@ -29,8 +29,6 @@ new class(dirname(__DIR__)) extends \Lean\Kernel { protected function serviceContainer() : self { - # $this->container->get(ConnectionAdapter::class); - return parent::serviceContainer(); } diff --git a/skeleton/var/tmp/.gitkeep b/skeleton/var/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/ControllerTrait.php b/src/ControllerTrait.php index fdca39b..9f866c6 100644 --- a/src/ControllerTrait.php +++ b/src/ControllerTrait.php @@ -92,7 +92,7 @@ trait ControllerTrait { return new HtmlResponse($html, $code, $headers); } - public static function renderJson(/*\JsonSerializable|array*/ $data, int $code = 200, array $headers = []) : ResponseInterface + public static function renderJson(mixed $data, int $code = 200, array $headers = []) : ResponseInterface { return new JsonResponse($data, $code, $headers); } diff --git a/src/Event/RoutingCompileRoutes.php b/src/Event/RoutingCompileRoutes.php new file mode 100644 index 0000000..5eda957 --- /dev/null +++ b/src/Event/RoutingCompileRoutes.php @@ -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|\Notes\Route\Annotation\Method\Route $attribute) : void; +} diff --git a/src/Event/RoutingMapRoutes.php b/src/Event/RoutingMapRoutes.php new file mode 100644 index 0000000..292ff0e --- /dev/null +++ b/src/Event/RoutingMapRoutes.php @@ -0,0 +1,12 @@ +<?php + +namespace Lean\Event; + +use Lean\Routing; +use Notes\Route\Attribute\Method\Route; +use Psr\Container\ContainerInterface; +use Psr\Http\Message\{ ResponseInterface, ServerRequestInterface }; + +interface RoutingMapRoutes { + public function execute(Routing $routing, ContainerInterface $container, ServerRequestInterface & $request, Route|\Notes\Route\Annotation\Method\Route $attribute) : void; +} diff --git a/src/Routing.php b/src/Routing.php index 6fa75ac..2b9fd4a 100644 --- a/src/Routing.php +++ b/src/Routing.php @@ -24,58 +24,34 @@ use Picea\Picea, use Storage\Cookie, Storage\Session; +use Mcnd\Event\EventManager; + use function DI\autowire, DI\create; class Routing { public Annotation $selectedRoute; - protected Session $session; - - protected Cookie $cookie; - - protected UrlExtension $extension; - - protected RouteFetcher $fetcher; - - protected SecurityHandler $security; - - protected LanguageHandler $language; - - protected Taxus $taxus; - - protected Router $router; + public ResponseInterface $response; 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 Session $session, + public Cookie $cookie, + public UrlExtension $extension, + public Router $router, + public RouteFetcher $fetcher, + public SecurityHandler $security, + public LanguageHandler $language, + public Taxus $taxus, + 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); - } + $this->eventManager->execute(Event\RoutingCompileRoutes::class, $this, $annotation); + /* @deprecated annotation->method will become standard when using native attributes */ 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 ( @@ -84,34 +60,13 @@ class Routing { { $class = $annotation->class; $method = $annotation->classMethod; - - # $container->set($class, autowire($class)->method($method, $request)); - $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(); - - return $redirect; - } - - if ( $forbidden = $this->security->taxus($class, $method, $object->user ?? null) ) { - return $forbidden; - } - - # @TODO of course, this as to go ; moving to a simple callback method soon which can then be fed by Picea - if ( $container->has(Picea::class) ) { - $container->get(Picea::class)->globalVariables['route'] = $annotation; - } - - $request = $request->withAttribute('lean.route', $annotation); - - $this->session->set("lean.route", $annotation); + $this->eventManager->execute(Event\RoutingMapRoutes::class, $this, $container, $request, $annotation); $container->set(ServerRequestInterface::class, $request); - return $object->$method($request, $arguments); + return $this->response ?? $object->$method($request, $arguments); }); } } diff --git a/view/lean/widget/popup.phtml b/view/lean/widget/popup.phtml new file mode 100644 index 0000000..e710722 --- /dev/null +++ b/view/lean/widget/popup.phtml @@ -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> \ No newline at end of file