Skip to content

Commit a20438b

Browse files
committed
Theme System: Fixed theme view before/after issues
- Updated the system to work with modules. - Updated module docs to consider namespacing. - Fixed view loading and registration event ordering. - Fixed checking if views are registered.
1 parent 9d3d0a4 commit a20438b

File tree

4 files changed

+34
-11
lines changed

4 files changed

+34
-11
lines changed

app/App/Providers/ThemeServiceProvider.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ public function boot(): void
3535
$themeService->readThemeActions();
3636
$themeService->dispatch(ThemeEvents::APP_BOOT, $this->app);
3737

38-
$themeViews = new ThemeViews();
38+
$themeViews = new ThemeViews($viewFactory->getFinder());
39+
$themeViews->registerViewPathsForTheme($themeService->getModules());
3940
$themeService->dispatch(ThemeEvents::THEME_REGISTER_VIEWS, $themeViews);
40-
$themeViews->registerViewPathsForTheme($viewFactory->getFinder(), $themeService->getModules());
4141
if ($themeViews->hasRegisteredViews()) {
4242
$viewFactory->share('__themeViews', $themeViews);
4343
Blade::directive('include', function ($expression) {

app/Theming/ThemeViews.php

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,26 @@ class ThemeViews
1717
*/
1818
protected array $afterViews = [];
1919

20+
public function __construct(
21+
protected FileViewFinder $finder
22+
) {
23+
}
24+
2025
/**
2126
* Register any extra paths for where we may expect views to be located
22-
* with the provided FileViewFinder, to make custom views available for use.
27+
* with the FileViewFinder, to make custom views available for use.
2328
* @param ThemeModule[] $modules
2429
*/
25-
public function registerViewPathsForTheme(FileViewFinder $finder, array $modules): void
30+
public function registerViewPathsForTheme(array $modules): void
2631
{
2732
foreach ($modules as $module) {
2833
$moduleViewsPath = $module->path('views');
2934
if (file_exists($moduleViewsPath) && is_dir($moduleViewsPath)) {
30-
$finder->prependLocation($moduleViewsPath);
35+
$this->finder->prependLocation($moduleViewsPath);
3136
}
3237
}
3338

34-
$finder->prependLocation(theme_path());
39+
$this->finder->prependLocation(theme_path());
3540
}
3641

3742
/**
@@ -70,19 +75,21 @@ public function renderAfter(string $targetView, string $localView, int $priority
7075

7176
public function hasRegisteredViews(): bool
7277
{
73-
return !empty($this->beforeViews) && !empty($this->afterViews);
78+
return !empty($this->beforeViews) || !empty($this->afterViews);
7479
}
7580

7681
protected function registerAdjacentView(array &$location, string $targetView, string $localView, int $priority = 50): void
7782
{
78-
$viewPath = theme_path($localView . '.blade.php');
79-
if (!file_exists($viewPath)) {
80-
throw new ThemeException("Expected registered view file at \"{$viewPath}\" does not exist");
83+
try {
84+
$viewPath = $this->finder->find($localView);
85+
} catch (\InvalidArgumentException $exception) {
86+
throw new ThemeException("Expected registered view file with name \"{$localView}\" could not be found.");
8187
}
8288

8389
if (!isset($location[$targetView])) {
8490
$location[$targetView] = [];
8591
}
92+
8693
$location[$targetView][$viewPath] = $priority;
8794
}
8895

dev/docs/theme-system-modules.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ Here are some general best practices when it comes to creating modules:
5858
- Use a unique name and clear description so the user can understand the purpose of the module.
5959
- Increment the metadata version on change, keeping to [semver](https://semver.org/) to indicate compatibility of new versions.
6060
- Where possible, prefer to [insert views before/after](logical-theme-system.md#custom-view-registration-example) instead of overriding existing views, to reduce likelihood of conflicts or update troubles.
61+
- When using/registering custom views, use some level of unique namespacing within the view path to prevent potential conflicts with other customizations.
62+
- For example, I may store a view within my module as `views/my-module-name-welcome.blade.php`, to be registered as 'my-module-name-welcome'.
63+
- This is important since views may be resolved from other modules or the active theme, which may/will override your module level view.
6164

6265
### Distribution Format
6366

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use BookStack\Facades\Theme;
66
use Tests\TestCase;
77

8-
class ThemeModuleTests extends TestCase
8+
class ThemeModuleTest extends TestCase
99
{
1010
public function test_modules_loaded_on_theme_load()
1111
{
@@ -207,6 +207,19 @@ public function test_logical_functions_file_loaded_from_module_and_it_runs_along
207207
});
208208
}
209209

210+
public function test_module_can_use_theme_view_render_functions()
211+
{
212+
$this->usingModuleFolder(function (string $moduleFolderPath) {
213+
file_put_contents($moduleFolderPath . '/functions.php', "<?php\n\BookStack\Facades\Theme::listen(\BookStack\Theming\ThemeEvents::THEME_REGISTER_VIEWS, fn(\$views) => \$views->renderBefore('layouts.parts.header', 'cat', 100));");
214+
mkdir($moduleFolderPath . '/views', 0777, true);
215+
file_put_contents($moduleFolderPath . '/views/cat.blade.php', 'mysupercatispouncy');
216+
217+
$this->refreshApplication();
218+
219+
$this->asAdmin()->get('/')->assertSee('mysupercatispouncy');
220+
});
221+
}
222+
210223
protected function usingModuleFolder(callable $callback): void
211224
{
212225
$this->usingThemeFolder(function (string $themeFolder) use ($callback) {

0 commit comments

Comments
 (0)