Added Plugin.asset support for HtmlHelper css(), script(), and image() with tests

This commit is contained in:
Tigran Gabrielyan 2012-02-07 20:14:56 -08:00
parent b6f99bc0b9
commit 7fe6a4d5ea
5 changed files with 238 additions and 24 deletions

View file

@ -149,6 +149,10 @@ class HtmlHelperTest extends CakeTestCase {
$this->Html->request = new CakeRequest(null, false); $this->Html->request = new CakeRequest(null, false);
$this->Html->request->webroot = ''; $this->Html->request->webroot = '';
App::build(array(
'plugins' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin'. DS)
));
Configure::write('Asset.timestamp', false); Configure::write('Asset.timestamp', false);
} }
@ -496,6 +500,12 @@ class HtmlHelperTest extends CakeTestCase {
$result = $this->Html->css('screen.css'); $result = $this->Html->css('screen.css');
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
CakePlugin::load('TestPlugin');
$result = $this->Html->css('TestPlugin.style', null, array('plugin' => false));
$expected['link']['href'] = 'preg:/.*css\/TestPlugin\.style\.css/';
$this->assertTags($result, $expected);
CakePlugin::unload('TestPlugin');
$result = $this->Html->css('my.css.library'); $result = $this->Html->css('my.css.library');
$expected['link']['href'] = 'preg:/.*css\/my\.css\.library\.css/'; $expected['link']['href'] = 'preg:/.*css\/my\.css\.library\.css/';
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
@ -541,6 +551,49 @@ class HtmlHelperTest extends CakeTestCase {
$this->assertNull($result); $this->assertNull($result);
} }
/**
* testPluginCssLink method
*
* @return void
*/
public function testPluginCssLink() {
Configure::write('Asset.filter.css', false);
CakePlugin::load('TestPlugin');
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected = array(
'link' => array('rel' => 'stylesheet', 'type' => 'text/css', 'href' => 'preg:/.*test_plugin\/css\/test_plugin_asset\.css/')
);
$this->assertTags($result, $expected);
$result = $this->Html->css('TestPlugin.test_plugin_asset.css');
$this->assertTags($result, $expected);
$result = $this->Html->css('TestPlugin.my.css.library');
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/my\.css\.library\.css/';
$this->assertTags($result, $expected);
$result = $this->Html->css('TestPlugin.test_plugin_asset.css?1234');
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/test_plugin_asset\.css\?1234/';
$this->assertTags($result, $expected);
Configure::write('Asset.filter.css', 'css.php');
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected['link']['href'] = 'preg:/.*test_plugin\/ccss\/test_plugin_asset\.css/';
$this->assertTags($result, $expected);
Configure::write('Asset.filter.css', false);
$result = explode("\n", trim($this->Html->css(array('TestPlugin.test_plugin_asset', 'TestPlugin.vendor.generic'))));
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/test_plugin_asset\.css/';
$this->assertTags($result[0], $expected);
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/vendor\.generic\.css/';
$this->assertTags($result[1], $expected);
$this->assertEquals(count($result), 2);
CakePlugin::unload('TestPlugin');
}
/** /**
* test use of css() and timestamping * test use of css() and timestamping
* *
@ -581,6 +634,50 @@ class HtmlHelperTest extends CakeTestCase {
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
} }
/**
* test use of css() and timestamping with plugin syntax
*
* @return void
*/
public function testPluginCssTimestamping() {
CakePlugin::load('TestPlugin');
Configure::write('debug', 2);
Configure::write('Asset.timestamp', true);
$expected = array(
'link' => array('rel' => 'stylesheet', 'type' => 'text/css', 'href' => '')
);
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/test_plugin_asset\.css\?[0-9]+/';
$this->assertTags($result, $expected);
Configure::write('debug', 0);
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/test_plugin_asset\.css/';
$this->assertTags($result, $expected);
Configure::write('Asset.timestamp', 'force');
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected['link']['href'] = 'preg:/.*test_plugin\/css\/test_plugin_asset\.css\?[0-9]+/';
$this->assertTags($result, $expected);
$this->Html->request->webroot = '/testing/';
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected['link']['href'] = 'preg:/\/testing\/test_plugin\/css\/test_plugin_asset\.css\?[0-9]+/';
$this->assertTags($result, $expected);
$this->Html->request->webroot = '/testing/longer/';
$result = $this->Html->css('TestPlugin.test_plugin_asset');
$expected['link']['href'] = 'preg:/\/testing\/longer\/test_plugin\/css\/test_plugin_asset\.css\?[0-9]+/';
$this->assertTags($result, $expected);
CakePlugin::unload('TestPlugin');
}
/** /**
* test timestamp enforcement for script tags. * test timestamp enforcement for script tags.
* *
@ -606,6 +703,37 @@ class HtmlHelperTest extends CakeTestCase {
Configure::write('Asset.timestamp', false); Configure::write('Asset.timestamp', false);
} }
/**
* test timestamp enforcement for script tags with plugin syntax.
*
* @return void
*/
public function testPluginScriptTimestamping() {
CakePlugin::load('TestPlugin');
$pluginPath = App::pluginPath('TestPlugin');
$pluginJsPath = $pluginPath . 'webroot/js';
$this->skipIf(!is_writable($pluginJsPath), $pluginJsPath . ' is not Writable, timestamp testing has been skipped.');
Configure::write('debug', 2);
Configure::write('Asset.timestamp', true);
touch($pluginJsPath . DS . '__cake_js_test.js');
$timestamp = substr(strtotime('now'), 0, 8);
$result = $this->Html->script('TestPlugin.__cake_js_test', array('inline' => true, 'once' => false));
$this->assertRegExp('/test_plugin\/js\/__cake_js_test.js\?' . $timestamp . '[0-9]{2}"/', $result, 'Timestamp value not found %s');
Configure::write('debug', 0);
Configure::write('Asset.timestamp', 'force');
$result = $this->Html->script('TestPlugin.__cake_js_test', array('inline' => true, 'once' => false));
$this->assertRegExp('/test_plugin\/js\/__cake_js_test.js\?' . $timestamp . '[0-9]{2}"/', $result, 'Timestamp value not found %s');
unlink($pluginJsPath . DS . '__cake_js_test.js');
Configure::write('Asset.timestamp', false);
CakePlugin::unload('TestPlugin');
}
/** /**
* test that scripts added with uses() are only ever included once. * test that scripts added with uses() are only ever included once.
* test script tag generation * test script tag generation
@ -675,6 +803,72 @@ class HtmlHelperTest extends CakeTestCase {
} }
/**
* test that plugin scripts added with uses() are only ever included once.
* test script tag generation with plugin syntax
*
* @return void
*/
public function testPluginScript() {
CakePlugin::load('TestPlugin');
$result = $this->Html->script('TestPlugin.foo');
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/foo.js')
);
$this->assertTags($result, $expected);
$result = $this->Html->script(array('TestPlugin.foobar', 'TestPlugin.bar'));
$expected = array(
array('script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/foobar.js')),
'/script',
array('script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/bar.js')),
'/script',
);
$this->assertTags($result, $expected);
$result = $this->Html->script('TestPlugin.jquery-1.3');
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/jquery-1.3.js')
);
$this->assertTags($result, $expected);
$result = $this->Html->script('TestPlugin.test.json');
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/test.json.js')
);
$this->assertTags($result, $expected);
$result = $this->Html->script('TestPlugin./jquery-1.3.2.js?someparam=foo');
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'test_plugin/jquery-1.3.2.js?someparam=foo')
);
$this->assertTags($result, $expected);
$result = $this->Html->script('TestPlugin.test.json.js?foo=bar');
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/test.json.js?foo=bar')
);
$this->assertTags($result, $expected);
$result = $this->Html->script('TestPlugin.foo');
$this->assertNull($result, 'Script returned upon duplicate inclusion %s');
$result = $this->Html->script(array('TestPlugin.foo', 'TestPlugin.bar', 'TestPlugin.baz'));
$this->assertNotRegExp('/test_plugin\/js\/foo.js/', $result);
$result = $this->Html->script('TestPlugin.foo', array('inline' => true, 'once' => false));
$this->assertNotNull($result);
$result = $this->Html->script('TestPlugin.jquery-1.3.2', array('defer' => true, 'encoding' => 'utf-8'));
$expected = array(
'script' => array('type' => 'text/javascript', 'src' => 'test_plugin/js/jquery-1.3.2.js', 'defer' => 'defer', 'encoding' => 'utf-8')
);
$this->assertTags($result, $expected);
CakePlugin::unload('TestPlugin');
}
/** /**
* test that script() works with blocks. * test that script() works with blocks.
* *

View file

@ -198,6 +198,10 @@ class HelperTest extends CakeTestCase {
ClassRegistry::addObject('HelperTestPost', new HelperTestPost()); ClassRegistry::addObject('HelperTestPost', new HelperTestPost());
ClassRegistry::addObject('HelperTestComment', new HelperTestComment()); ClassRegistry::addObject('HelperTestComment', new HelperTestComment());
ClassRegistry::addObject('HelperTestTag', new HelperTestTag()); ClassRegistry::addObject('HelperTestTag', new HelperTestTag());
App::build(array(
'plugins' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS),
));
} }
/** /**
@ -613,6 +617,19 @@ class HelperTest extends CakeTestCase {
$result = $this->Helper->assetUrl('foo.jpg', array('fullBase' => true)); $result = $this->Helper->assetUrl('foo.jpg', array('fullBase' => true));
$this->assertEquals('http://localhost/foo.jpg', $result); $this->assertEquals('http://localhost/foo.jpg', $result);
$result = $this->Helper->assetUrl('style', array('ext' => '.css'));
$this->assertEqual('style.css', $result);
CakePlugin::load('TestPlugin');
$result = $this->Helper->assetUrl('TestPlugin.style', array('ext' => '.css'));
$this->assertEqual('test_plugin/style.css', $result);
$result = $this->Helper->assetUrl('TestPlugin.style', array('ext' => '.css', 'plugin' => false));
$this->assertEqual('TestPlugin.style.css', $result);
CakePlugin::unload('TestPlugin');
Configure::write('Asset.timestamp', 'force'); Configure::write('Asset.timestamp', 'force');
$result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => CSS_URL)); $result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => CSS_URL));
@ -630,7 +647,6 @@ class HelperTest extends CakeTestCase {
$_timestamp = Configure::read('Asset.timestamp'); $_timestamp = Configure::read('Asset.timestamp');
Configure::write('Asset.timestamp', 'force'); Configure::write('Asset.timestamp', 'force');
App::build(array( App::build(array(
'plugins' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS),
'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS), 'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS),
)); ));
CakePlugin::loadAll(); CakePlugin::loadAll();

View file

@ -267,15 +267,30 @@ class Helper extends Object {
* @param array $options Options array. Possible keys: * @param array $options Options array. Possible keys:
* `fullBase` Return full url with domain name * `fullBase` Return full url with domain name
* `pathPrefix` Path prefix for relative urls * `pathPrefix` Path prefix for relative urls
* `ext` Asset extension to append
* `plugin` False value will prevent parsing path as a plugin
* @return string Generated url * @return string Generated url
*/ */
public function assetUrl($path, array $options) { public function assetUrl($path, array $options) {
if (is_array($path)) { if (is_array($path)) {
$path = $this->url($path, !empty($options['fullBase'])); $path = $this->url($path, !empty($options['fullBase']));
} elseif (strpos($path, '://') === false) { } elseif (strpos($path, '://') === false) {
if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
list($plugin, $path) = $this->_View->pluginSplit($path, false);
}
if (!empty($options['pathPrefix']) && $path[0] !== '/') { if (!empty($options['pathPrefix']) && $path[0] !== '/') {
$path = $options['pathPrefix'] . $path; $path = $options['pathPrefix'] . $path;
} }
if (
!empty($options['ext']) &&
strpos($path, '?') === false &&
substr($path, -strlen($options['ext'])) !== $options['ext']
) {
$path .= $options['ext'];
}
if (isset($plugin)) {
$path = Inflector::underscore($plugin) . '/' . $path;
}
$path = $this->assetTimestamp($this->webroot($path)); $path = $this->assetTimestamp($this->webroot($path));
if (!empty($options['fullBase'])) { if (!empty($options['fullBase'])) {

View file

@ -420,6 +420,7 @@ class HtmlHelper extends AppHelper {
* and included in the `$scripts_for_layout` layout variable. Defaults to true. * and included in the `$scripts_for_layout` layout variable. Defaults to true.
* - `block` Set the name of the block link/style tag will be appended to. This overrides the `inline` * - `block` Set the name of the block link/style tag will be appended to. This overrides the `inline`
* option. * option.
* - `plugin` False value will prevent parsing path as a plugin
* *
* @param mixed $path The name of a CSS style sheet or an array containing names of * @param mixed $path The name of a CSS style sheet or an array containing names of
* CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot * CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot
@ -450,16 +451,7 @@ class HtmlHelper extends AppHelper {
if (strpos($path, '//') !== false) { if (strpos($path, '//') !== false) {
$url = $path; $url = $path;
} else { } else {
if ($path[0] !== '/') { $url = $this->assetUrl($path, $options + array('pathPrefix' => CSS_URL, 'ext' => '.css'));
$path = CSS_URL . $path;
}
if (strpos($path, '?') === false) {
if (substr($path, -4) !== '.css') {
$path .= '.css';
}
}
$url = $this->assetTimestamp($this->webroot($path));
if (Configure::read('Asset.filter.css')) { if (Configure::read('Asset.filter.css')) {
$pos = strpos($url, CSS_URL); $pos = strpos($url, CSS_URL);
@ -518,6 +510,7 @@ class HtmlHelper extends AppHelper {
* Using this option will override the inline option. * Using this option will override the inline option.
* - `once` Whether or not the script should be checked for uniqueness. If true scripts will only be * - `once` Whether or not the script should be checked for uniqueness. If true scripts will only be
* included once, use false to allow the same script to be included more than once per request. * included once, use false to allow the same script to be included more than once per request.
* - `plugin` False value will prevent parsing path as a plugin
* *
* @param mixed $url String or array of javascript files to include * @param mixed $url String or array of javascript files to include
* @param mixed $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value * @param mixed $options Array of options, and html attributes see above. If boolean sets $options['inline'] = value
@ -552,13 +545,7 @@ class HtmlHelper extends AppHelper {
$this->_includedScripts[$url] = true; $this->_includedScripts[$url] = true;
if (strpos($url, '//') === false) { if (strpos($url, '//') === false) {
if ($url[0] !== '/') { $url = $this->assetUrl($url, $options + array('pathPrefix' => JS_URL, 'ext' => '.js'));
$url = JS_URL . $url;
}
if (strpos($url, '?') === false && substr($url, -3) !== '.js') {
$url .= '.js';
}
$url = $this->assetTimestamp($this->webroot($url));
if (Configure::read('Asset.filter.js')) { if (Configure::read('Asset.filter.js')) {
$url = str_replace(JS_URL, 'cjs/', $url); $url = str_replace(JS_URL, 'cjs/', $url);
@ -780,6 +767,7 @@ class HtmlHelper extends AppHelper {
* - `url` If provided an image link will be generated and the link will point at * - `url` If provided an image link will be generated and the link will point at
* `$options['url']`. * `$options['url']`.
* - `fullBase` If true the src attribute will get a full address for the image file. * - `fullBase` If true the src attribute will get a full address for the image file.
* - `plugin` False value will prevent parsing path as a plugin
* *
* @param string $path Path to the image file, relative to the app/webroot/img/ directory. * @param string $path Path to the image file, relative to the app/webroot/img/ directory.
* @param array $options Array of HTML attributes. See above for special options. * @param array $options Array of HTML attributes. See above for special options.

View file

@ -685,7 +685,7 @@ class View extends Object {
case self::TYPE_ELEMENT: case self::TYPE_ELEMENT:
$parent = $this->_getElementFileName($name); $parent = $this->_getElementFileName($name);
if (!$parent) { if (!$parent) {
list($plugin, $name) = $this->_pluginSplit($name); list($plugin, $name) = $this->pluginSplit($name);
$paths = $this->_paths($plugin); $paths = $this->_paths($plugin);
$defaultPath = $paths[0] . 'Elements' . DS; $defaultPath = $paths[0] . 'Elements' . DS;
throw new LogicException(__d( throw new LogicException(__d(
@ -943,7 +943,7 @@ class View extends Object {
$name = $this->view; $name = $this->view;
} }
$name = str_replace('/', DS, $name); $name = str_replace('/', DS, $name);
list($plugin, $name) = $this->_pluginSplit($name); list($plugin, $name) = $this->pluginSplit($name);
if (strpos($name, DS) === false && $name[0] !== '.') { if (strpos($name, DS) === false && $name[0] !== '.') {
$name = $this->viewPath . DS . $subDir . Inflector::underscore($name); $name = $this->viewPath . DS . $subDir . Inflector::underscore($name);
@ -988,16 +988,17 @@ class View extends Object {
* It checks if the plugin is loaded, else filename will stay unchanged for filenames containing dot * It checks if the plugin is loaded, else filename will stay unchanged for filenames containing dot
* *
* @param string $name The name you want to plugin split. * @param string $name The name you want to plugin split.
* @param boolean $fallback If true uses the plugin set in the current CakeRequest when parsed plugin is not loaded
* @return array Array with 2 indexes. 0 => plugin name, 1 => filename * @return array Array with 2 indexes. 0 => plugin name, 1 => filename
*/ */
protected function _pluginSplit($name) { public function pluginSplit($name, $fallback = true) {
$plugin = null; $plugin = null;
list($first, $second) = pluginSplit($name); list($first, $second) = pluginSplit($name);
if (CakePlugin::loaded($first) === true) { if (CakePlugin::loaded($first) === true) {
$name = $second; $name = $second;
$plugin = $first; $plugin = $first;
} }
if (isset($this->plugin) && !$plugin) { if (isset($this->plugin) && !$plugin && $fallback) {
$plugin = $this->plugin; $plugin = $this->plugin;
} }
return array($plugin, $name); return array($plugin, $name);
@ -1019,7 +1020,7 @@ class View extends Object {
if (!is_null($this->layoutPath)) { if (!is_null($this->layoutPath)) {
$subDir = $this->layoutPath . DS; $subDir = $this->layoutPath . DS;
} }
list($plugin, $name) = $this->_pluginSplit($name); list($plugin, $name) = $this->pluginSplit($name);
$paths = $this->_paths($plugin); $paths = $this->_paths($plugin);
$file = 'Layouts' . DS . $subDir . $name; $file = 'Layouts' . DS . $subDir . $name;
@ -1055,7 +1056,7 @@ class View extends Object {
* @return mixed Either a string to the element filename or false when one can't be found. * @return mixed Either a string to the element filename or false when one can't be found.
*/ */
protected function _getElementFileName($name) { protected function _getElementFileName($name) {
list($plugin, $name) = $this->_pluginSplit($name); list($plugin, $name) = $this->pluginSplit($name);
$paths = $this->_paths($plugin); $paths = $this->_paths($plugin);
$exts = $this->_getExtensions(); $exts = $this->_getExtensions();