diff --git a/app/Exceptions/ThemeException.php b/app/Exceptions/ThemeException.php
new file mode 100644
index 000000000..b721effe2
--- /dev/null
+++ b/app/Exceptions/ThemeException.php
@@ -0,0 +1,7 @@
+
+ */
+ protected array $listeners = [];
/**
* Listen to a given custom theme event,
* setting up the action to be ran when the event occurs.
*/
- public function listen(string $event, callable $action)
+ public function listen(string $event, callable $action): void
{
if (!isset($this->listeners[$event])) {
$this->listeners[$event] = [];
@@ -31,10 +35,8 @@ class ThemeService
*
* If a callback returns a non-null value, this method will
* stop and return that value itself.
- *
- * @return mixed
*/
- public function dispatch(string $event, ...$args)
+ public function dispatch(string $event, ...$args): mixed
{
foreach ($this->listeners[$event] ?? [] as $action) {
$result = call_user_func_array($action, $args);
@@ -49,7 +51,7 @@ class ThemeService
/**
* Register a new custom artisan command to be available.
*/
- public function registerCommand(Command $command)
+ public function registerCommand(Command $command): void
{
Artisan::starting(function (Application $application) use ($command) {
$application->addCommands([$command]);
@@ -59,18 +61,22 @@ class ThemeService
/**
* Read any actions from the set theme path if the 'functions.php' file exists.
*/
- public function readThemeActions()
+ public function readThemeActions(): void
{
$themeActionsFile = theme_path('functions.php');
if ($themeActionsFile && file_exists($themeActionsFile)) {
- require $themeActionsFile;
+ try {
+ require $themeActionsFile;
+ } catch (\Error $exception) {
+ throw new ThemeException("Failed loading theme functions file at \"{$themeActionsFile}\" with error: {$exception->getMessage()}");
+ }
}
}
/**
* @see SocialAuthService::addSocialDriver
*/
- public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null)
+ public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null): void
{
$socialAuthService = app()->make(SocialAuthService::class);
$socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect);
diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php
index e0a6f46d0..8875788a6 100644
--- a/resources/views/layouts/base.blade.php
+++ b/resources/views/layouts/base.blade.php
@@ -65,7 +65,9 @@
@yield('bottom')
-
+ @if($cspNonce ?? false)
+
+ @endif
@yield('scripts')
@include('layouts.parts.base-body-end')
diff --git a/tests/ThemeTest.php b/tests/ThemeTest.php
index 08c99d297..f0266cd0c 100644
--- a/tests/ThemeTest.php
+++ b/tests/ThemeTest.php
@@ -8,6 +8,7 @@ use BookStack\Activity\Models\Webhook;
use BookStack\Entities\Models\Book;
use BookStack\Entities\Models\Page;
use BookStack\Entities\Tools\PageContent;
+use BookStack\Exceptions\ThemeException;
use BookStack\Facades\Theme;
use BookStack\Theming\ThemeEvents;
use BookStack\Users\Models\User;
@@ -51,6 +52,19 @@ class ThemeTest extends TestCase
});
}
+ public function test_theme_functions_loads_errors_are_caught_and_logged()
+ {
+ $this->usingThemeFolder(function ($themeFolder) {
+ $functionsFile = theme_path('functions.php');
+ file_put_contents($functionsFile, "expectException(ThemeException::class);
+ $this->expectExceptionMessageMatches('/Failed loading theme functions file at ".*?" with error: Class "BookStack\\\\Biscuits" not found/');
+
+ $this->runWithEnv('APP_THEME', $themeFolder, fn() => null);
+ });
+ }
+
public function test_event_commonmark_environment_configure()
{
$callbackCalled = false;