mirror of
https://github.com/BookStackApp/BookStack.git
synced 2026-05-04 18:08:46 +03:00
Theme: Added the ability to add views before/after existing ones
Adds a registration system via the logical theme system, to tell BookStack about views to render before or after a specific template is included in the system.
This commit is contained in:
@@ -4,7 +4,9 @@ namespace BookStack\App\Providers;
|
||||
|
||||
use BookStack\Theming\ThemeEvents;
|
||||
use BookStack\Theming\ThemeService;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ThemeServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -24,8 +26,17 @@ class ThemeServiceProvider extends ServiceProvider
|
||||
{
|
||||
// Boot up the theme system
|
||||
$themeService = $this->app->make(ThemeService::class);
|
||||
$themeService->registerViewPathsForTheme($this->app->make('view')->getFinder());
|
||||
$themeService->readThemeActions();
|
||||
$themeService->dispatch(ThemeEvents::APP_BOOT, $this->app);
|
||||
|
||||
$viewFactory = $this->app->make('view');
|
||||
$themeService->registerViewPathsForTheme($viewFactory->getFinder());
|
||||
|
||||
if ($themeService->logicalThemeIsActive()) {
|
||||
$themeService->readThemeActions();
|
||||
$themeService->dispatch(ThemeEvents::APP_BOOT, $this->app);
|
||||
$viewFactory->share('__theme', $themeService);
|
||||
Blade::directive('include', function ($expression) {
|
||||
return "<?php echo \$__theme->handleViewInclude({$expression}, array_diff_key(get_defined_vars(), ['__data' => 1, '__path' => 1])); ?>";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,16 @@ class ThemeService
|
||||
*/
|
||||
protected array $listeners = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
protected array $beforeViews = [];
|
||||
|
||||
/**
|
||||
* @var array<string, array<string, int>>
|
||||
*/
|
||||
protected array $afterViews = [];
|
||||
|
||||
/**
|
||||
* Get the currently configured theme.
|
||||
* Returns an empty string if not configured.
|
||||
@@ -82,15 +92,22 @@ class ThemeService
|
||||
public function readThemeActions(): void
|
||||
{
|
||||
$themeActionsFile = theme_path('functions.php');
|
||||
if ($themeActionsFile && file_exists($themeActionsFile)) {
|
||||
try {
|
||||
require $themeActionsFile;
|
||||
} catch (\Error $exception) {
|
||||
throw new ThemeException("Failed loading theme functions file at \"{$themeActionsFile}\" with error: {$exception->getMessage()}");
|
||||
}
|
||||
try {
|
||||
require $themeActionsFile;
|
||||
} catch (\Error $exception) {
|
||||
throw new ThemeException("Failed loading theme functions file at \"{$themeActionsFile}\" with error: {$exception->getMessage()}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a logical theme is active.
|
||||
*/
|
||||
public function logicalThemeIsActive(): bool
|
||||
{
|
||||
$themeActionsFile = theme_path('functions.php');
|
||||
return $themeActionsFile && file_exists($themeActionsFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any extra paths for where we may expect views to be located
|
||||
* with the provided FileViewFinder, to make custom views available for use.
|
||||
@@ -108,4 +125,63 @@ class ThemeService
|
||||
$driverManager = app()->make(SocialDriverManager::class);
|
||||
$driverManager->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the response for a blade template view include.
|
||||
*/
|
||||
public function handleViewInclude(string $viewPath, array $data = []): string
|
||||
{
|
||||
$viewsContent = [
|
||||
...$this->renderViewSets($this->beforeViews[$viewPath] ?? [], $data),
|
||||
view()->make($viewPath, $data)->render(),
|
||||
...$this->renderViewSets($this->afterViews[$viewPath] ?? [], $data),
|
||||
];
|
||||
|
||||
return implode("\n", $viewsContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom view to be rendered before the given target view is included in the template system.
|
||||
*/
|
||||
public function registerViewRenderBefore(string $targetView, string $localView, int $priority = 50): void
|
||||
{
|
||||
$this->registerAdjacentView($this->beforeViews, $targetView, $localView, $priority);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom view to be rendered after the given target view is included in the template system.
|
||||
*/
|
||||
public function registerViewRenderAfter(string $targetView, string $localView, int $priority = 50): void
|
||||
{
|
||||
$this->registerAdjacentView($this->afterViews, $targetView, $localView, $priority);
|
||||
}
|
||||
|
||||
protected function registerAdjacentView(array &$location, string $targetView, string $localView, int $priority = 50): void
|
||||
{
|
||||
$viewPath = theme_path($localView . '.blade.php');
|
||||
if (!file_exists($viewPath)) {
|
||||
throw new ThemeException("Expected registered view file at \"{$viewPath}\" does not exist");
|
||||
}
|
||||
|
||||
if (!isset($location[$targetView])) {
|
||||
$location[$targetView] = [];
|
||||
}
|
||||
$location[$targetView][$viewPath] = $priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $viewSet
|
||||
* @return string[]
|
||||
*/
|
||||
protected function renderViewSets(array $viewSet, array $data): array
|
||||
{
|
||||
$paths = array_keys($viewSet);
|
||||
usort($paths, function (string $a, string $b) use ($viewSet) {
|
||||
return $viewSet[$a] <=> $viewSet[$b];
|
||||
});
|
||||
|
||||
return array_map(function (string $viewPath) use ($data) {
|
||||
return view()->file($viewPath, $data)->render();
|
||||
}, $paths);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user