Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 19 additions & 9 deletions modules/cms/classes/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,15 +306,7 @@ public function runPage($page, $useAjax = true)
/*
* The 'this' variable is reserved for default variables.
*/
$this->vars['this'] = [
'page' => $this->page,
'layout' => $this->layout,
'theme' => $this->theme,
'param' => $this->router->getParameters(),
'controller' => $this,
'environment' => App::environment(),
'session' => App::make('session'),
];
$this->vars['this'] = $this->getFrontendContext();

/*
* Check for the presence of validation errors in the session.
Expand Down Expand Up @@ -584,6 +576,7 @@ protected function postProcessResult($page, $url, $content)
protected function initTwigEnvironment()
{
$this->twig = App::make('twig.environment.cms');
$this->twig->getExtension('Cms\Twig\Extension')->setController($this);
}

/**
Expand Down Expand Up @@ -1564,4 +1557,21 @@ protected function isSoftComponent($label)
{
return starts_with($label, '@');
}

/**
* Returns the frontend context variables
* @return array
*/
public function getFrontendContext(): array
{
return [
'page' => $this->page,
'layout' => $this->layout,
'theme' => $this->theme,
'param' => $this->router->getParameters(),
'controller' => $this,
'environment' => App::environment(),
'session' => App::make('session'),
];
}
}
14 changes: 14 additions & 0 deletions modules/cms/twig/Extension.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php namespace Cms\Twig;

use Block;
use Cms\Classes\Controller;
use Event;
use Twig\Extension\AbstractExtension as TwigExtension;
use Twig\TwigFilter as TwigSimpleFilter;
Expand All @@ -14,6 +15,8 @@
*/
class Extension extends TwigExtension
{
protected Controller $controller;

/**
* Returns an array of functions to add to the existing list.
*/
Expand Down Expand Up @@ -67,6 +70,7 @@ public function getTokenParsers(): array
new FlashTokenParser,
new ScriptsTokenParser,
new StylesTokenParser,
new MacroTokenParser,
];
}

Expand Down Expand Up @@ -193,4 +197,14 @@ public function endBlock($append = true): void
{
Block::endBlock($append);
}

public function getFrontendContext(): array
{
return $this->controller->getFrontendContext();
}

public function setController(Controller $controller)
{
$this->controller = $controller;
}
}
97 changes: 97 additions & 0 deletions modules/cms/twig/MacroNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php namespace Cms\Twig;

use Twig\Compiler;
use Twig\Compiler as TwigCompiler;

/**
* Extends the twig's native macro node to inject appropriate context
*
* @package winter\wn-cms-module
* @author Romain BILLOIR
*/
class MacroNode extends \Twig\Node\MacroNode
{
/**
* Compiles the node to PHP.
*
* @param TwigCompiler $compiler A TwigCompiler instance
*/
public function compile(Compiler $compiler): void
{
$compiler
->addDebugInfo($this)
->write(sprintf('public function macro_%s(', $this->getAttribute('name')))
;

$count = \count($this->getNode('arguments'));
$pos = 0;
foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->raw('$__'.$name.'__ = ')
->subcompile($default)
;

if (++$pos < $count) {
$compiler->raw(', ');
}
}

if ($count) {
$compiler->raw(', ');
}

$compiler
->raw('...$__varargs__')
->raw(")\n")
->write("{\n")
->indent()
->write("\$macros = \$this->macros;\n")
->write("\$context = \$this->env->mergeGlobals(array_merge(\n")
->indent()
->write("['this' => \$this->env->getExtension('Cms\Twig\Extension')->getFrontendContext()],\n")
->write("[\n")
;

foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->write('')
->string($name)
->raw(' => $__'.$name.'__')
->raw(",\n")
;
}

$compiler
->write('')
->string(self::VARARGS_NAME)
->raw(' => ')
;

$compiler
->raw("\$__varargs__,\n")
->outdent()
->write("]));\n\n")
->write("\$blocks = [];\n\n")
;
if ($compiler->getEnvironment()->isDebug()) {
$compiler->write("ob_start();\n");
} else {
$compiler->write("ob_start(function () { return ''; });\n");
}
$compiler
->write("try {\n")
->indent()
->subcompile($this->getNode('body'))
->raw("\n")
->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
->outdent()
->write("} finally {\n")
->indent()
->write("ob_end_clean();\n")
->outdent()
->write("}\n")
->outdent()
->write("}\n\n")
;
}
}
55 changes: 55 additions & 0 deletions modules/cms/twig/MacroTokenParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Cms\Twig;

use App;
use Twig\Error\SyntaxError;
use Twig\Node\BodyNode;
use Twig\Node\Node;
use Twig\Token;
use Twig\TokenParser\AbstractTokenParser;

/**
* Duplication of the twig's native macro token parser to use the Winter's MacroNode
*
* @package winter\wn-cms-module
* @author Romain BILLOIR
*/
class MacroTokenParser extends AbstractTokenParser
{
public function parse(Token $token): Node
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue();

$arguments = $this->parser->getExpressionParser()->parseArguments(true, true);

$stream->expect(/* Token::BLOCK_END_TYPE */ 3);
$this->parser->pushLocalScope();
$body = $this->parser->subparse([$this, 'decideBlockEnd'], true);
if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) {
$value = $token->getValue();

if ($value != $name) {
throw new SyntaxError(sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext());
}
}
$this->parser->popLocalScope();
$stream->expect(/* Token::BLOCK_END_TYPE */ 3);

$this->parser->setMacro($name, new MacroNode($name, new BodyNode([$body]), $arguments, $lineno, $this->getTag()));

return new Node();
}

public function decideBlockEnd(Token $token): bool
{
return $token->test('endmacro');
}

public function getTag(): string
{
return 'macro';
}
}