Fix issues with accessing the expected context variables inside of Twig macros.

This fixes #578 by adding the ability to pass the CMS Controller instance to the CMS Twig Extension removing the reliance on context variables as well as making the expected "global" twig variables inside of the CMS Twig environment actually global within that environment.

Replaces #598 & #593.

Credit to @RomainMazB for the initial implementation.
This commit is contained in:
Luke Towers 2022-07-10 01:09:09 -06:00
parent e2683d8041
commit 5b8d189df4
12 changed files with 49 additions and 31 deletions

View File

@ -26,7 +26,7 @@ jobs:
- name: Install PHP and PHP Code Sniffer
uses: shivammathur/setup-php@v2
with:
php-version: 7.3
php-version: 8.0
extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
tools: phpcs

View File

@ -47,8 +47,7 @@
},
"scripts": {
"post-create-project-cmd": [
"@php artisan key:generate",
"@php artisan package:discover"
"@php artisan key:generate"
],
"post-update-cmd": [
"@php artisan winter:version",

View File

@ -306,7 +306,7 @@ class Controller
/*
* The 'this' variable is reserved for default variables.
*/
$this->vars['this'] = [
$this->getTwig()->addGlobal('this', [
'page' => $this->page,
'layout' => $this->layout,
'theme' => $this->theme,
@ -314,7 +314,7 @@ class Controller
'controller' => $this,
'environment' => App::environment(),
'session' => App::make('session'),
];
]);
/*
* Check for the presence of validation errors in the session.
@ -584,6 +584,7 @@ class Controller
protected function initTwigEnvironment()
{
$this->twig = App::make('twig.environment.cms');
$this->twig->getExtension(\Cms\Twig\Extension::class)->setController($this);
}
/**

View File

@ -2,6 +2,7 @@
namespace Cms\Tests\Classes;
use Cms;
use System\Tests\Bootstrap\TestCase;
use Cms\Classes\Theme;
use Cms\Classes\Controller;
@ -589,7 +590,7 @@ ESC;
$response = $controller->run('/with-macro')->getContent();
$this->assertStringContainsString(
'<p><a href="/">with-macro.htm</a><strong>with-macro.htm</strong></p>',
'<p><a href="' . Cms::url('/') . '">with-macro.htm</a><strong>with-macro.htm</strong></p>',
$response
);
}

View File

@ -3,5 +3,4 @@ url = "/with-macro"
{% macro pageTest(_context) -%}
<a href="{{ 'index' | page }}">{{ this.page.fileName }}</a>
{%- endmacro pageTest %}
<p>{{ _self.pageTest() }}<strong>{{ this.page.fileName }}</strong></p>

View File

@ -34,7 +34,7 @@ class ComponentNode extends TwigNode
}
$compiler
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->componentFunction(\$context,")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->componentFunction(")
->subcompile($this->getNode('nodes')->getNode(0))
->write(", \$context['__cms_component_params']")
->write(");\n")

View File

@ -36,7 +36,7 @@ class ContentNode extends TwigNode
}
$compiler
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->contentFunction(\$context,")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->contentFunction(")
->subcompile($this->getNode('nodes')->getNode(0))
->write(", \$context['__cms_content_params']")
->write(");\n")

View File

@ -5,6 +5,7 @@ use Event;
use Twig\Extension\AbstractExtension as TwigExtension;
use Twig\TwigFilter as TwigSimpleFilter;
use Twig\TwigFunction as TwigSimpleFunction;
use Cms\Classes\Controller;
/**
* The CMS Twig extension class implements the basic CMS Twig functions and filters.
@ -14,6 +15,27 @@ use Twig\TwigFunction as TwigSimpleFunction;
*/
class Extension extends TwigExtension
{
/**
* The instanciated CMS controller
*/
protected Controller $controller;
/**
* Sets the CMS controller instance
*/
public function setController(Controller $controller)
{
$this->controller = $controller;
}
/**
* Gets the CMS controller instance
*/
public function getController(): Controller
{
return $this->controller;
}
/**
* Returns an array of functions to add to the existing list.
*/
@ -21,7 +43,6 @@ class Extension extends TwigExtension
{
$options = [
'is_safe' => ['html'],
'needs_context' => true,
];
return [
@ -40,7 +61,6 @@ class Extension extends TwigExtension
{
$options = [
'is_safe' => ['html'],
'needs_context' => true,
];
return [
@ -73,41 +93,41 @@ class Extension extends TwigExtension
/**
* Renders a page; used in the layout code to output the requested page.
*/
public function pageFunction(array $context): string
public function pageFunction(): string
{
return $context['this']['controller']->renderPage();
return $this->controller->renderPage();
}
/**
* Renders the requested partial with the provided parameters. Optionally throw an exception if the partial cannot be found
*/
public function partialFunction(array $context, string $name, array $parameters = [], bool $throwException = false): string
public function partialFunction(string $name, array $parameters = [], bool $throwException = false): string
{
return $context['this']['controller']->renderPartial($name, $parameters, $throwException);
return $this->controller->renderPartial($name, $parameters, $throwException);
}
/**
* Renders the requested content file.
*/
public function contentFunction(array $context, string $name, array $parameters = []): string
public function contentFunction(string $name, array $parameters = []): string
{
return $context['this']['controller']->renderContent($name, $parameters);
return $this->controller->renderContent($name, $parameters);
}
/**
* Renders a component's default partial.
*/
public function componentFunction(array $context, string $name, array $parameters = []): string
public function componentFunction(string $name, array $parameters = []): string
{
return $context['this']['controller']->renderComponent($name, $parameters);
return $this->controller->renderComponent($name, $parameters);
}
/**
* Renders registered assets of a given type or all types if $type not provided
*/
public function assetsFunction(array $context, string $type = null): ?string
public function assetsFunction(string $type = null): ?string
{
return $context['this']['controller']->makeAssets($type);
return $this->controller->makeAssets($type);
}
/**
@ -126,26 +146,24 @@ class Extension extends TwigExtension
/**
* Returns the relative URL for the provided page
*
* @param array $context The Twig context for the call (relies on $context['this']['controller'] to exist)
* @param mixed $name Specifies the Cms Page file name.
* @param array|bool $parameters Route parameters to consider in the URL. If boolean will be used as the value for $routePersistence
* @param bool $routePersistence Set to false to exclude the existing routing parameters from the generated URL
*/
public function pageFilter(array $context, $name, $parameters = [], $routePersistence = true): ?string
public function pageFilter($name, $parameters = [], $routePersistence = true): ?string
{
return $context['this']['controller']->pageUrl($name, $parameters, $routePersistence);
return $this->controller->pageUrl($name, $parameters, $routePersistence);
}
/**
* Converts supplied URL to a theme URL relative to the website root. If the URL provided is an
* array then the files will be combined.
*
* @param array $context The Twig context for the call (relies on $context['this']['controller'] to exist)
* @param mixed $url Specifies the input to be turned into a URL (arrays will be passed to the AssetCombiner)
*/
public function themeFilter(array $context, $url): string
public function themeFilter($url): string
{
return $context['this']['controller']->themeUrl($url);
return $this->controller->themeUrl($url);
}
/**

View File

@ -25,7 +25,7 @@ class PageNode extends TwigNode
{
$compiler
->addDebugInfo($this)
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->pageFunction(\$context);\n")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->pageFunction();\n")
;
}
}

View File

@ -34,7 +34,7 @@ class PartialNode extends TwigNode
}
$compiler
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->partialFunction(\$context,")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->partialFunction(")
->subcompile($this->getNode('nodes')->getNode(0))
->write(", \$context['__cms_partial_params']")
->write(", true")

View File

@ -25,7 +25,7 @@ class ScriptsNode extends TwigNode
{
$compiler
->addDebugInfo($this)
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->assetsFunction(\$context, 'js');\n")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->assetsFunction('js');\n")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->displayBlock('scripts');\n")
;
}

View File

@ -25,7 +25,7 @@ class StylesNode extends TwigNode
{
$compiler
->addDebugInfo($this)
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->assetsFunction(\$context, 'css');\n")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->assetsFunction('css');\n")
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->displayBlock('styles');\n")
;
}