From 7c885c72fd43afa295f41dc860744079e79df773 Mon Sep 17 00:00:00 2001 From: Gary Green <1702638+garygreen@users.noreply.github.com> Date: Sun, 29 Oct 2023 11:59:48 +0000 Subject: [PATCH] feat: frontend content flexible order priorities (#3765) * Fix frontend factory so it includes controller content * chore: more readable parameter passing * feat: add priorities to frontend content --- .../core/src/Admin/AdminServiceProvider.php | 2 +- framework/core/src/Extend/Frontend.php | 9 +- .../core/src/Forum/ForumServiceProvider.php | 12 ++- framework/core/src/Frontend/Frontend.php | 16 ++-- .../src/Frontend/FrontendServiceProvider.php | 11 +-- .../core/src/Http/RouteHandlerFactory.php | 6 +- .../extenders/FrontendContentTest.php | 89 +++++++++++++++++++ 7 files changed, 123 insertions(+), 22 deletions(-) create mode 100644 framework/core/tests/integration/extenders/FrontendContentTest.php diff --git a/framework/core/src/Admin/AdminServiceProvider.php b/framework/core/src/Admin/AdminServiceProvider.php index bde292eb2..384c02c87 100644 --- a/framework/core/src/Admin/AdminServiceProvider.php +++ b/framework/core/src/Admin/AdminServiceProvider.php @@ -109,7 +109,7 @@ class AdminServiceProvider extends AbstractServiceProvider /** @var \Flarum\Frontend\Frontend $frontend */ $frontend = $container->make('flarum.frontend.factory')('admin'); - $frontend->content($container->make(Content\AdminPayload::class)); + $frontend->content($container->make(Content\AdminPayload::class), 100); return $frontend; }); diff --git a/framework/core/src/Extend/Frontend.php b/framework/core/src/Extend/Frontend.php index 67a204ffa..22ea6c056 100644 --- a/framework/core/src/Extend/Frontend.php +++ b/framework/core/src/Extend/Frontend.php @@ -132,12 +132,13 @@ class Frontend implements ExtenderInterface * - \Psr\Http\Message\ServerRequestInterface $request * * The callable should return void. + * @param int $priority: The priority of the content. Higher priorities are executed first. * * @return self */ - public function content(callable|string|null $callback): self + public function content(callable|string|null $callback, int $priority = 0): self { - $this->content[] = $callback; + $this->content[] = compact('callback', 'priority'); return $this; } @@ -303,7 +304,7 @@ class Frontend implements ExtenderInterface "flarum.frontend.$this->frontend", function (ActualFrontend $frontend, Container $container) { foreach ($this->content as $content) { - $frontend->content(ContainerUtil::wrapCallback($content, $container)); + $frontend->content(ContainerUtil::wrapCallback($content['callback'], $container), $content['priority']); } } ); @@ -323,7 +324,7 @@ class Frontend implements ExtenderInterface $preloads = is_callable($preloadArr) ? ContainerUtil::wrapCallback($preloadArr, $container)($document) : $preloadArr; $document->preloads = array_merge($document->preloads, $preloads); } - }); + }, 110); } ); } diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index a4db2dad6..66203febd 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -22,6 +22,7 @@ use Flarum\Frontend\AddLocaleAssets; use Flarum\Frontend\AddTranslations; use Flarum\Frontend\Assets; use Flarum\Frontend\Compiler\Source\SourceCollector; +use Flarum\Frontend\Frontend; use Flarum\Frontend\RecompileFrontendAssets; use Flarum\Http\Middleware as HttpMiddleware; use Flarum\Http\RouteCollection; @@ -127,8 +128,15 @@ class ForumServiceProvider extends AbstractServiceProvider return $assets; }); - $this->container->bind('flarum.frontend.forum', function (Container $container) { - return $container->make('flarum.frontend.factory')('forum'); + $this->container->bind('flarum.frontend.forum', function (Container $container, array $parameters = []) { + /** @var Frontend $frontend */ + $frontend = $container->make('flarum.frontend.factory')('forum'); + + if (isset($parameters['content'])) { + $frontend->content(is_callable($parameters['content']) ? $parameters['content'] : $container->make($parameters['content']), 100); + } + + return $frontend; }); $this->container->singleton('flarum.forum.discussions.sortmap', function () { diff --git a/framework/core/src/Frontend/Frontend.php b/framework/core/src/Frontend/Frontend.php index e8ac8b35d..32079881c 100644 --- a/framework/core/src/Frontend/Frontend.php +++ b/framework/core/src/Frontend/Frontend.php @@ -17,7 +17,7 @@ use Psr\Http\Message\ServerRequestInterface as Request; class Frontend { /** - * @var callable[] + * @var array */ protected array $content = []; @@ -27,9 +27,9 @@ class Frontend ) { } - public function content(callable $content): void + public function content(callable $callback, int $priority): void { - $this->content[] = $content; + $this->content[] = compact('callback', 'priority'); } public function document(Request $request): Document @@ -45,8 +45,14 @@ class Frontend protected function populate(Document $document, Request $request): void { - foreach ($this->content as $content) { - $content($document, $request); + $content = $this->content; + + usort($content, function ($a, $b) { + return $b['priority'] <=> $a['priority']; + }); + + foreach ($content as $item) { + $item['callback']($document, $request); } } diff --git a/framework/core/src/Frontend/FrontendServiceProvider.php b/framework/core/src/Frontend/FrontendServiceProvider.php index c7a8aa98e..c72452e2a 100644 --- a/framework/core/src/Frontend/FrontendServiceProvider.php +++ b/framework/core/src/Frontend/FrontendServiceProvider.php @@ -54,15 +54,16 @@ class FrontendServiceProvider extends AbstractServiceProvider $this->container->singleton('flarum.frontend.factory', function (Container $container) { return function (string $name) use ($container) { + /** @var Frontend $frontend */ $frontend = $container->make(Frontend::class); $frontend->content(function (Document $document) use ($name) { $document->layoutView = 'flarum::frontend.'.$name; - }); + }, 200); - $frontend->content($container->make(Content\Assets::class)->forFrontend($name)); - $frontend->content($container->make(Content\CorePayload::class)); - $frontend->content($container->make(Content\Meta::class)); + $frontend->content($container->make(Content\Assets::class)->forFrontend($name), 190); + $frontend->content($container->make(Content\CorePayload::class), 180); + $frontend->content($container->make(Content\Meta::class), 170); $frontend->content(function (Document $document) use ($container) { $default_preloads = $container->make('flarum.frontend.default_preloads'); @@ -90,7 +91,7 @@ class FrontendServiceProvider extends AbstractServiceProvider $default_preloads, $document->preloads, ); - }); + }, 160); return $frontend; }; diff --git a/framework/core/src/Http/RouteHandlerFactory.php b/framework/core/src/Http/RouteHandlerFactory.php index f09e4b7bf..bbfea9230 100644 --- a/framework/core/src/Http/RouteHandlerFactory.php +++ b/framework/core/src/Http/RouteHandlerFactory.php @@ -40,11 +40,7 @@ class RouteHandlerFactory public function toFrontend(string $frontend, callable|string|null $content = null): Closure { return $this->toController(function (Container $container) use ($frontend, $content) { - $frontend = $container->make("flarum.frontend.$frontend"); - - if ($content) { - $frontend->content(is_callable($content) ? $content : $container->make($content)); - } + $frontend = $container->make("flarum.frontend.$frontend", compact('content')); return new FrontendController($frontend); }); diff --git a/framework/core/tests/integration/extenders/FrontendContentTest.php b/framework/core/tests/integration/extenders/FrontendContentTest.php new file mode 100644 index 000000000..ed5c0bd7e --- /dev/null +++ b/framework/core/tests/integration/extenders/FrontendContentTest.php @@ -0,0 +1,89 @@ +extend( + (new Frontend('forum')) + ->content(function (Document $document) use (&$title) { + $title = $document->title; + }), + ); + + $body = $this->send( + $this->request('GET', '/') + )->getBody()->getContents(); + + $this->assertNotNull($title, $body); + } + + /** + * @test + */ + public function content_can_be_added_with_high_priority() + { + $title = 1; + + $this->extend( + (new Frontend('forum')) + ->content(function (Document $document) use (&$title) { + $title = $document->title; + }, 110), + ); + + $body = $this->send( + $this->request('GET', '/') + )->getBody()->getContents(); + + $this->assertNull($title, $body); + } + + /** + * @test + */ + public function contents_can_be_added_with_different_priorities() + { + $test = []; + + $this->extend( + (new Frontend('forum')) + ->content(function (Document $document) use (&$test) { + $test[] = 1; + }, 110) + ->content(function (Document $document) use (&$test) { + $test[] = 3; + }, 90) + ->content(function (Document $document) use (&$test) { + $test[] = 2; + }, 100), + ); + + $body = $this->send( + $this->request('GET', '/') + )->getBody()->getContents(); + + $this->assertEquals([1, 2, 3], $test, $body); + } +}