Fixes #254 add Laravel helper functions to Twig environment
This commit is contained in:
parent
54cf043dea
commit
3c7c87b338
|
|
@ -4,7 +4,6 @@ use Lang;
|
||||||
use Backend;
|
use Backend;
|
||||||
use BackendMenu;
|
use BackendMenu;
|
||||||
use BackendAuth;
|
use BackendAuth;
|
||||||
use System\Classes\MarkupManager;
|
|
||||||
use Backend\Classes\WidgetManager;
|
use Backend\Classes\WidgetManager;
|
||||||
use October\Rain\Support\ModuleServiceProvider;
|
use October\Rain\Support\ModuleServiceProvider;
|
||||||
|
|
||||||
|
|
@ -93,48 +92,6 @@ class ServiceProvider extends ModuleServiceProvider
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
|
||||||
* Register markup tags
|
|
||||||
*/
|
|
||||||
MarkupManager::instance()->registerCallback(function($manager){
|
|
||||||
$manager->registerFunctions([
|
|
||||||
// Global helpers
|
|
||||||
'post' => 'post',
|
|
||||||
|
|
||||||
// Form helpers
|
|
||||||
'form_ajax' => ['Form', 'ajax'],
|
|
||||||
'form_open' => ['Form', 'open'],
|
|
||||||
'form_close' => ['Form', 'close'],
|
|
||||||
'form_token' => ['Form', 'token'],
|
|
||||||
'form_session_key' => ['Form', 'sessionKey'],
|
|
||||||
'form_token' => ['Form', 'token'],
|
|
||||||
'form_model' => ['Form', 'model'],
|
|
||||||
'form_label' => ['Form', 'label'],
|
|
||||||
'form_text' => ['Form', 'text'],
|
|
||||||
'form_password' => ['Form', 'password'],
|
|
||||||
'form_checkbox' => ['Form', 'checkbox'],
|
|
||||||
'form_radio' => ['Form', 'radio'],
|
|
||||||
'form_file' => ['Form', 'file'],
|
|
||||||
'form_select' => ['Form', 'select'],
|
|
||||||
'form_select_range' => ['Form', 'selectRange'],
|
|
||||||
'form_select_month' => ['Form', 'selectMonth'],
|
|
||||||
'form_submit' => ['Form', 'submit'],
|
|
||||||
'form_macro' => ['Form', '__call'],
|
|
||||||
'form_value' => ['Form', 'value'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$manager->registerFilters([
|
|
||||||
// String helpers
|
|
||||||
'slug' => ['Str', 'slug'],
|
|
||||||
'plural' => ['Str', 'plural'],
|
|
||||||
'singular' => ['Str', 'singular'],
|
|
||||||
'finish' => ['Str', 'finish'],
|
|
||||||
'snake' => ['Str', 'snake'],
|
|
||||||
'camel' => ['Str', 'camel'],
|
|
||||||
'studly' => ['Str', 'studly'],
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register widgets
|
* Register widgets
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ use URL;
|
||||||
use File;
|
use File;
|
||||||
use Lang;
|
use Lang;
|
||||||
use Cache;
|
use Cache;
|
||||||
|
use Route;
|
||||||
use Config;
|
use Config;
|
||||||
use Request;
|
use Request;
|
||||||
use Response;
|
use Response;
|
||||||
|
|
@ -193,7 +194,13 @@ class CombineAssets
|
||||||
*/
|
*/
|
||||||
protected function getCombinedUrl($outputFilename = 'undefined.css')
|
protected function getCombinedUrl($outputFilename = 'undefined.css')
|
||||||
{
|
{
|
||||||
return URL::action('Cms\Classes\Controller@combine', [$outputFilename], false);
|
$combineAction = 'Cms\Classes\Controller@combine';
|
||||||
|
$actionExists = Route::getRoutes()->getByAction($combineAction) !== null;
|
||||||
|
|
||||||
|
if ($actionExists)
|
||||||
|
return URL::action($combineAction, [$outputFilename], false);
|
||||||
|
else
|
||||||
|
return Request::getBasePath().'/combine/'.$outputFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use BackendAuth;
|
||||||
use Twig_Environment;
|
use Twig_Environment;
|
||||||
use Twig_Loader_String;
|
use Twig_Loader_String;
|
||||||
use System\Classes\ErrorHandler;
|
use System\Classes\ErrorHandler;
|
||||||
|
use System\Classes\MarkupManager;
|
||||||
use System\Classes\PluginManager;
|
use System\Classes\PluginManager;
|
||||||
use System\Classes\SettingsManager;
|
use System\Classes\SettingsManager;
|
||||||
use System\Twig\Engine as TwigEngine;
|
use System\Twig\Engine as TwigEngine;
|
||||||
|
|
@ -179,6 +180,44 @@ class ServiceProvider extends ModuleServiceProvider
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register markup tags
|
||||||
|
*/
|
||||||
|
MarkupManager::instance()->registerCallback(function($manager){
|
||||||
|
$manager->registerFunctions([
|
||||||
|
// Functions
|
||||||
|
'post' => 'post',
|
||||||
|
'link_to' => 'link_to',
|
||||||
|
'link_to_asset' => 'link_to_asset',
|
||||||
|
'link_to_route' => 'link_to_route',
|
||||||
|
'link_to_action' => 'link_to_action',
|
||||||
|
'asset' => 'asset',
|
||||||
|
'action' => 'action',
|
||||||
|
'url' => 'url',
|
||||||
|
'route' => 'route',
|
||||||
|
'secure_url' => 'secure_url',
|
||||||
|
'secure_asset' => 'secure_asset',
|
||||||
|
|
||||||
|
// Classes
|
||||||
|
'str_*' => ['Str', '*'],
|
||||||
|
'url_*' => ['URL', '*'],
|
||||||
|
'html_*' => ['HTML', '*'],
|
||||||
|
'form_*' => ['Form', '*'],
|
||||||
|
'form_macro' => ['Form', '__call'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$manager->registerFilters([
|
||||||
|
// Classes
|
||||||
|
'slug' => ['Str', 'slug'],
|
||||||
|
'plural' => ['Str', 'plural'],
|
||||||
|
'singular' => ['Str', 'singular'],
|
||||||
|
'finish' => ['Str', 'finish'],
|
||||||
|
'snake' => ['Str', 'snake'],
|
||||||
|
'camel' => ['Str', 'camel'],
|
||||||
|
'studly' => ['Str', 'studly'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register settings
|
* Register settings
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,10 @@
|
||||||
<?php namespace System\Classes;
|
<?php namespace System\Classes;
|
||||||
|
|
||||||
|
use Str;
|
||||||
|
use Twig_TokenParser;
|
||||||
|
use Twig_SimpleFilter;
|
||||||
|
use Twig_SimpleFunction;
|
||||||
|
use System\Classes\ApplicationException;
|
||||||
use System\Classes\PluginManager;
|
use System\Classes\PluginManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -189,4 +194,126 @@ class MarkupManager
|
||||||
return $this->listExtensions(self::EXTENSION_TOKEN_PARSER);
|
return $this->listExtensions(self::EXTENSION_TOKEN_PARSER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a set of Twig functions for use in a twig extension.
|
||||||
|
* @param array $functions Current collection
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function makeTwigFunctions($functions = [])
|
||||||
|
{
|
||||||
|
if (!is_array($functions))
|
||||||
|
$functions = [];
|
||||||
|
|
||||||
|
foreach ($this->listFunctions() as $name => $callable) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle a wildcard function
|
||||||
|
*/
|
||||||
|
if (strpos($name, '*') !== false && $this->isWildCallable($callable)) {
|
||||||
|
$callable = function($name) use ($callable) {
|
||||||
|
$arguments = array_slice(func_get_args(), 1);
|
||||||
|
$method = $this->isWildCallable($callable, Str::camel($name));
|
||||||
|
return call_user_func_array($method, $arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_callable($callable))
|
||||||
|
throw new ApplicationException(sprintf('The markup function for %s is not callable.', $name));
|
||||||
|
|
||||||
|
$functions[] = new Twig_SimpleFunction($name, $callable, ['is_safe' => ['html']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a set of Twig filters for use in a twig extension.
|
||||||
|
* @param array $filters Current collection
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function makeTwigFilters($filters = [])
|
||||||
|
{
|
||||||
|
if (!is_array($filters))
|
||||||
|
$filters = [];
|
||||||
|
|
||||||
|
foreach ($this->listFilters() as $name => $callable) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle a wildcard function
|
||||||
|
*/
|
||||||
|
if (strpos($name, '*') !== false && $this->isWildCallable($callable)) {
|
||||||
|
$callable = function($name) use ($callable) {
|
||||||
|
$arguments = array_slice(func_get_args(), 1);
|
||||||
|
$method = $this->isWildCallable($callable, Str::camel($name));
|
||||||
|
return call_user_func_array($method, $arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_callable($callable))
|
||||||
|
throw new ApplicationException(sprintf('The markup filter for %s is not callable.', $name));
|
||||||
|
|
||||||
|
$filters[] = new Twig_SimpleFilter($name, $callable, ['is_safe' => ['html']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes a set of Twig token parsers for use in a twig extension.
|
||||||
|
* @param array $parsers Current collection
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function makeTwigTokenParsers($parsers = [])
|
||||||
|
{
|
||||||
|
if (!is_array($parsers))
|
||||||
|
$parsers = [];
|
||||||
|
|
||||||
|
$extraParsers = $this->listTokenParsers();
|
||||||
|
foreach ($extraParsers as $obj) {
|
||||||
|
if (!$obj instanceof Twig_TokenParser)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$parsers[] = $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if a callable type contains a wildcard, also acts as a
|
||||||
|
* utility to replace the wildcard with a string.
|
||||||
|
* @param callable $callable
|
||||||
|
* @param string $replaceWith
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
protected function isWildCallable($callable, $replaceWith = false)
|
||||||
|
{
|
||||||
|
$isWild = false;
|
||||||
|
|
||||||
|
if (is_string($callable) && strpos($callable, '*') !== false)
|
||||||
|
$isWild = $replaceWith ? str_replace('*', $replaceWith, $callable) : true;
|
||||||
|
|
||||||
|
if (is_array($callable)) {
|
||||||
|
if (is_string($callable[0]) && strpos($callable[0], '*') !== false) {
|
||||||
|
if ($replaceWith) {
|
||||||
|
$isWild = $callable;
|
||||||
|
$isWild[0] = str_replace('*', $replaceWith, $callable[0]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$isWild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($callable[1]) && strpos($callable[1], '*') !== false) {
|
||||||
|
if ($replaceWith) {
|
||||||
|
$isWild = $isWild ?: $callable;
|
||||||
|
$isWild[1] = str_replace('*', $replaceWith, $callable[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
$isWild = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $isWild;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -52,12 +52,7 @@ class Extension extends Twig_Extension
|
||||||
/*
|
/*
|
||||||
* Include extensions provided by plugins
|
* Include extensions provided by plugins
|
||||||
*/
|
*/
|
||||||
foreach ($this->markupManager->listFunctions() as $name => $callable) {
|
$functions = $this->markupManager->makeTwigFunctions($functions);
|
||||||
if (!is_callable($callable))
|
|
||||||
throw new ApplicationException(sprintf('The markup function for %s is not callable.', $name));
|
|
||||||
|
|
||||||
$functions[] = new Twig_SimpleFunction($name, $callable, ['is_safe' => ['html']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $functions;
|
return $functions;
|
||||||
}
|
}
|
||||||
|
|
@ -76,12 +71,7 @@ class Extension extends Twig_Extension
|
||||||
/*
|
/*
|
||||||
* Include extensions provided by plugins
|
* Include extensions provided by plugins
|
||||||
*/
|
*/
|
||||||
foreach ($this->markupManager->listFilters() as $name => $callable) {
|
$filters = $this->markupManager->makeTwigFilters($filters);
|
||||||
if (!is_callable($callable))
|
|
||||||
throw new ApplicationException(sprintf('The markup filter for %s is not callable.', $name));
|
|
||||||
|
|
||||||
$filters[] = new Twig_SimpleFilter($name, $callable, ['is_safe' => ['html']]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $filters;
|
return $filters;
|
||||||
}
|
}
|
||||||
|
|
@ -95,13 +85,10 @@ class Extension extends Twig_Extension
|
||||||
{
|
{
|
||||||
$parsers = [];
|
$parsers = [];
|
||||||
|
|
||||||
$extraParsers = $this->markupManager->listTokenParsers();
|
/*
|
||||||
foreach ($extraParsers as $obj) {
|
* Include extensions provided by plugins
|
||||||
if (!$obj instanceof Twig_TokenParser)
|
*/
|
||||||
continue;
|
$parsers = $this->markupManager->makeTwigTokenParsers($parsers);
|
||||||
|
|
||||||
$parsers[] = $obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $parsers;
|
return $parsers;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,8 @@ use Cms\Classes\Theme;
|
||||||
|
|
||||||
class ControllerTest extends TestCase
|
class ControllerTest extends TestCase
|
||||||
{
|
{
|
||||||
public function tearDown() {
|
public function tearDown()
|
||||||
|
{
|
||||||
Mockery::close();
|
Mockery::close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use System\Classes\MarkupManager;
|
||||||
|
|
||||||
|
class MarkupManagerTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
include_once base_path().'/tests/fixtures/system/plugins/october/test/Plugin.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helpers
|
||||||
|
//
|
||||||
|
|
||||||
|
protected static function callProtectedMethod($object, $name, $params = [])
|
||||||
|
{
|
||||||
|
$className = get_class($object);
|
||||||
|
$class = new ReflectionClass($className);
|
||||||
|
$method = $class->getMethod($name);
|
||||||
|
$method->setAccessible(true);
|
||||||
|
return $method->invokeArgs($object, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getProtectedProperty($object, $name)
|
||||||
|
{
|
||||||
|
$className = get_class($object);
|
||||||
|
$class = new ReflectionClass($className);
|
||||||
|
$property = $class->getProperty($name);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
return $property->getValue($object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function setProtectedProperty($object, $name, $value)
|
||||||
|
{
|
||||||
|
$className = get_class($object);
|
||||||
|
$class = new ReflectionClass($className);
|
||||||
|
$property = $class->getProperty($name);
|
||||||
|
$property->setAccessible(true);
|
||||||
|
return $property->setValue($object, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Tests
|
||||||
|
//
|
||||||
|
|
||||||
|
public function testIsWildCallable()
|
||||||
|
{
|
||||||
|
$manager = MarkupManager::instance();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Negatives
|
||||||
|
*/
|
||||||
|
$callable = 'something';
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertFalse($result);
|
||||||
|
|
||||||
|
$callable = ['Form', 'open'];
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertFalse($result);
|
||||||
|
|
||||||
|
$callable = function() { return 'O, Hai!'; };
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertFalse($result);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* String
|
||||||
|
*/
|
||||||
|
$callable = 'something_*';
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'delicious']);
|
||||||
|
$this->assertEquals('something_delicious', $result);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Array
|
||||||
|
*/
|
||||||
|
$callable = ['Class', 'foo_*'];
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'bar']);
|
||||||
|
$this->assertTrue(isset($result[0]));
|
||||||
|
$this->assertTrue(isset($result[1]));
|
||||||
|
$this->assertEquals('Class', $result[0]);
|
||||||
|
$this->assertEquals('foo_bar', $result[1]);
|
||||||
|
|
||||||
|
$callable = ['My*', 'method'];
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Class']);
|
||||||
|
$this->assertTrue(isset($result[0]));
|
||||||
|
$this->assertTrue(isset($result[1]));
|
||||||
|
$this->assertEquals('MyClass', $result[0]);
|
||||||
|
$this->assertEquals('method', $result[1]);
|
||||||
|
|
||||||
|
$callable = ['My*', 'my*'];
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable]);
|
||||||
|
$this->assertTrue($result);
|
||||||
|
|
||||||
|
$result = self::callProtectedMethod($manager, 'isWildCallable', [$callable, 'Food']);
|
||||||
|
$this->assertTrue(isset($result[0]));
|
||||||
|
$this->assertTrue(isset($result[1]));
|
||||||
|
$this->assertEquals('MyFood', $result[0]);
|
||||||
|
$this->assertEquals('myFood', $result[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue