diff --git a/classes/core/PKPBladeViewServiceProvider.php b/classes/core/PKPBladeViewServiceProvider.php
new file mode 100644
index 00000000000..85a241310f3
--- /dev/null
+++ b/classes/core/PKPBladeViewServiceProvider.php
@@ -0,0 +1,148 @@
+registerViewFinder();
+ $this->registerBladeCompiler();
+ $this->registerEngineResolver();
+ $this->registerFactory();
+ }
+
+ /**
+ * Register the view environment.
+ *
+ * @return void
+ */
+ public function registerFactory()
+ {
+ $this->app->singleton(
+ ViewFactory::class,
+ fn (PKPContainer $app) => $this->configureViewFactoryWithBindings($app)
+ );
+
+ $this->app->singleton(
+ 'view',
+ fn (PKPContainer $app) => $app->get(ViewFactory::class)
+ );
+ }
+
+ /**
+ * Register the view finder implementation.
+ *
+ * @return void
+ */
+ public function registerViewFinder()
+ {
+ $this->app->singleton(
+ 'view.finder',
+ fn (PKPContainer $app) => new FileViewFinder(
+ $app->get('files'),
+ [
+ $app->basePath('/templates'),
+ $app->basePath('/lib/pkp/templates'),
+ ]
+ )
+ );
+ }
+
+ /**
+ * Register the Blade compiler implementation.
+ *
+ * @return void
+ */
+ public function registerBladeCompiler()
+ {
+ $this->app->singleton('blade.compiler', function (PKPContainer $app) {
+ return tap(new BladeCompiler(
+ $app->get('files'),
+ BASE_SYS_DIR . '/compiled',
+ $app->get('config')->get('view.relative_hash', false) ? $app->basePath() : '',
+ $app->get('config')->get('view.cache', true),
+ $app->get('config')->get('view.compiled_extension', 'php'),
+ ), function (BladeCompiler $bladeCompiler) {
+ $bladeCompiler->component('dynamic-component', DynamicComponent::class);
+
+ // Register component namespaces for PKP and APP Blade components
+ $bladeCompiler->componentNamespace('PKP\\components', 'pkp');
+ $bladeCompiler->componentNamespace('APP\\components', 'app');
+
+ $this->app->instance(BladeCompiler::class, $bladeCompiler);
+ $this->app->alias(
+ BladeCompiler::class,
+ (new class extends \Illuminate\Support\Facades\Blade {
+ public static function getFacadeAccessor() { return parent::getFacadeAccessor(); }
+ })::getFacadeAccessor()
+ );
+ });
+ });
+ }
+
+ /**
+ * Register the Blade engine implementation.
+ *
+ * @param \Illuminate\View\Engines\EngineResolver $resolver
+ * @return void
+ */
+ public function registerBladeEngine($resolver)
+ {
+ $resolver->register(
+ 'blade',
+ fn () => new CompilerEngine(
+ $this->app['blade.compiler'],
+ $this->app['files']
+ )
+ );
+ }
+
+ /**
+ * Configure the view factory with required bindings and setting the alias
+ *
+ * @param \PKP\core\PKPContainer $app
+ * @return \Illuminate\View\Factory
+ */
+ protected function configureViewFactoryWithBindings(PKPContainer $app): ViewFactory
+ {
+ // Next we need to grab the engine resolver instance that will be used by the
+ // environment. The resolver will be used by an environment to get each of
+ // the various engine implementations such as plain PHP or Blade engine.
+ $resolver = $app->get('view.engine.resolver');
+
+ $finder = $app->get('view.finder');
+
+ $factory = $this->createFactory($resolver, $finder, $app->get('events'));
+
+ // We will also set the container instance on this view environment since the
+ // view composers may be classes registered in the container, which allows
+ // for great testable, flexible composers for the application developer.
+ $factory->setContainer($app);
+
+ $factory->share('app', $app);
+
+ $app->instance(\Illuminate\Contracts\View\Factory::class, $factory);
+ $app->alias(
+ \Illuminate\Contracts\View\Factory::class,
+ (new class extends \Illuminate\Support\Facades\View {
+ public static function getFacadeAccessor() { return parent::getFacadeAccessor(); }
+ })::getFacadeAccessor()
+ );
+
+ return $factory;
+ }
+}
diff --git a/classes/core/PKPContainer.php b/classes/core/PKPContainer.php
index 2283669131a..a438a71fd9c 100644
--- a/classes/core/PKPContainer.php
+++ b/classes/core/PKPContainer.php
@@ -30,6 +30,7 @@
use Illuminate\Support\Str;
use Laravel\Scout\EngineManager;
use PKP\config\Config;
+use PKP\core\PKPBladeViewServiceProvider;
use PKP\i18n\LocaleServiceProvider;
use PKP\proxy\ProxyParser;
use Throwable;
@@ -57,6 +58,17 @@ public function __construct()
$this->registerCoreContainerAliases();
}
+ /**
+ * Get the application namespace.
+ * Required by Laravel's ComponentTagCompiler for component discovery.
+ *
+ * @return string
+ */
+ public function getNamespace(): string
+ {
+ return 'PKP\\';
+ }
+
/**
* Get the proper database driver
*/
@@ -83,6 +95,7 @@ protected function registerBaseBindings(): void
static::setInstance($this);
$this->instance('app', $this);
$this->instance(Container::class, $this);
+ $this->instance(\Illuminate\Contracts\Foundation\Application::class, $this);
$this->instance('path', $this->basePath);
$this->instance('path.config', "{$this->basePath}/config"); // Necessary for Scout to let CLI happen
$this->singleton(ExceptionHandler::class, function () {
@@ -189,6 +202,7 @@ public function registerConfiguredProviders(): void
$this->register(new ConsoleCommandServiceProvider($this));
$this->register(new ValidationServiceProvider($this));
$this->register(new \Illuminate\Foundation\Providers\FormRequestServiceProvider($this));
+ $this->register(new PKPBladeViewServiceProvider($this));
$this->register(new \Laravel\Scout\ScoutServiceProvider($this));
}
@@ -336,6 +350,9 @@ public function registerCoreContainerAliases(): void
\Illuminate\Contracts\Encryption\Encrypter::class,
\Illuminate\Contracts\Encryption\StringEncrypter::class,
],
+ 'view' => [
+ \Illuminate\Support\Facades\View::class,
+ ],
] as $key => $aliases) {
foreach ($aliases as $alias) {
$this->alias($key, $alias);
diff --git a/composer.json b/composer.json
index 1e8208bc746..e8981d5da20 100644
--- a/composer.json
+++ b/composer.json
@@ -83,6 +83,8 @@
"APP\\pages\\": "../../pages/",
"PKP\\": "classes/",
"APP\\": "../../classes/",
+ "PKP\\components\\": ["templates/components/"],
+ "APP\\components\\": ["../../templates/components/"],
"PKP\\plugins\\": "plugins/",
"APP\\plugins\\": "../../plugins/",
"PKP\\jobs\\": "jobs/",
diff --git a/pages/about/AboutContextHandler.php b/pages/about/AboutContextHandler.php
index c836cdf4362..2e0fcb402f1 100644
--- a/pages/about/AboutContextHandler.php
+++ b/pages/about/AboutContextHandler.php
@@ -25,6 +25,8 @@
use PKP\context\Context;
use PKP\facades\Locale;
use PKP\orcid\OrcidManager;
+use Illuminate\Support\Facades\View;
+use PKP\core\PKPContainer;
use PKP\plugins\Hook;
use PKP\security\authorization\ContextRequiredPolicy;
use PKP\security\Role;
@@ -57,9 +59,21 @@ public function authorize($request, &$args, $roleAssignments)
*/
public function index($args, $request)
{
- $templateMgr = TemplateManager::getManager($request);
- $this->setupTemplate($request);
- $templateMgr->display('frontend/pages/about.tpl');
+ // Render template with page.blade.php
+ // we can also use View::make('TEMPLATE', [...]);
+ echo view('bladeTest.about', [
+ 'title' => 'My Title',
+ 'text' => 'This is my text!',
+ ]);
+
+ // echo \Illuminate\Support\Facades\View::make('about', [
+ // 'title' => 'My Title',
+ // 'text' => 'This is my text!',
+ // ]);
+
+ // $templateMgr = TemplateManager::getManager($request);
+ // $this->setupTemplate($request);
+ // $templateMgr->display('frontend/pages/about.tpl');
}
diff --git a/templates/bladeTest/about.blade.php b/templates/bladeTest/about.blade.php
new file mode 100644
index 00000000000..335632bbf6a
--- /dev/null
+++ b/templates/bladeTest/about.blade.php
@@ -0,0 +1,6 @@
+Hello World -- from /lib/pkp/template
+params :
+ title : {{$title}}
+ text : {{$text}}
+
+
This is a trivial Blade component example.
+