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 BackendMenu;
|
||||
use BackendAuth;
|
||||
use System\Classes\MarkupManager;
|
||||
use Backend\Classes\WidgetManager;
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use URL;
|
|||
use File;
|
||||
use Lang;
|
||||
use Cache;
|
||||
use Route;
|
||||
use Config;
|
||||
use Request;
|
||||
use Response;
|
||||
|
|
@ -193,7 +194,13 @@ class CombineAssets
|
|||
*/
|
||||
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_Loader_String;
|
||||
use System\Classes\ErrorHandler;
|
||||
use System\Classes\MarkupManager;
|
||||
use System\Classes\PluginManager;
|
||||
use System\Classes\SettingsManager;
|
||||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
<?php namespace System\Classes;
|
||||
|
||||
use Str;
|
||||
use Twig_TokenParser;
|
||||
use Twig_SimpleFilter;
|
||||
use Twig_SimpleFunction;
|
||||
use System\Classes\ApplicationException;
|
||||
use System\Classes\PluginManager;
|
||||
|
||||
/**
|
||||
|
|
@ -189,4 +194,126 @@ class MarkupManager
|
|||
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
|
||||
*/
|
||||
foreach ($this->markupManager->listFunctions() as $name => $callable) {
|
||||
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']]);
|
||||
}
|
||||
$functions = $this->markupManager->makeTwigFunctions($functions);
|
||||
|
||||
return $functions;
|
||||
}
|
||||
|
|
@ -76,12 +71,7 @@ class Extension extends Twig_Extension
|
|||
/*
|
||||
* Include extensions provided by plugins
|
||||
*/
|
||||
foreach ($this->markupManager->listFilters() as $name => $callable) {
|
||||
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']]);
|
||||
}
|
||||
$filters = $this->markupManager->makeTwigFilters($filters);
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
|
@ -95,13 +85,10 @@ class Extension extends Twig_Extension
|
|||
{
|
||||
$parsers = [];
|
||||
|
||||
$extraParsers = $this->markupManager->listTokenParsers();
|
||||
foreach ($extraParsers as $obj) {
|
||||
if (!$obj instanceof Twig_TokenParser)
|
||||
continue;
|
||||
|
||||
$parsers[] = $obj;
|
||||
}
|
||||
/*
|
||||
* Include extensions provided by plugins
|
||||
*/
|
||||
$parsers = $this->markupManager->makeTwigTokenParsers($parsers);
|
||||
|
||||
return $parsers;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use Cms\Classes\Theme;
|
|||
|
||||
class ControllerTest extends TestCase
|
||||
{
|
||||
public function tearDown() {
|
||||
public function tearDown()
|
||||
{
|
||||
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