Browse Source

Merge pull request #421 from tigrang/plugin-assets

Plugin.asset support for HtmlHelper::css , script, and image
Mark Story 14 years ago
parent
commit
ae9ff9fbd0

+ 194 - 0
lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php

@@ -149,6 +149,10 @@ class HtmlHelperTest extends CakeTestCase {
 		$this->Html->request = new CakeRequest(null, false);
 		$this->Html->request->webroot = '';
 
+		App::build(array(
+			'plugins' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin'. DS)
+		));
+
 		Configure::write('Asset.timestamp', false);
 	}
 
@@ -496,6 +500,12 @@ class HtmlHelperTest extends CakeTestCase {
 		$result = $this->Html->css('screen.css');
 		$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');
 		$expected['link']['href'] = 'preg:/.*css\/my\.css\.library\.css/';
 		$this->assertTags($result, $expected);
@@ -542,6 +552,49 @@ class HtmlHelperTest extends CakeTestCase {
 	}
 
 /**
+ * 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
  *
  * @return void
@@ -582,6 +635,50 @@ class HtmlHelperTest extends CakeTestCase {
 	}
 
 /**
+ * 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.
  *
  * @return void
@@ -607,6 +704,37 @@ class HtmlHelperTest extends CakeTestCase {
 	}
 
 /**
+ * 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 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.
  *

+ 17 - 1
lib/Cake/Test/Case/View/HelperTest.php

@@ -198,6 +198,10 @@ class HelperTest extends CakeTestCase {
 		ClassRegistry::addObject('HelperTestPost', new HelperTestPost());
 		ClassRegistry::addObject('HelperTestComment', new HelperTestComment());
 		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));
 		$this->assertEquals(FULL_BASE_URL . '/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');
 
 		$result = $this->Helper->assetUrl('cake.generic.css', array('pathPrefix' => CSS_URL));
@@ -630,7 +647,6 @@ class HelperTest extends CakeTestCase {
 		$_timestamp = Configure::read('Asset.timestamp');
 		Configure::write('Asset.timestamp', 'force');
 		App::build(array(
-			'plugins' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS),
 			'View' => array(CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS),
 		));
 		CakePlugin::loadAll();

+ 15 - 0
lib/Cake/View/Helper.php

@@ -267,15 +267,30 @@ class Helper extends Object {
  * @param array $options Options array. Possible keys:
  * 	`fullBase` Return full url with domain name
  * 	`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
  */
 	public function assetUrl($path, array $options) {
 		if (is_array($path)) {
 			$path = $this->url($path, !empty($options['fullBase']));
 		} 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] !== '/') {
 				$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));
 
 			if (!empty($options['fullBase'])) {

+ 5 - 17
lib/Cake/View/Helper/HtmlHelper.php

@@ -420,6 +420,7 @@ class HtmlHelper extends AppHelper {
  *   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`
  *   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
  *   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) {
 			$url = $path;
 		} else {
-			if ($path[0] !== '/') {
-				$path = CSS_URL . $path;
-			}
-
-			if (strpos($path, '?') === false) {
-				if (substr($path, -4) !== '.css') {
-					$path .= '.css';
-				}
-			}
-			$url = $this->assetTimestamp($this->webroot($path));
+			$url = $this->assetUrl($path, $options + array('pathPrefix' => CSS_URL, 'ext' => '.css'));
 
 			if (Configure::read('Asset.filter.css')) {
 				$pos = strpos($url, CSS_URL);
@@ -518,6 +510,7 @@ class HtmlHelper extends AppHelper {
  *   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
  *   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 $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;
 
 		if (strpos($url, '//') === false) {
-			if ($url[0] !== '/') {
-				$url = JS_URL . $url;
-			}
-			if (strpos($url, '?') === false && substr($url, -3) !== '.js') {
-				$url .= '.js';
-			}
-			$url = $this->assetTimestamp($this->webroot($url));
+			$url = $this->assetUrl($url, $options + array('pathPrefix' => JS_URL, 'ext' => '.js'));
 
 			if (Configure::read('Asset.filter.js')) {
 				$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
  *   `$options['url']`.
  * - `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 array $options Array of HTML attributes.  See above for special options.

+ 7 - 6
lib/Cake/View/View.php

@@ -685,7 +685,7 @@ class View extends Object {
 				case self::TYPE_ELEMENT:
 					$parent = $this->_getElementFileName($name);
 					if (!$parent) {
-						list($plugin, $name) = $this->_pluginSplit($name);
+						list($plugin, $name) = $this->pluginSplit($name);
 						$paths = $this->_paths($plugin);
 						$defaultPath = $paths[0] . 'Elements' . DS;
 						throw new LogicException(__d(
@@ -943,7 +943,7 @@ class View extends Object {
 			$name = $this->view;
 		}
 		$name = str_replace('/', DS, $name);
-		list($plugin, $name) = $this->_pluginSplit($name);
+		list($plugin, $name) = $this->pluginSplit($name);
 
 		if (strpos($name, DS) === false && $name[0] !== '.') {
 			$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
  *
  * @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
  */
-	protected function _pluginSplit($name) {
+	public function pluginSplit($name, $fallback = true) {
 		$plugin = null;
 		list($first, $second) = pluginSplit($name);
 		if (CakePlugin::loaded($first) === true) {
 			$name = $second;
 			$plugin = $first;
 		}
-		if (isset($this->plugin) && !$plugin) {
+		if (isset($this->plugin) && !$plugin && $fallback) {
 			$plugin = $this->plugin;
 		}
 		return array($plugin, $name);
@@ -1019,7 +1020,7 @@ class View extends Object {
 		if (!is_null($this->layoutPath)) {
 			$subDir = $this->layoutPath . DS;
 		}
-		list($plugin, $name) = $this->_pluginSplit($name);
+		list($plugin, $name) = $this->pluginSplit($name);
 		$paths = $this->_paths($plugin);
 		$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.
  */
 	protected function _getElementFileName($name) {
-		list($plugin, $name) = $this->_pluginSplit($name);
+		list($plugin, $name) = $this->pluginSplit($name);
 
 		$paths = $this->_paths($plugin);
 		$exts = $this->_getExtensions();