feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
45
modules/cms/twig/ComponentNode.php
Normal file
45
modules/cms/twig/ComponentNode.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a component node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ComponentNode extends TwigNode
|
||||
{
|
||||
public function __construct(TwigNode $nodes, $paramNames, $lineno, $tag = 'component')
|
||||
{
|
||||
parent::__construct(['nodes' => $nodes], ['names' => $paramNames], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$compiler->write("\$context['__cms_component_params'] = [];\n");
|
||||
|
||||
for ($i = 1; $i < count($this->getNode('nodes')); $i++) {
|
||||
$compiler->write("\$context['__cms_component_params']['".$this->getAttribute('names')[$i-1]."'] = ");
|
||||
$compiler->subcompile($this->getNode('nodes')->getNode($i));
|
||||
$compiler->write(";\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->componentFunction(")
|
||||
->subcompile($this->getNode('nodes')->getNode(0))
|
||||
->write(", \$context['__cms_component_params']")
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
$compiler->write("unset(\$context['__cms_component_params']);\n");
|
||||
}
|
||||
}
|
||||
70
modules/cms/twig/ComponentTokenParser.php
Normal file
70
modules/cms/twig/ComponentTokenParser.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
use Twig\Error\SyntaxError as TwigErrorSyntax;
|
||||
|
||||
/**
|
||||
* Parser for the `{% component %}` Twig tag.
|
||||
*
|
||||
* {% component "pluginComponent" %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ComponentTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return TwigNode A TwigNode instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$name = $this->parser->getExpressionParser()->parseExpression();
|
||||
$paramNames = [];
|
||||
$nodes = [$name];
|
||||
|
||||
$end = false;
|
||||
while (!$end) {
|
||||
$current = $stream->next();
|
||||
|
||||
switch ($current->getType()) {
|
||||
case TwigToken::NAME_TYPE:
|
||||
$paramNames[] = $current->getValue();
|
||||
$stream->expect(TwigToken::OPERATOR_TYPE, '=');
|
||||
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
|
||||
break;
|
||||
|
||||
case TwigToken::BLOCK_END_TYPE:
|
||||
$end = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new TwigErrorSyntax(
|
||||
sprintf('Invalid syntax in the component tag. Line %s', $lineno),
|
||||
$stream->getCurrent()->getLine(),
|
||||
$stream->getSourceContext()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new ComponentNode(new TwigNode($nodes), $paramNames, $token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'component';
|
||||
}
|
||||
}
|
||||
48
modules/cms/twig/ContentNode.php
Normal file
48
modules/cms/twig/ContentNode.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a content node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ContentNode extends TwigNode
|
||||
{
|
||||
public function __construct(TwigNode $nodes, $paramNames, $lineno, $tag = 'content')
|
||||
{
|
||||
parent::__construct(['nodes' => $nodes], ['names' => $paramNames], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$compiler->write("\$context['__cms_content_params'] = [];\n");
|
||||
|
||||
for ($i = 1; $i < count($this->getNode('nodes')); $i++) {
|
||||
$compiler->write("\$context['__cms_content_params']['".$this->getAttribute('names')[$i-1]."'] = ");
|
||||
$compiler->write('twig_escape_filter($this->env, ');
|
||||
$compiler->subcompile($this->getNode('nodes')->getNode($i));
|
||||
$compiler->write(")");
|
||||
$compiler->write(";\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->contentFunction(")
|
||||
->subcompile($this->getNode('nodes')->getNode(0))
|
||||
->write(", \$context['__cms_content_params']")
|
||||
->write(", true")
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
$compiler->write("unset(\$context['__cms_content_params']);\n");
|
||||
}
|
||||
}
|
||||
74
modules/cms/twig/ContentTokenParser.php
Normal file
74
modules/cms/twig/ContentTokenParser.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
use Twig\Error\SyntaxError as TwigErrorSyntax;
|
||||
|
||||
/**
|
||||
* Parser for the `{% content %}` Twig tag.
|
||||
*
|
||||
* {% content "intro.htm" %}
|
||||
*
|
||||
* {% content "intro.md" name='John' %}
|
||||
*
|
||||
* {% content "intro/txt" name='John', year=2013 %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ContentTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return TwigNode A TwigNode instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$name = $this->parser->getExpressionParser()->parseExpression();
|
||||
$paramNames = [];
|
||||
$nodes = [$name];
|
||||
|
||||
$end = false;
|
||||
while (!$end) {
|
||||
$current = $stream->next();
|
||||
|
||||
switch ($current->getType()) {
|
||||
case TwigToken::NAME_TYPE:
|
||||
$paramNames[] = $current->getValue();
|
||||
$stream->expect(TwigToken::OPERATOR_TYPE, '=');
|
||||
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
|
||||
break;
|
||||
|
||||
case TwigToken::BLOCK_END_TYPE:
|
||||
$end = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new TwigErrorSyntax(
|
||||
sprintf('Invalid syntax in the content tag. Line %s', $lineno),
|
||||
$stream->getCurrent()->getLine(),
|
||||
$stream->getSourceContext()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new ContentNode(new TwigNode($nodes), $paramNames, $token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'content';
|
||||
}
|
||||
}
|
||||
597
modules/cms/twig/DebugExtension.php
Normal file
597
modules/cms/twig/DebugExtension.php
Normal file
@@ -0,0 +1,597 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Template as TwigTemplate;
|
||||
use Twig\Extension\AbstractExtension as TwigExtension;
|
||||
use Twig\Environment as TwigEnvironment;
|
||||
use Twig\TwigFunction as TwigSimpleFunction;
|
||||
use Cms\Classes\ComponentBase;
|
||||
use Illuminate\Pagination\Paginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
|
||||
use Symfony\Component\VarDumper\Cloner\VarCloner;
|
||||
use Winter\Storm\Database\Model;
|
||||
|
||||
class DebugExtension extends TwigExtension
|
||||
{
|
||||
const PAGE_CAPTION = 'Page variables';
|
||||
const ARRAY_CAPTION = 'Array variables';
|
||||
const OBJECT_CAPTION = 'Object variables';
|
||||
const COMPONENT_CAPTION = 'Component variables';
|
||||
|
||||
/**
|
||||
* @var integer Helper for rendering table row styles.
|
||||
*/
|
||||
protected $zebra = 1;
|
||||
|
||||
/**
|
||||
* @var boolean If no variable is passed, true.
|
||||
*/
|
||||
protected $variablePrefix = false;
|
||||
|
||||
/**
|
||||
* @var array Collection of method/property comments.
|
||||
*/
|
||||
protected $commentMap = [];
|
||||
|
||||
/**
|
||||
* @var array Blocked object methods that should not be included in the dump.
|
||||
*/
|
||||
protected $blockMethods = [
|
||||
'componentDetails',
|
||||
'defineProperties',
|
||||
'getPropertyOptions',
|
||||
'offsetExists',
|
||||
'offsetGet',
|
||||
'offsetSet',
|
||||
'offsetUnset'
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns a list of global functions to add to the existing list.
|
||||
* @return array An array of global functions
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigSimpleFunction('dump', [$this, 'runDump'], [
|
||||
'is_safe' => ['html'],
|
||||
'needs_context' => true,
|
||||
'needs_environment' => true
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the dump variables, if none is supplied, all the twig
|
||||
* template variables are used
|
||||
* @param TwigEnvironment $env
|
||||
* @param array $context
|
||||
* @return string
|
||||
*/
|
||||
public function runDump(TwigEnvironment $env, $context)
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$result = '';
|
||||
|
||||
$count = func_num_args();
|
||||
if ($count == 2) {
|
||||
$this->variablePrefix = true;
|
||||
$vars = [];
|
||||
foreach ($context as $key => $value) {
|
||||
if (!$value instanceof TwigTemplate) {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$result .= $this->dump($vars, static::PAGE_CAPTION);
|
||||
}
|
||||
else {
|
||||
$this->variablePrefix = false;
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
$var = func_get_arg($i);
|
||||
|
||||
if ($var instanceof ComponentBase) {
|
||||
$caption = [static::COMPONENT_CAPTION, get_class($var)];
|
||||
}
|
||||
elseif (is_array($var)) {
|
||||
$caption = static::ARRAY_CAPTION;
|
||||
}
|
||||
elseif (is_object($var)) {
|
||||
$caption = [static::OBJECT_CAPTION, get_class($var)];
|
||||
}
|
||||
else {
|
||||
$caption = [static::OBJECT_CAPTION, gettype($var)];
|
||||
}
|
||||
|
||||
$result .= $this->dump($var, $caption);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump information about a variable
|
||||
* @param mixed $variables Variable to dump
|
||||
* @param mixed $caption Caption [and subcaption] of the dump
|
||||
* @return void
|
||||
*/
|
||||
public function dump($variables = null, $caption = null)
|
||||
{
|
||||
$this->commentMap = [];
|
||||
$this->zebra = 1;
|
||||
$info = [];
|
||||
|
||||
if (!is_array($variables)) {
|
||||
if ($variables instanceof Paginator) {
|
||||
$variables = $this->paginatorToArray($variables);
|
||||
}
|
||||
elseif (is_object($variables)) {
|
||||
$variables = $this->objectToArray($variables);
|
||||
}
|
||||
else {
|
||||
$variables = [$variables];
|
||||
}
|
||||
}
|
||||
|
||||
$output = [];
|
||||
$output[] = '<table>';
|
||||
|
||||
if ($caption) {
|
||||
$output[] = $this->makeTableHeader($caption);
|
||||
}
|
||||
|
||||
$output[] = '<tbody>';
|
||||
foreach ($variables as $key => $item) {
|
||||
$output[] = $this->makeTableRow($key, $item);
|
||||
}
|
||||
$output[] = '</tbody>';
|
||||
$output[] = '</table>';
|
||||
|
||||
$html = implode(PHP_EOL, $output);
|
||||
|
||||
return '<pre style="' . $this->getContainerCss() . '">' . $html . '</pre>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the HTML used for the table header.
|
||||
* @param mixed $caption Caption [and subcaption] of the dump
|
||||
* @return string
|
||||
*/
|
||||
protected function makeTableHeader($caption)
|
||||
{
|
||||
if (is_array($caption)) {
|
||||
list($caption, $subcaption) = $caption;
|
||||
}
|
||||
|
||||
$output = [];
|
||||
$output[] = '<thead>';
|
||||
$output[] = '<tr>';
|
||||
$output[] = '<th colspan="3" style="'.$this->getHeaderCss().'">';
|
||||
$output[] = $caption;
|
||||
|
||||
if (isset($subcaption)) {
|
||||
$output[] = '<div style="'.$this->getSubheaderCss().'">'.$subcaption.'</div>';
|
||||
}
|
||||
|
||||
$output[] = '</th>';
|
||||
$output[] = '</tr>';
|
||||
$output[] = '</thead>';
|
||||
return implode(PHP_EOL, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the HTML used for each table row.
|
||||
* @param mixed $key
|
||||
* @param mixed $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function makeTableRow($key, $variable)
|
||||
{
|
||||
$this->zebra = $this->zebra ? 0 : 1;
|
||||
$css = $this->getDataCss($variable);
|
||||
$output = [];
|
||||
$output[] = '<tr>';
|
||||
$output[] = '<td style="'.$css.';cursor:pointer" onclick="'.$this->evalToggleDumpOnClick().'">'.$this->evalKeyLabel($key).'</td>';
|
||||
$output[] = '<td style="'.$css.'">'.$this->evalVarLabel($variable).'</td>';
|
||||
$output[] = '<td style="'.$css.'">'.$this->evalVarDesc($variable, $key).'</td>';
|
||||
$output[] = '</tr>';
|
||||
$output[] = '<tr>';
|
||||
$output[] = '<td colspan="3">'.$this->evalVarDump($variable).'</td>';
|
||||
$output[] = '</tr>';
|
||||
return implode(PHP_EOL, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds JavaScript for toggling the dump container
|
||||
* @return string
|
||||
*/
|
||||
protected function evalToggleDumpOnClick()
|
||||
{
|
||||
$output = "var d=this.parentElement.nextElementSibling.getElementsByTagName('div')[0];";
|
||||
$output .= "d.style.display=='none'?d.style.display='block':d.style.display='none'";
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps a variable using HTML Dumper, wrapped in a hidden DIV element.
|
||||
* @param mixed $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalVarDump($variable)
|
||||
{
|
||||
$dumper = new HtmlDumper;
|
||||
$cloner = new VarCloner;
|
||||
|
||||
$output = '<div style="display:none">';
|
||||
$output .= $dumper->dump($cloner->cloneVar($variable), true);
|
||||
$output .= '</div>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a variable name as HTML friendly.
|
||||
* @param string $key
|
||||
* @return string
|
||||
*/
|
||||
protected function evalKeyLabel($key)
|
||||
{
|
||||
if ($this->variablePrefix === true) {
|
||||
$output = '{{ <span>%s</span> }}';
|
||||
}
|
||||
elseif (is_array($this->variablePrefix)) {
|
||||
$prefix = implode('.', $this->variablePrefix);
|
||||
$output = '{{ <span>'.$prefix.'.%s</span> }}';
|
||||
}
|
||||
elseif ($this->variablePrefix) {
|
||||
$output = '{{ <span>'.$this->variablePrefix.'.%s</span> }}';
|
||||
}
|
||||
else {
|
||||
$output = '%s';
|
||||
}
|
||||
|
||||
return sprintf($output, $key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the variable description
|
||||
* @param mixed $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalVarLabel($variable)
|
||||
{
|
||||
$type = $this->getType($variable);
|
||||
switch ($type) {
|
||||
case 'object':
|
||||
return $this->evalObjLabel($variable);
|
||||
|
||||
case 'array':
|
||||
return $type . '('.count($variable).')';
|
||||
|
||||
default:
|
||||
return $type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an object type for label
|
||||
* @param object $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function getType($variable)
|
||||
{
|
||||
$type = gettype($variable);
|
||||
if ($type == 'string' && substr($variable, 0, 12) == '___METHOD___') {
|
||||
return 'method';
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an object type for label
|
||||
* @param object $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalObjLabel($variable)
|
||||
{
|
||||
$class = get_class($variable);
|
||||
$label = class_basename($variable);
|
||||
|
||||
if ($variable instanceof ComponentBase) {
|
||||
$label = '<strong>Component</strong>';
|
||||
}
|
||||
elseif ($variable instanceof Collection) {
|
||||
$label = 'Collection('.$variable->count().')';
|
||||
}
|
||||
elseif ($variable instanceof Paginator) {
|
||||
$label = 'Paged Collection('.$variable->count().')';
|
||||
}
|
||||
elseif ($variable instanceof Model) {
|
||||
$label = 'Model';
|
||||
}
|
||||
|
||||
return '<abbr title="'.e($class).'">'.$label.'</abbr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the variable description
|
||||
* @param mixed $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalVarDesc($variable, $key)
|
||||
{
|
||||
$type = $this->getType($variable);
|
||||
|
||||
if ($type == 'method') {
|
||||
return $this->evalMethodDesc($variable);
|
||||
}
|
||||
|
||||
if (isset($this->commentMap[$key])) {
|
||||
return $this->commentMap[$key];
|
||||
}
|
||||
|
||||
if ($type == 'array') {
|
||||
return $this->evalArrDesc($variable);
|
||||
}
|
||||
|
||||
if ($type == 'object') {
|
||||
return $this->evalObjDesc($variable);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an method type for description
|
||||
* @param object $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalMethodDesc($variable)
|
||||
{
|
||||
$parts = explode('|', $variable);
|
||||
if (count($parts) < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$method = $parts[1];
|
||||
return $this->commentMap[$method] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an array type for description
|
||||
* @param array $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalArrDesc($variable)
|
||||
{
|
||||
$output = [];
|
||||
foreach ($variable as $key => $value) {
|
||||
$output[] = '<abbr title="'.e(gettype($value)).'">'.$key.'</abbr>';
|
||||
}
|
||||
|
||||
return implode(', ', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate an object type for description
|
||||
* @param array $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function evalObjDesc($variable)
|
||||
{
|
||||
$output = [];
|
||||
if ($variable instanceof ComponentBase) {
|
||||
$details = $variable->componentDetails();
|
||||
$output[] = '<abbr title="'.array_get($details, 'description').'">';
|
||||
$output[] = array_get($details, 'name');
|
||||
$output[] = '</abbr>';
|
||||
}
|
||||
|
||||
return implode('', $output);
|
||||
}
|
||||
|
||||
//
|
||||
// Object helpers
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns default comment information for a paginator object.
|
||||
* @param Illuminate\Pagination\Paginator $paginator
|
||||
* @return array
|
||||
*/
|
||||
protected function paginatorToArray(Paginator $paginator)
|
||||
{
|
||||
$this->commentMap = [
|
||||
'links()' => 'Renders links for navigating the collection',
|
||||
'currentPage' => 'Get the current page for the request.',
|
||||
'lastPage' => 'Get the last page that should be available.',
|
||||
'perPage' => 'Get the number of items to be displayed per page.',
|
||||
'total' => 'Get the total number of items in the complete collection.',
|
||||
'from' => 'Get the number of the first item on the paginator.',
|
||||
'to' => 'Get the number of the last item on the paginator.',
|
||||
'count' => 'Returns the number of items in this collection',
|
||||
];
|
||||
|
||||
return [
|
||||
'links' => '___METHOD___|links()',
|
||||
'currentPage' => '___METHOD___|currentPage',
|
||||
'lastPage' => '___METHOD___|lastPage',
|
||||
'perPage' => '___METHOD___|perPage',
|
||||
'total' => '___METHOD___|total',
|
||||
'from' => '___METHOD___|from',
|
||||
'to' => '___METHOD___|to',
|
||||
'count' => '___METHOD___|count',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of an object as an array, containing methods and properties.
|
||||
* @param mixed $object
|
||||
* @return array
|
||||
*/
|
||||
protected function objectToArray($object)
|
||||
{
|
||||
$class = get_class($object);
|
||||
$info = new \ReflectionClass($object);
|
||||
|
||||
$this->commentMap[$class] = [];
|
||||
|
||||
$methods = [];
|
||||
foreach ($info->getMethods() as $method) {
|
||||
if (!$method->isPublic()) {
|
||||
continue; // Only public
|
||||
}
|
||||
if ($method->class != $class) {
|
||||
continue; // Only locals
|
||||
}
|
||||
$name = $method->getName();
|
||||
if (in_array($name, $this->blockMethods)) {
|
||||
continue; // Blocked methods
|
||||
}
|
||||
if (preg_match('/^on[A-Z]{1}[\w+]*$/', $name)) {
|
||||
continue; // AJAX methods
|
||||
}
|
||||
if (preg_match('/^get[A-Z]{1}[\w+]*Options$/', $name)) {
|
||||
continue; // getSomethingOptions
|
||||
}
|
||||
if (substr($name, 0, 1) == '_') {
|
||||
continue; // Magic/hidden method
|
||||
}
|
||||
$name .= '()';
|
||||
$methods[$name] = '___METHOD___|'.$name;
|
||||
$this->commentMap[$name] = $this->evalDocBlock($method);
|
||||
}
|
||||
|
||||
$vars = [];
|
||||
foreach ($info->getProperties() as $property) {
|
||||
if ($property->isStatic()) {
|
||||
continue; // Only non-static
|
||||
}
|
||||
if (!$property->isPublic()) {
|
||||
continue; // Only public
|
||||
}
|
||||
if ($property->class != $class) {
|
||||
continue; // Only locals
|
||||
}
|
||||
$name = $property->getName();
|
||||
$vars[$name] = $object->{$name};
|
||||
$this->commentMap[$name] = $this->evalDocBlock($property);
|
||||
}
|
||||
|
||||
return $methods + $vars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the comment from a DocBlock
|
||||
* @param ReflectionClass $reflectionObj
|
||||
* @return string
|
||||
*/
|
||||
protected function evalDocBlock($reflectionObj)
|
||||
{
|
||||
$comment = $reflectionObj->getDocComment();
|
||||
$comment = substr($comment, 3, -2);
|
||||
|
||||
$parts = explode('@', $comment);
|
||||
$comment = array_shift($parts);
|
||||
$comment = trim(trim($comment), '*');
|
||||
$comment = implode(' ', array_map('trim', explode('*', $comment)));
|
||||
|
||||
return $comment;
|
||||
}
|
||||
|
||||
//
|
||||
// Style helpers
|
||||
//
|
||||
|
||||
/**
|
||||
* Get the CSS string for the output data
|
||||
* @param mixed $variable
|
||||
* @return string
|
||||
*/
|
||||
protected function getDataCss($variable)
|
||||
{
|
||||
$css = [
|
||||
'padding' => '7px',
|
||||
'background-color' => $this->zebra ? '#D8D9DB' : '#FFF',
|
||||
'color' => '#405261',
|
||||
];
|
||||
|
||||
$type = gettype($variable);
|
||||
if ($type == 'NULL') {
|
||||
$css['color'] = '#999';
|
||||
}
|
||||
|
||||
return $this->arrayToCss($css);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS string for the output container
|
||||
* @return string
|
||||
*/
|
||||
protected function getContainerCss()
|
||||
{
|
||||
return $this->arrayToCss([
|
||||
'background-color' => '#F3F3F3',
|
||||
'border' => '1px solid #bbb',
|
||||
'border-radius' => '4px',
|
||||
'font-size' => '12px',
|
||||
'line-height' => '18px',
|
||||
'margin' => '20px',
|
||||
'padding' => '7px',
|
||||
'display' => 'inline-block',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS string for the output header
|
||||
* @return string
|
||||
*/
|
||||
protected function getHeaderCss()
|
||||
{
|
||||
return $this->arrayToCss([
|
||||
'font-size' => '18px',
|
||||
'font-weight' => 'normal',
|
||||
'margin' => '0',
|
||||
'padding' => '10px',
|
||||
'background-color' => '#7B8892',
|
||||
'color' => '#FFF',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CSS string for the output subheader
|
||||
* @return string
|
||||
*/
|
||||
protected function getSubheaderCss()
|
||||
{
|
||||
return $this->arrayToCss([
|
||||
'font-size' => '12px',
|
||||
'font-weight' => 'normal',
|
||||
'font-style' => 'italic',
|
||||
'margin' => '0',
|
||||
'padding' => '0',
|
||||
'background-color' => '#7B8892',
|
||||
'color' => '#FFF',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a key/value pair array into a CSS string
|
||||
* @param array $rules List of rules to process
|
||||
* @return string
|
||||
*/
|
||||
protected function arrayToCss(array $rules)
|
||||
{
|
||||
$strings = [];
|
||||
|
||||
foreach ($rules as $key => $value) {
|
||||
$strings[] = $key . ': ' . $value;
|
||||
}
|
||||
|
||||
return implode('; ', $strings);
|
||||
}
|
||||
}
|
||||
31
modules/cms/twig/DefaultNode.php
Normal file
31
modules/cms/twig/DefaultNode.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a "default" node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class DefaultNode extends TwigNode
|
||||
{
|
||||
public function __construct($lineno, $tag = 'default')
|
||||
{
|
||||
parent::__construct([], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("echo '<!-- X_WINTER_DEFAULT_BLOCK_CONTENT -->';\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
41
modules/cms/twig/DefaultTokenParser.php
Normal file
41
modules/cms/twig/DefaultTokenParser.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% default %}` Twig tag.
|
||||
*
|
||||
* {% put head %}
|
||||
* <link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet"/>
|
||||
* {% default %}
|
||||
* {% endput %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class DefaultTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return Twig\Node\Node A Twig\Node\Node instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
return new DefaultNode($token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'default';
|
||||
}
|
||||
}
|
||||
233
modules/cms/twig/Extension.php
Normal file
233
modules/cms/twig/Extension.php
Normal file
@@ -0,0 +1,233 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Block;
|
||||
use Cms\Classes\Controller;
|
||||
use Event;
|
||||
use System\Classes\Asset\Vite;
|
||||
use Twig\Extension\AbstractExtension as TwigExtension;
|
||||
use Twig\TwigFilter as TwigSimpleFilter;
|
||||
use Twig\TwigFunction as TwigSimpleFunction;
|
||||
|
||||
/**
|
||||
* The CMS Twig extension class implements the basic CMS Twig functions and filters.
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
$options = [
|
||||
'is_safe' => ['html'],
|
||||
];
|
||||
|
||||
return [
|
||||
new TwigSimpleFunction('page', [$this, 'pageFunction'], $options),
|
||||
new TwigSimpleFunction('partial', [$this, 'partialFunction'], $options),
|
||||
new TwigSimpleFunction('content', [$this, 'contentFunction'], $options),
|
||||
new TwigSimpleFunction('component', [$this, 'componentFunction'], $options),
|
||||
new TwigSimpleFunction('placeholder', [$this, 'placeholderFunction'], ['is_safe' => ['html']]),
|
||||
new TwigSimpleFunction('viteReactRefresh', [$this, 'viteReactRefreshFunction'], $options),
|
||||
new TwigSimpleFunction('vite', [$this, 'viteFunction'], $options),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of filters this extension provides.
|
||||
*/
|
||||
public function getFilters(): array
|
||||
{
|
||||
$options = [
|
||||
'is_safe' => ['html'],
|
||||
];
|
||||
|
||||
return [
|
||||
new TwigSimpleFilter('page', [$this, 'pageFilter'], $options),
|
||||
new TwigSimpleFilter('theme', [$this, 'themeFilter'], $options),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of token parsers this extension provides.
|
||||
*/
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
return [
|
||||
new PageTokenParser,
|
||||
new PartialTokenParser,
|
||||
new ContentTokenParser,
|
||||
new PutTokenParser,
|
||||
new PlaceholderTokenParser,
|
||||
new DefaultTokenParser,
|
||||
new FrameworkTokenParser,
|
||||
new SnowboardTokenParser,
|
||||
new ComponentTokenParser,
|
||||
new FlashTokenParser,
|
||||
new ScriptsTokenParser,
|
||||
new StylesTokenParser,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a page; used in the layout code to output the requested page.
|
||||
*/
|
||||
public function pageFunction(): string
|
||||
{
|
||||
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(string $name, array $parameters = [], bool $throwException = false): string|bool
|
||||
{
|
||||
return $this->controller->renderPartial($name, $parameters, $throwException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the requested content file.
|
||||
*/
|
||||
public function contentFunction(string $name, array $parameters = [], bool $throwException = false): string|bool
|
||||
{
|
||||
return $this->controller->renderContent($name, $parameters, $throwException);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a component's default partial.
|
||||
*/
|
||||
public function componentFunction(string $name, array $parameters = []): string
|
||||
{
|
||||
return $this->controller->renderComponent($name, $parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders registered assets of a given type or all types if $type not provided
|
||||
*/
|
||||
public function assetsFunction(?string $type = null): ?string
|
||||
{
|
||||
return $this->controller->makeAssets($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders placeholder content, without removing the block, must be called before the placeholder tag itself
|
||||
*/
|
||||
public function placeholderFunction(string $name, ?string $default = null): ?string
|
||||
{
|
||||
if (($result = Block::get($name)) === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = str_replace('<!-- X_WINTER_DEFAULT_BLOCK_CONTENT -->', trim($default), $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative URL for the provided page
|
||||
*
|
||||
* @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($name, $parameters = [], $routePersistence = true): ?string
|
||||
{
|
||||
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 mixed $url Specifies the input to be turned into a URL (arrays will be passed to the AssetCombiner)
|
||||
*/
|
||||
public function themeFilter($url): string
|
||||
{
|
||||
return $this->controller->themeUrl($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Vite tags via Laravel's Vite Object.
|
||||
*/
|
||||
public function viteFunction(array $entrypoints, string $package, ?string $buildDirectory = null): \Illuminate\Support\HtmlString
|
||||
{
|
||||
return Vite::tags($entrypoints, $package, $buildDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates Vite React Refresh tags via Laravel's Vite Object.
|
||||
*/
|
||||
public function viteReactRefreshFunction(string $package, ?string $buildDirectory = null): ?\Illuminate\Support\HtmlString
|
||||
{
|
||||
return Vite::reactRefreshTag($package, $buildDirectory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a layout block.
|
||||
*/
|
||||
public function startBlock(string $name): void
|
||||
{
|
||||
Block::startBlock($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a layout block contents (or null if it doesn't exist) and removes the block.
|
||||
*/
|
||||
public function displayBlock(string $name, ?string $default = null): ?string
|
||||
{
|
||||
if (($result = Block::placeholder($name)) === null) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @event cms.block.render
|
||||
* Provides an opportunity to modify the rendered block content
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('cms.block.render', function ((string) $name, (string) $result) {
|
||||
* if ($name === 'myBlockName') {
|
||||
* return 'my custom content';
|
||||
* }
|
||||
* });
|
||||
*
|
||||
*/
|
||||
if ($event = Event::fire('cms.block.render', [$name, $result], true)) {
|
||||
$result = $event;
|
||||
}
|
||||
|
||||
$result = str_replace('<!-- X_WINTER_DEFAULT_BLOCK_CONTENT -->', trim($default), $result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a layout block.
|
||||
*/
|
||||
public function endBlock($append = true): void
|
||||
{
|
||||
Block::endBlock($append);
|
||||
}
|
||||
}
|
||||
67
modules/cms/twig/FlashNode.php
Normal file
67
modules/cms/twig/FlashNode.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a flash node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class FlashNode extends TwigNode
|
||||
{
|
||||
public function __construct($name, TwigNode $body, $lineno, $tag = 'flash')
|
||||
{
|
||||
parent::__construct(['body' => $body], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$attrib = $this->getAttribute('name');
|
||||
|
||||
$compiler
|
||||
->write('$_type = isset($context["type"]) ? $context["type"] : null;')
|
||||
->write('$_message = isset($context["message"]) ? $context["message"] : null;')
|
||||
;
|
||||
|
||||
if ($attrib == 'all') {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('foreach (Flash::all() as $type => $message) {'.PHP_EOL)
|
||||
->indent()
|
||||
->write('$context["type"] = $type;')
|
||||
->write('$context["message"] = $message;')
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write('}'.PHP_EOL)
|
||||
;
|
||||
}
|
||||
else {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('$context["type"] = ')
|
||||
->string($attrib)
|
||||
->write(';')
|
||||
->write('foreach (Flash::')
|
||||
->raw($attrib)
|
||||
->write('() as $message) {'.PHP_EOL)
|
||||
->indent()
|
||||
->write('$context["message"] = $message;')
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write('}'.PHP_EOL)
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write('$context["type"] = $_type;')
|
||||
->write('$context["message"] = $_message;')
|
||||
;
|
||||
}
|
||||
}
|
||||
55
modules/cms/twig/FlashTokenParser.php
Normal file
55
modules/cms/twig/FlashTokenParser.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the {% flash %} Twig tag.
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class FlashTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
*
|
||||
* @return TwigNode A TwigNode instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
if ($token = $stream->nextIf(TwigToken::NAME_TYPE)) {
|
||||
$name = $token->getValue();
|
||||
}
|
||||
else {
|
||||
$name = 'all';
|
||||
}
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
|
||||
$body = $this->parser->subparse([$this, 'decideIfEnd'], true);
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
|
||||
return new FlashNode($name, $body, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
public function decideIfEnd(TwigToken $token)
|
||||
{
|
||||
return $token->test(['endflash']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'flash';
|
||||
}
|
||||
}
|
||||
62
modules/cms/twig/FrameworkNode.php
Normal file
62
modules/cms/twig/FrameworkNode.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use System\Models\Parameter;
|
||||
use System\Classes\CombineAssets;
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
use Url;
|
||||
|
||||
/**
|
||||
* Represents a "framework" node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class FrameworkNode extends TwigNode
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = 'framework')
|
||||
{
|
||||
parent::__construct([], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$build = Parameter::get('system::core.build', 'winter');
|
||||
$cacheBust = '?v=' . $build;
|
||||
$attrib = $this->getAttribute('name');
|
||||
$includeExtras = strtolower(trim($attrib)) === 'extras';
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$_minify = ".CombineAssets::class."::instance()->useMinify;" . PHP_EOL);
|
||||
|
||||
$basePath = rtrim(Url::asset(''), '/');
|
||||
|
||||
if ($includeExtras) {
|
||||
$compiler
|
||||
->write("if (\$_minify) {" . PHP_EOL)
|
||||
->indent()
|
||||
->write("echo '<script src=\"{$basePath}/modules/system/assets/js/framework.combined-min.js$cacheBust\"></script>'.PHP_EOL;" . PHP_EOL)
|
||||
->outdent()
|
||||
->write("}" . PHP_EOL)
|
||||
->write("else {" . PHP_EOL)
|
||||
->indent()
|
||||
->write("echo '<script src=\"{$basePath}/modules/system/assets/js/framework.js$cacheBust\"></script>'.PHP_EOL;" . PHP_EOL)
|
||||
->write("echo '<script src=\"{$basePath}/modules/system/assets/js/framework.extras.js$cacheBust\"></script>'.PHP_EOL;" . PHP_EOL)
|
||||
->outdent()
|
||||
->write("}" . PHP_EOL)
|
||||
->write("echo '<link rel=\"stylesheet\" property=\"stylesheet\" href=\"{$basePath}/modules/system/assets/css/framework.extras'.(\$_minify ? '-min' : '').'.css$cacheBust\">'.PHP_EOL;" . PHP_EOL)
|
||||
;
|
||||
}
|
||||
else {
|
||||
$compiler->write("echo '<script src=\"{$basePath}/modules/system/assets/js/framework'.(\$_minify ? '-min' : '').'.js$cacheBust\"></script>'.PHP_EOL;" . PHP_EOL);
|
||||
}
|
||||
|
||||
$compiler->write('unset($_minify);' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
45
modules/cms/twig/FrameworkTokenParser.php
Normal file
45
modules/cms/twig/FrameworkTokenParser.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% framework %}` Twig tag.
|
||||
*
|
||||
* {% framework %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class FrameworkTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return Twig\Node\Node A Twig\Node\Node instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$name = null;
|
||||
if ($token = $stream->nextIf(TwigToken::NAME_TYPE)) {
|
||||
$name = $token->getValue();
|
||||
}
|
||||
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
return new FrameworkNode($name, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'framework';
|
||||
}
|
||||
}
|
||||
165
modules/cms/twig/Loader.php
Normal file
165
modules/cms/twig/Loader.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Event;
|
||||
use Twig\Source as TwigSource;
|
||||
use Twig\Loader\LoaderInterface as TwigLoaderInterface;
|
||||
use Cms\Contracts\CmsObject;
|
||||
use System\Twig\Loader as LoaderBase;
|
||||
use Cms\Classes\Partial as CmsPartial;
|
||||
|
||||
/**
|
||||
* This class implements a Twig template loader for the CMS.
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Loader extends LoaderBase implements TwigLoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var \Cms\Classes\CmsCompoundObject A CMS object to load the template from.
|
||||
*/
|
||||
protected $obj;
|
||||
|
||||
/**
|
||||
* @var array Cache
|
||||
*/
|
||||
protected $fallbackCache = [];
|
||||
|
||||
/**
|
||||
* Sets a CMS object to load the template from.
|
||||
*
|
||||
* @param \Cms\Contracts\CmsObject $obj Specifies the CMS object.
|
||||
* @return void
|
||||
*/
|
||||
public function setObject(CmsObject $obj)
|
||||
{
|
||||
$this->obj = $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Twig content string.
|
||||
* This step is cached internally by Twig.
|
||||
*/
|
||||
public function getSourceContext(string $name): TwigSource
|
||||
{
|
||||
if (!$this->validateCmsObject($name)) {
|
||||
return parent::getSourceContext($name);
|
||||
}
|
||||
|
||||
$content = $this->obj->getTwigContent();
|
||||
|
||||
/**
|
||||
* @event cms.template.processTwigContent
|
||||
* Provides an opportunity to modify Twig content before being processed by Twig. `$dataHolder` = {content: $twigContent}
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* Event::listen('cms.template.processTwigContent', function ((\Cms\Classes\CmsObject) $thisObject, (object) $dataHolder) {
|
||||
* $dataHolder->content = "NO CONTENT FOR YOU!";
|
||||
* });
|
||||
*
|
||||
*/
|
||||
$dataHolder = (object) ['content' => $content];
|
||||
Event::fire('cms.template.processTwigContent', [$this->obj, $dataHolder]);
|
||||
|
||||
return new TwigSource((string) $dataHolder->content, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Twig cache key.
|
||||
*/
|
||||
public function getCacheKey(string $name): string
|
||||
{
|
||||
if (!$this->validateCmsObject($name)) {
|
||||
return parent::getCacheKey($name);
|
||||
}
|
||||
|
||||
return $this->obj->getTwigCacheKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the content is fresh.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param mixed $time The time to check against the template
|
||||
* @return bool
|
||||
*/
|
||||
public function isFresh(string $name, int $time): bool
|
||||
{
|
||||
if (!$this->validateCmsObject($name)) {
|
||||
return parent::isFresh($name, $time);
|
||||
}
|
||||
|
||||
return $this->obj->mtime <= $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name of the loaded template.
|
||||
*/
|
||||
public function getFilename(string $name): string
|
||||
{
|
||||
if (!$this->validateCmsObject($name)) {
|
||||
return parent::getFilename($name);
|
||||
}
|
||||
|
||||
return $this->obj->getFilePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the template exists.
|
||||
*/
|
||||
public function exists(string $name): bool
|
||||
{
|
||||
if (!$this->validateCmsObject($name)) {
|
||||
return parent::exists($name);
|
||||
}
|
||||
|
||||
return $this->obj->exists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that checks if the template name matches
|
||||
* the loaded object, with fallback support to partials.
|
||||
*/
|
||||
protected function validateCmsObject(string $name): bool
|
||||
{
|
||||
if ($this->obj && $name === $this->obj->getFilePath()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($fallbackObj = $this->findFallbackObject($name)) {
|
||||
$this->obj = $fallbackObj;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a fallback CMS partial object.
|
||||
*
|
||||
* @param string $name The filename to attempt to load a fallback CMS partial for
|
||||
* @return Cms\Classes\Partial|bool Returns false if a CMS partial can't be found
|
||||
*/
|
||||
protected function findFallbackObject($name)
|
||||
{
|
||||
// Ignore Laravel views
|
||||
if (strpos($name, '::') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the cache
|
||||
if (array_key_exists($name, $this->fallbackCache)) {
|
||||
return $this->fallbackCache[$name];
|
||||
}
|
||||
|
||||
// Attempt to load the path as a CMS Partial object
|
||||
try {
|
||||
$partial = CmsPartial::find($name);
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->fallbackCache[$name] = $partial;
|
||||
}
|
||||
}
|
||||
31
modules/cms/twig/PageNode.php
Normal file
31
modules/cms/twig/PageNode.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a page node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PageNode extends TwigNode
|
||||
{
|
||||
public function __construct($lineno, $tag = 'page')
|
||||
{
|
||||
parent::__construct([], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->pageFunction();\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
38
modules/cms/twig/PageTokenParser.php
Normal file
38
modules/cms/twig/PageTokenParser.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% page %}` Twig tag.
|
||||
*
|
||||
* {% page %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PageTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return Twig\Node\Node A Twig\Node\Node instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
return new PageNode($token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'page';
|
||||
}
|
||||
}
|
||||
46
modules/cms/twig/PartialNode.php
Normal file
46
modules/cms/twig/PartialNode.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a partial node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PartialNode extends TwigNode
|
||||
{
|
||||
public function __construct(TwigNode $nodes, $paramNames, $lineno, $tag = 'partial')
|
||||
{
|
||||
parent::__construct(['nodes' => $nodes], ['names' => $paramNames], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$compiler->write("\$context['__cms_partial_params'] = [];\n");
|
||||
|
||||
for ($i = 1; $i < count($this->getNode('nodes')); $i++) {
|
||||
$compiler->write("\$context['__cms_partial_params']['".$this->getAttribute('names')[$i-1]."'] = ");
|
||||
$compiler->subcompile($this->getNode('nodes')->getNode($i));
|
||||
$compiler->write(";\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->partialFunction(")
|
||||
->subcompile($this->getNode('nodes')->getNode(0))
|
||||
->write(", \$context['__cms_partial_params']")
|
||||
->write(", true")
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
$compiler->write("unset(\$context['__cms_partial_params']);\n");
|
||||
}
|
||||
}
|
||||
74
modules/cms/twig/PartialTokenParser.php
Normal file
74
modules/cms/twig/PartialTokenParser.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
use Twig\Error\SyntaxError as TwigErrorSyntax;
|
||||
|
||||
/**
|
||||
* Parser for the `{% partial %}` Twig tag.
|
||||
*
|
||||
* {% partial "sidebar" %}
|
||||
*
|
||||
* {% partial "sidebar" name='John' %}
|
||||
*
|
||||
* {% partial "sidebar" name='John', year=2013 %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PartialTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return TwigNode A TwigNode instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$name = $this->parser->getExpressionParser()->parseExpression();
|
||||
$paramNames = [];
|
||||
$nodes = [$name];
|
||||
|
||||
$end = false;
|
||||
while (!$end) {
|
||||
$current = $stream->next();
|
||||
|
||||
switch ($current->getType()) {
|
||||
case TwigToken::NAME_TYPE:
|
||||
$paramNames[] = $current->getValue();
|
||||
$stream->expect(TwigToken::OPERATOR_TYPE, '=');
|
||||
$nodes[] = $this->parser->getExpressionParser()->parseExpression();
|
||||
break;
|
||||
|
||||
case TwigToken::BLOCK_END_TYPE:
|
||||
$end = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new TwigErrorSyntax(
|
||||
sprintf('Invalid syntax in the partial tag. Line %s', $lineno),
|
||||
$stream->getCurrent()->getLine(),
|
||||
$stream->getSourceContext()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new PartialNode(new TwigNode($nodes), $paramNames, $token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'partial';
|
||||
}
|
||||
}
|
||||
83
modules/cms/twig/PlaceholderNode.php
Normal file
83
modules/cms/twig/PlaceholderNode.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a placeholder node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PlaceholderNode extends TwigNode
|
||||
{
|
||||
public function __construct($name, $paramValues, $body, $lineno, $tag = 'placeholder')
|
||||
{
|
||||
$nodes = [];
|
||||
|
||||
if ($body) {
|
||||
$nodes['default'] = $body;
|
||||
}
|
||||
|
||||
$attributes = $paramValues;
|
||||
$attributes['name'] = $name;
|
||||
|
||||
parent::__construct($nodes, $attributes, $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$hasBody = $this->hasNode('default');
|
||||
$varId = '__placeholder_'.$this->getAttribute('name').'_default_contents';
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$context[")
|
||||
->raw("'".$varId."'")
|
||||
->raw("] = null;");
|
||||
|
||||
if ($hasBody) {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('ob_start();')
|
||||
->subcompile($this->getNode('default'))
|
||||
->write("\$context[")
|
||||
->raw("'".$varId."'")
|
||||
->raw("] = ob_get_clean();");
|
||||
}
|
||||
|
||||
$isText = $this->hasAttribute('type') && $this->getAttribute('type') == 'text';
|
||||
|
||||
$compiler->addDebugInfo($this);
|
||||
if (!$isText) {
|
||||
$compiler->write("echo \$this->env->getExtension('Cms\Twig\Extension')->displayBlock(");
|
||||
}
|
||||
else {
|
||||
$compiler->write("echo twig_escape_filter(\$this->env, \$this->env->getExtension('Cms\Twig\Extension')->displayBlock(");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw("'".$this->getAttribute('name')."', ")
|
||||
->raw("\$context[")
|
||||
->raw("'".$varId."'")
|
||||
->raw("]")
|
||||
->raw(")");
|
||||
|
||||
if (!$isText) {
|
||||
$compiler->raw(";\n");
|
||||
}
|
||||
else {
|
||||
$compiler->raw(");\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("unset(\$context[")
|
||||
->raw("'".$varId."'")
|
||||
->raw("]);");
|
||||
}
|
||||
}
|
||||
98
modules/cms/twig/PlaceholderTokenParser.php
Normal file
98
modules/cms/twig/PlaceholderTokenParser.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
use Twig\Error\SyntaxError as TwigErrorSyntax;
|
||||
|
||||
/**
|
||||
* Parser for the `{% placeholder %}` Twig tag.
|
||||
*
|
||||
* {% placeholder head %}
|
||||
*
|
||||
* or - use default placeholder content
|
||||
*
|
||||
* {% placeholder head %}
|
||||
* <link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet"/>
|
||||
* {% endshowblock %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PlaceholderTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return TwigNode A TwigNode instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$name = $stream->expect(TwigToken::NAME_TYPE)->getValue();
|
||||
$body = null;
|
||||
$params = [];
|
||||
|
||||
if ($stream->test(TwigToken::NAME_TYPE, 'default')) {
|
||||
$stream->next();
|
||||
$params = $this->loadParams($stream);
|
||||
|
||||
$body = $this->parser->subparse([$this, 'decidePlaceholderEnd'], true);
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
}
|
||||
else {
|
||||
$params = $this->loadParams($stream);
|
||||
}
|
||||
|
||||
return new PlaceholderNode($name, $params, $body, $token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
public function decidePlaceholderEnd(TwigToken $token)
|
||||
{
|
||||
return $token->test('endplaceholder');
|
||||
}
|
||||
|
||||
protected function loadParams($stream)
|
||||
{
|
||||
$params = [];
|
||||
|
||||
$end = false;
|
||||
while (!$end) {
|
||||
$current = $stream->next();
|
||||
|
||||
switch ($current->getType()) {
|
||||
case TwigToken::NAME_TYPE:
|
||||
$paramName = $current->getValue();
|
||||
$stream->expect(TwigToken::OPERATOR_TYPE, '=');
|
||||
$current = $stream->next();
|
||||
$params[$paramName] = $current->getValue();
|
||||
break;
|
||||
|
||||
case TwigToken::BLOCK_END_TYPE:
|
||||
$end = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new TwigErrorSyntax(
|
||||
sprintf('Invalid syntax in the placeholder tag. Line %s', $stream->getCurrent()->getLine()),
|
||||
$stream->getCurrent()->getLine(),
|
||||
$stream->getSourceContext()
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'placeholder';
|
||||
}
|
||||
}
|
||||
44
modules/cms/twig/PutNode.php
Normal file
44
modules/cms/twig/PutNode.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a put node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PutNode extends TwigNode
|
||||
{
|
||||
public function __construct(TwigNode $body, $name, $endType, $lineno, $tag = 'put')
|
||||
{
|
||||
parent::__construct(['body' => $body], ['name' => $name, 'endType' => $endType], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->startBlock(")
|
||||
->raw("'".$this->getAttribute('name')."'")
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
$isOverwrite = strtolower($this->getAttribute('endType')) == 'overwrite';
|
||||
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->endBlock(")
|
||||
->raw($isOverwrite ? 'false' : 'true')
|
||||
->write(");\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
63
modules/cms/twig/PutTokenParser.php
Normal file
63
modules/cms/twig/PutTokenParser.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% put %}` Twig tag.
|
||||
*
|
||||
* {% put head %}
|
||||
* <link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet"/>
|
||||
* {% endput %}
|
||||
*
|
||||
* or
|
||||
*
|
||||
* {% put head %}
|
||||
* <link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.css" rel="stylesheet"/>
|
||||
* {% default %}
|
||||
* {% endput %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PutTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return Twig\Node\Node A Twig\Node\Node instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
$name = $stream->expect(TwigToken::NAME_TYPE)->getValue();
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
$body = $this->parser->subparse([$this, 'decidePutEnd'], true);
|
||||
|
||||
$endType = null;
|
||||
if ($token = $stream->nextIf(TwigToken::NAME_TYPE)) {
|
||||
$endType = $token->getValue();
|
||||
}
|
||||
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
|
||||
return new PutNode($body, $name, $endType, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
public function decidePutEnd(TwigToken $token)
|
||||
{
|
||||
return $token->test('endput');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'put';
|
||||
}
|
||||
}
|
||||
32
modules/cms/twig/ScriptsNode.php
Normal file
32
modules/cms/twig/ScriptsNode.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a "scripts" node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ScriptsNode extends TwigNode
|
||||
{
|
||||
public function __construct($lineno, $tag = 'scripts')
|
||||
{
|
||||
parent::__construct([], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->assetsFunction('js');\n")
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->displayBlock('scripts');\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
38
modules/cms/twig/ScriptsTokenParser.php
Normal file
38
modules/cms/twig/ScriptsTokenParser.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% scripts %}` Twig tag.
|
||||
*
|
||||
* {% scripts %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class ScriptsTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return Twig\Node\Node A Twig\Node\Node instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
return new ScriptsNode($token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'scripts';
|
||||
}
|
||||
}
|
||||
79
modules/cms/twig/SnowboardNode.php
Normal file
79
modules/cms/twig/SnowboardNode.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Config;
|
||||
use System\Classes\CombineAssets;
|
||||
use System\Models\Parameter;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Url;
|
||||
|
||||
/**
|
||||
* Represents a "snowboard" node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Winter CMS
|
||||
*/
|
||||
class SnowboardNode extends TwigNode
|
||||
{
|
||||
/**
|
||||
* @var bool Indicates if the base Snowboard framework is already loaded, in case of multiple uses of this tag.
|
||||
*/
|
||||
public static $baseLoaded = false;
|
||||
|
||||
public function __construct(array $modules, $lineno, $tag = 'snowboard')
|
||||
{
|
||||
parent::__construct([], ['modules' => $modules], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$build = Parameter::get('system::core.build', 'winter');
|
||||
$cacheBust = '?v=' . $build;
|
||||
$modules = $this->getAttribute('modules');
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$_minify = ".CombineAssets::class."::instance()->useMinify;" . PHP_EOL);
|
||||
|
||||
$moduleMap = [
|
||||
'base' => (Config::get('develop.debugSnowboard', false) === true)
|
||||
? 'snowboard.base.debug'
|
||||
: 'snowboard.base',
|
||||
'vendor' => 'snowboard.vendor',
|
||||
'request' => 'snowboard.request',
|
||||
'attr' => 'snowboard.data-attr',
|
||||
'extras' => 'snowboard.extras',
|
||||
];
|
||||
$manifestPath = Url::asset('/modules/system/assets/js/build/manifest.js');
|
||||
$basePath = Url::asset('/modules/system/assets/js/snowboard/build') . '/';
|
||||
|
||||
if (!static::$baseLoaded) {
|
||||
// Add manifest and vendor files
|
||||
$compiler
|
||||
->write("echo '<script data-module=\"snowboard-manifest\" src=\"{$manifestPath}{$cacheBust}\"></script>'.PHP_EOL;" . PHP_EOL);
|
||||
$vendorJs = $moduleMap['vendor'];
|
||||
$compiler
|
||||
->write("echo '<script data-module=\"snowboard-vendor\" src=\"{$basePath}{$vendorJs}.js{$cacheBust}\"></script>'.PHP_EOL;" . PHP_EOL);
|
||||
|
||||
// Add base script
|
||||
$baseJs = $moduleMap['base'];
|
||||
$baseUrl = Url::to('/');
|
||||
$assetUrl = Url::asset('/');
|
||||
$compiler
|
||||
->write("echo '<script data-module=\"snowboard-base\" data-base-url=\"{$baseUrl}\" data-asset-url=\"{$assetUrl}\" src=\"{$basePath}{$baseJs}.js{$cacheBust}\"></script>'.PHP_EOL;" . PHP_EOL);
|
||||
|
||||
static::$baseLoaded = true;
|
||||
}
|
||||
|
||||
foreach ($modules as $module) {
|
||||
$moduleJs = $moduleMap[$module];
|
||||
$compiler
|
||||
->write("echo '<script data-module=\"{$module}\" src=\"{$basePath}{$moduleJs}.js{$cacheBust}\"></script>'.PHP_EOL;" . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
modules/cms/twig/SnowboardTokenParser.php
Normal file
60
modules/cms/twig/SnowboardTokenParser.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% snowboard %}` Twig tag.
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Winter CMS
|
||||
*/
|
||||
class SnowboardTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$modules = [];
|
||||
|
||||
do {
|
||||
$token = $stream->next();
|
||||
|
||||
if ($token->getType() === TwigToken::NAME_TYPE) {
|
||||
$modules[] = $token->getValue();
|
||||
}
|
||||
} while ($token->getType() !== TwigToken::BLOCK_END_TYPE);
|
||||
|
||||
// Filter out invalid types
|
||||
$modules = array_filter(
|
||||
array_map(function ($item) {
|
||||
return strtolower($item);
|
||||
}, $modules),
|
||||
function ($item) {
|
||||
return in_array($item, ['request', 'attr', 'extras', 'all']);
|
||||
}
|
||||
);
|
||||
|
||||
if (in_array('all', $modules)) {
|
||||
$modules = [
|
||||
'request',
|
||||
'attr',
|
||||
'extras',
|
||||
];
|
||||
}
|
||||
|
||||
return new SnowboardNode($modules, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'snowboard';
|
||||
}
|
||||
}
|
||||
32
modules/cms/twig/StylesNode.php
Normal file
32
modules/cms/twig/StylesNode.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Node\Node as TwigNode;
|
||||
use Twig\Compiler as TwigCompiler;
|
||||
|
||||
/**
|
||||
* Represents a "styles" node
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class StylesNode extends TwigNode
|
||||
{
|
||||
public function __construct($lineno, $tag = 'styles')
|
||||
{
|
||||
parent::__construct([], [], $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param TwigCompiler $compiler A TwigCompiler instance
|
||||
*/
|
||||
public function compile(TwigCompiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->assetsFunction('css');\n")
|
||||
->write("echo \$this->env->getExtension('Cms\Twig\Extension')->displayBlock('styles');\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
38
modules/cms/twig/StylesTokenParser.php
Normal file
38
modules/cms/twig/StylesTokenParser.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php namespace Cms\Twig;
|
||||
|
||||
use Twig\Token as TwigToken;
|
||||
use Twig\TokenParser\AbstractTokenParser as TwigTokenParser;
|
||||
|
||||
/**
|
||||
* Parser for the `{% styles %}` Twig tag.
|
||||
*
|
||||
* {% styles %}
|
||||
*
|
||||
* @package winter\wn-cms-module
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class StylesTokenParser extends TwigTokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param TwigToken $token A TwigToken instance
|
||||
* @return Twig\Node\Node A Twig\Node\Node instance
|
||||
*/
|
||||
public function parse(TwigToken $token)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(TwigToken::BLOCK_END_TYPE);
|
||||
return new StylesNode($token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag name associated with this token parser.
|
||||
*
|
||||
* @return string The tag name
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return 'styles';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user