diff --git a/composer.json b/composer.json index 3ec783547..f1faa2eed 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,6 @@ "php": "^7.4|^8.0", "cebe/markdown": "^1.2@dev", "cycle/proxy-factory": "^1.2", - "yiisoft/composer-config-plugin": "^1.0@dev", "nyholm/psr7": "^1.0", "psr/log": "^1.1", "yiisoft/access": "^1.0", @@ -30,9 +29,10 @@ "yiisoft/auth": "^1.0", "yiisoft/cache": "^3.0@dev", "yiisoft/cache-file": "^3.0@dev", + "yiisoft/composer-config-plugin": "^1.0@dev", "yiisoft/data": "^3.0@dev", + "yiisoft/data-response": "^3.0@dev", "yiisoft/di": "^3.0@dev", - "yiisoft/yii-event": "^3.0@dev", "yiisoft/factory": "^3.0@dev", "yiisoft/html": "^3.0@dev", "yiisoft/injector": "^1.0", @@ -45,18 +45,19 @@ "yiisoft/router": "^3.0@dev", "yiisoft/router-fastroute": "^3.0@dev", "yiisoft/security": "^3.0@dev", + "yiisoft/serializer": "^3.0@dev", "yiisoft/strings": "^1.0", "yiisoft/var-dumper": "^3.0@dev", "yiisoft/view": "^3.0@dev", "yiisoft/widget": "^3.0@dev", + "yiisoft/yii-bootstrap4": "^3.0@dev", "yiisoft/yii-console": "^3.0@dev", "yiisoft/yii-cycle": "^3.0@dev", - "yiisoft/yii-web": "^3.0@dev", "yiisoft/yii-debug": "^3.0@dev", "yiisoft/yii-debug-api": "^3.0@dev", - "yiisoft/serializer": "^3.0@dev", - "yiisoft/data-response": "^3.0@dev", - "yiisoft/yii-bootstrap4": "^3.0@dev" + "yiisoft/yii-event": "^3.0@dev", + "yiisoft/yii-view": "^3.0@dev", + "yiisoft/yii-web": "^3.0@dev" }, "require-dev": { "roave/security-advisories": "dev-master", diff --git a/config/params.php b/config/params.php index e12b5a9f7..b8f46e431 100644 --- a/config/params.php +++ b/config/params.php @@ -1,7 +1,9 @@ [ @@ -40,17 +42,17 @@ 'yiisoft/yii-cycle' => [ 'dbal' => [ - 'default' => 'default', - 'aliases' => [], - 'databases' => [ + 'default' => 'default', + 'aliases' => [], + 'databases' => [ 'default' => ['connection' => 'sqlite'] ], 'connections' => [ 'sqlite' => [ - 'driver' => \Spiral\Database\Driver\SQLite\SQLiteDriver::class, + 'driver' => \Spiral\Database\Driver\SQLite\SQLiteDriver::class, 'connection' => 'sqlite:@runtime/database.db', - 'username' => '', - 'password' => '', + 'username' => '', + 'password' => '', ], ], // 'query-logger' => \Yiisoft\Yii\Cycle\Logger\StdoutQueryLogger::class, @@ -80,4 +82,10 @@ '@src/Blog/Entity', ], ], + + 'yiisoft/yii-view' => [ + 'injections' => [ + Reference::to(ApplicationViewInjection::class), + ], + ], ]; diff --git a/config/web.php b/config/web.php index 9405a4cb5..4eea10feb 100644 --- a/config/web.php +++ b/config/web.php @@ -1,10 +1,11 @@ get(CommentRepository::class) ); }, - ViewRenderer::class => [ - '__construct()' => [ - 'viewBasePath' => '@views', - 'layout' => '@views/layout/main', - ], - ], ]; diff --git a/src/ApplicationViewInjection.php b/src/ApplicationViewInjection.php new file mode 100644 index 000000000..0870b8f68 --- /dev/null +++ b/src/ApplicationViewInjection.php @@ -0,0 +1,60 @@ +user = $user; + $this->urlMatcher = $urlMatcher; + } + + public function getLayoutParameters(): array + { + return [ + 'user' => $this->user->getIdentity(), + 'currentUrl' => (string)$this->urlMatcher->getLastMatchedRequest()->getUri(), + 'brandLabel' => 'Yii Demo', + ]; + } + + public function getMetaTags(): array + { + return [ + [ + '__key' => 'generator', + 'name' => 'generator', + 'value' => 'Yii', + ], + ]; + } + + public function getLinkTags(): array + { + return [ + [ + '__key' => 'favicon', + 'name' => 'icon', + 'value' => '/favicon.ico', + ], + ]; + } +} diff --git a/src/Blog/Archive/ArchiveController.php b/src/Blog/Archive/ArchiveController.php index 8cbbeffc6..06b324a63 100644 --- a/src/Blog/Archive/ArchiveController.php +++ b/src/Blog/Archive/ArchiveController.php @@ -1,12 +1,14 @@ viewRenderer->render( + return $this->viewRenderer->withCsrf()->render( 'login', [ - 'csrf' => $request->getAttribute('csrf_token'), 'body' => $body, 'error' => $error, ] diff --git a/src/Controller/SignupController.php b/src/Controller/SignupController.php index 7b79f9d5f..674f14bdd 100644 --- a/src/Controller/SignupController.php +++ b/src/Controller/SignupController.php @@ -1,9 +1,10 @@ viewRenderer->render( + return $this->viewRenderer->withCsrf()->render( 'signup', [ 'body' => $body, 'error' => $error, - 'csrf' => $request->getAttribute('csrf_token'), ] ); } diff --git a/src/Controller/SiteController.php b/src/Controller/SiteController.php index e2a83b0af..b90698910 100644 --- a/src/Controller/SiteController.php +++ b/src/Controller/SiteController.php @@ -1,9 +1,11 @@ get(Csrf::class); + $csrf = $container->get(CsrfMiddleware::class); $session = $container->get(SessionMiddleware::class); $router = $container->get(Router::class); $errorCatcher = $container->get(ErrorCatcher::class); diff --git a/src/ViewRenderer.php b/src/ViewRenderer.php deleted file mode 100644 index b9d777ad7..000000000 --- a/src/ViewRenderer.php +++ /dev/null @@ -1,197 +0,0 @@ -responseFactory = $responseFactory; - $this->user = $user; - $this->aliases = $aliases; - $this->view = $view; - $this->urlMatcher = $urlMatcher; - $this->viewBasePath = $viewBasePath; - $this->layout = $layout; - } - - public function getViewPath(): string - { - if ($this->viewPath !== null) { - return $this->viewPath; - } - - return $this->aliases->get($this->viewBasePath) . '/' . $this->name; - } - - public function render(string $view, array $parameters = []): ResponseInterface - { - $contentRenderer = fn () => $this->renderProxy($view, $parameters); - - return $this->responseFactory->createResponse($contentRenderer); - } - - public function renderPartial(string $view, array $parameters = []): ResponseInterface - { - $content = $this->view->render($view, $parameters, $this); - - return $this->responseFactory->createResponse($content); - } - - public function withController(object $controller): self - { - $new = clone $this; - $new->name = $this->getName($controller); - - return $new; - } - - public function withControllerName(string $name): self - { - $new = clone $this; - $new->name = $name; - - return $new; - } - - public function withViewPath(string $viewPath): self - { - $new = clone $this; - $new->viewPath = $viewPath; - - return $new; - } - - public function withViewBasePath(string $viewBasePath): self - { - $new = clone $this; - $new->viewBasePath = $viewBasePath; - - return $new; - } - - public function withLayout(string $layout): self - { - $new = clone $this; - $new->layout = $layout; - - return $new; - } - - public function withCsrf(string $requestAttribute = Csrf::REQUEST_NAME): self - { - $new = clone $this; - $new->csrfTokenRequestAttribute = $requestAttribute; - $new->csrfToken = $new->getCsrfToken(); - - return $new; - } - - private function renderProxy(string $view, array $parameters = []): string - { - if ($this->csrfToken !== null) { - $parameters['csrf'] = $this->csrfToken; - $this->view->registerMetaTag( - [ - 'name' => 'csrf', - 'content' => $this->csrfToken, - ], - 'csrf_meta_tags' - ); - } - $content = $this->view->render($view, $parameters, $this); - $user = $this->user->getIdentity(); - $layout = $this->findLayoutFile($this->aliases->get($this->layout)); - - if ($layout === null) { - return $content; - } - - $layoutParameters['content'] = $content; - $layoutParameters['user'] = $user; - - return $this->view->renderFile( - $layout, - $layoutParameters, - $this, - ); - } - - private function findLayoutFile(?string $file): ?string - { - if ($file === null) { - return null; - } - - if (pathinfo($file, PATHINFO_EXTENSION) !== '') { - return $file; - } - - return $file . '.' . $this->view->getDefaultExtension(); - } - - /** - * Returns the controller name. Name should be converted to "id" case. - * Method returns classname without `controller` on the ending. - * If namespace is not contain `controller` or `controllers` - * then returns only classname without `controller` on the ending - * else returns all subnamespaces from `controller` (or `controllers`) to the end - * - * @return string - * @example App\Controller\FooBar\BazController -> foo-bar/baz - * @example App\Controllers\FooBar\BazController -> foo-bar/baz - * @example Path\To\File\BlogController -> blog - * @see Inflector::pascalCaseToId() - */ - private function getName(object $controller): string - { - if ($this->name !== null) { - return $this->name; - } - - $regexp = '/((?<=controller\\\|s\\\)(?:[\w\\\]+)|(?:[a-z]+))controller/iuU'; - if (!preg_match($regexp, get_class($controller), $m) || empty($m[1])) { - throw new \RuntimeException('Cannot detect controller name'); - } - - $inflector = new Inflector(); - $name = str_replace('\\', '/', $m[1]); - - return $this->name = $inflector->pascalCaseToId($name); - } - - private function getCsrfToken(): string - { - return $this->urlMatcher->getLastMatchedRequest()->getAttribute($this->csrfTokenRequestAttribute); - } -} diff --git a/views/layout/main.php b/views/layout/main.php index 7353e05a2..f42ff4b57 100644 --- a/views/layout/main.php +++ b/views/layout/main.php @@ -9,13 +9,15 @@ * @var \Yiisoft\Router\UrlGeneratorInterface $urlGenerator * @var Yiisoft\Router\UrlMatcherInterface $urlMatcher * @var \Yiisoft\View\WebView $this - * @var \App\Entity\User $user * @var \Yiisoft\Assets\AssetManager $assetManager * @var string $content + * + * @see \App\ApplicationViewInjection + * @var \App\Entity\User $user + * @var string $currentUrl + * @var string $brandLabel */ -$currentUrl = (string) $urlMatcher->getLastMatchedRequest()->getUri(); - $assetManager->register([ AppAsset::class ]); @@ -38,7 +40,7 @@ $this->beginBody(); echo NavBar::begin() - ->brandLabel('Yii Demo') + ->brandLabel($brandLabel) ->brandUrl($urlGenerator->generate('site/index')) ->options(['class' => 'navbar navbar-light bg-light navbar-expand-sm text-white']) ->start();