Browse Source

Merge remote-tracking branch 'origin/2.1' into 2.1

Jose Lorenzo Rodriguez 14 years ago
parent
commit
a47c8160dd

+ 89 - 1
lib/Cake/Test/Case/View/ViewTest.php

@@ -297,7 +297,7 @@ class ViewTest extends CakeTestCase {
  *
  * @return void
  */
-	public function testGetTemplate() {
+	public function testGetViewFileNames() {
 		$this->Controller->plugin = null;
 		$this->Controller->name = 'Pages';
 		$this->Controller->viewPath = 'Pages';
@@ -318,6 +318,34 @@ class ViewTest extends CakeTestCase {
 		$result = $View->getViewFileName('../Posts/index');
 		$this->assertEquals($expected, $result);
 
+		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS .'Pages' . DS .'page.home.ctp';
+		$result = $View->getViewFileName('page.home');
+		$this->assertEquals($expected, $result, 'Should not ruin files with dots.');
+
+		CakePlugin::load('TestPlugin');
+		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS .'Pages' . DS .'home.ctp';
+		$result = $View->getViewFileName('TestPlugin.home');
+		$this->assertEquals($expected, $result, 'Plugin is missing the view, cascade to app.');
+
+		$View->viewPath = 'Tests';
+		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' . DS . 'View' . DS .'Tests' . DS .'index.ctp';
+		$result = $View->getViewFileName('TestPlugin.index');
+		$this->assertEquals($expected, $result);
+	}
+
+/**
+ * Test getting layout filenames
+ *
+ * @return void
+ */
+	public function testGetLayoutFileName() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Pages';
+		$this->Controller->viewPath = 'Pages';
+		$this->Controller->action = 'display';
+
+		$View = new TestView($this->Controller);
+
 		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Layouts' . DS .'default.ctp';
 		$result = $View->getLayoutFileName();
 		$this->assertEquals($expected, $result);
@@ -330,7 +358,30 @@ class ViewTest extends CakeTestCase {
 		$View->layoutPath = 'Emails' . DS . 'html';
 		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'View' . DS . 'Layouts' . DS . 'Emails' . DS . 'html' . DS . 'default.ctp';
 		$result = $View->getLayoutFileName();
+		$this->assertEquals($expected, $result);
+	}
 
+/**
+ * Test getting layout filenames for plugins.
+ *
+ * @return void
+ */
+	public function testGetLayoutFileNamePlugin() {
+		$this->Controller->plugin = null;
+		$this->Controller->name = 'Pages';
+		$this->Controller->viewPath = 'Pages';
+		$this->Controller->action = 'display';
+
+		$View = new TestView($this->Controller);
+		CakePlugin::load('TestPlugin');
+
+		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' . DS . 'View' . DS . 'Layouts' . DS .'default.ctp';
+		$result = $View->getLayoutFileName('TestPlugin.default');
+		$this->assertEquals($expected, $result);
+
+		$View->plugin = 'TestPlugin';
+		$expected = CAKE . 'Test' . DS . 'test_app' . DS . 'Plugin' . DS . 'TestPlugin' . DS . 'View' . DS . 'Layouts' . DS .'default.ctp';
+		$result = $View->getLayoutFileName('default');
 		$this->assertEquals($expected, $result);
 	}
 
@@ -423,6 +474,13 @@ class ViewTest extends CakeTestCase {
 		$result = $this->View->element('plugin_element', array(), array('plugin' => 'test_plugin'));
 		$this->assertEquals($result, 'this is the plugin element using params[plugin]');
 
+		$result = $this->View->element('TestPlugin.plugin_element');
+		$this->assertEquals($result, 'this is the plugin element using params[plugin]');
+
+		$result = $this->View->element('test_plugin.plugin_element');
+		$this->assertPattern('/Not Found:/', $result);
+		$this->assertPattern('/test_plugin.plugin_element/', $result);
+
 		$this->View->plugin = 'TestPlugin';
 		$result = $this->View->element('test_plugin_element');
 		$this->assertEquals($result, 'this is the test set using View::$plugin plugin element');
@@ -430,6 +488,10 @@ class ViewTest extends CakeTestCase {
 		$result = $this->View->element('non_existant_element');
 		$this->assertRegExp('/Not Found:/', $result);
 		$this->assertRegExp('/non_existant_element/', $result);
+
+		$result = $this->View->element('TestPlugin.plugin_element', array(), array('plugin' => 'test_plugin'));
+		$this->assertRegExp('/Not Found:/', $result);
+		$this->assertRegExp('/TestPlugin.plugin_element/', $result);
 	}
 
 /**
@@ -797,6 +859,9 @@ class ViewTest extends CakeTestCase {
 		$result = $View->getViewFileName('index');
 		$this->assertRegExp('/Posts(\/|\\\)index.ctp/', $result);
 
+		$result = $View->getViewFileName('TestPlugin.index');
+		$this->assertPattern('/Posts(\/|\\\)index.ctp/', $result);
+
 		$result = $View->getViewFileName('/Pages/home');
 		$this->assertRegExp('/Pages(\/|\\\)home.ctp/', $result);
 
@@ -1133,6 +1198,29 @@ TEXT;
 	}
 
 /**
+ * Make sure that extending the current view with itself causes an exception
+ *
+ * @expectedException LogicException
+ * @return void
+ */
+	public function testExtendSelf() {
+		$this->View->layout = false;
+		$this->View->render('extend_self');
+	}
+
+/**
+ * Make sure that extending in a loop causes an exception
+ *
+ * @expectedException LogicException
+ * @return void
+ */
+	public function testExtendLoop() {
+		$this->View->layout = false;
+		$this->View->render('extend_loop');
+	}
+
+
+/**
  * Test extend() in an element and a view.
  *
  * @return void

+ 2 - 0
lib/Cake/Test/test_app/View/Pages/page.home.ctp

@@ -0,0 +1,2 @@
+Empty page with a dot in the filename.
+Used to test plugin.view and missing plugins.

+ 2 - 0
lib/Cake/Test/test_app/View/Posts/extend_loop.ctp

@@ -0,0 +1,2 @@
+<?php $this->extend('extend_loop_inner'); ?>
+Outer element.

+ 2 - 0
lib/Cake/Test/test_app/View/Posts/extend_loop_inner.ctp

@@ -0,0 +1,2 @@
+<?php $this->extend('extend_loop'); ?>
+Inner loop element.

+ 2 - 0
lib/Cake/Test/test_app/View/Posts/extend_self.ctp

@@ -0,0 +1,2 @@
+<?php $this->extend('extend_self'); ?>
+To infinifty and beyond.

+ 54 - 14
lib/Cake/View/View.php

@@ -33,6 +33,7 @@ App::uses('CakeEventManager', 'Event');
  * and then inserted into the selected layout.  This also means you can pass data from the view to the
  * layout using `$this->set()`
  *
+ *
  * @package       Cake.View
  * @property      CacheHelper $Cache
  * @property      FormHelper $Form
@@ -326,28 +327,30 @@ class View extends Object {
  * This realizes the concept of Elements, (or "partial layouts") and the $params array is used to send
  * data to be used in the element. Elements can be cached improving performance by using the `cache` option.
  *
- * @param string $name Name of template file in the/app/View/Elements/ folder
+ * @param string $name Name of template file in the/app/View/Elements/ folder,
+ *   or `MyPlugin.template` to use the template element from MyPlugin.  If the element
+ *   is not found in the plugin, the normal view path cascade will be searched.
  * @param array $data Array of data to be made available to the rendered view (i.e. the Element)
  * @param array $options Array of options. Possible keys are:
  * - `cache` - Can either be `true`, to enable caching using the config in View::$elementCache. Or an array
  *   If an array, the following keys can be used:
  *   - `config` - Used to store the cached element in a custom cache configuration.
  *   - `key` - Used to define the key used in the Cache::write().  It will be prefixed with `element_`
- * - `plugin` - Load an element from a specific plugin.
+ * - `plugin` - Load an element from a specific plugin.  This option is deprecated, see below.
  * - `callbacks` - Set to true to fire beforeRender and afterRender helper callbacks for this element.
  *   Defaults to false.
  * @return string Rendered Element
+ * @deprecated The `$options['plugin']` is deprecated and will be removed in CakePHP 3.0.  Use
+ *   `Plugin.element_name` instead.
  */
 	public function element($name, $data = array(), $options = array()) {
 		$file = $plugin = $key = null;
 		$callbacks = false;
 
 		if (isset($options['plugin'])) {
-			$plugin = Inflector::camelize($options['plugin']);
-		}
-		if (isset($this->plugin) && !$plugin) {
-			$plugin = $this->plugin;
+			$name = Inflector::camelize($options['plugin']) . '.' . $name;
 		}
+
 		if (isset($options['callbacks'])) {
 			$callbacks = $options['callbacks'];
 		}
@@ -376,7 +379,7 @@ class View extends Object {
 			}
 		}
 
-		$file = $this->_getElementFilename($name, $plugin);
+		$file = $this->_getElementFilename($name);
 
 		if ($file) {
 			if (!$this->_helpersLoaded) {
@@ -417,6 +420,10 @@ class View extends Object {
  *
  * If View::$autoRender is false and no `$layout` is provided, the view will be returned bare.
  *
+ * View and layout names can point to plugin views/layouts.  Using the `Plugin.view` syntax
+ * a plugin view/layout can be used instead of the app ones.  If the chosen plugin is not found
+ * the view will be located along the regular view path cascade.
+ *
  * @param string $view Name of view file to use
  * @param string $layout Layout to use.
  * @return string Rendered Element
@@ -648,6 +655,7 @@ class View extends Object {
  *
  * @param string $name The view or element to 'extend' the current one with.
  * @return void
+ * @throws LogicException when you extend a view with itself or make extend loops.
  */
 	public function extend($name) {
 		switch ($this->_currentType) {
@@ -662,6 +670,12 @@ class View extends Object {
 			break;
 		
 		}
+		if ($parent == $this->_current) {
+			throw new LogicException(__d('cake_dev', 'You cannot have views extend themselves.'));
+		}
+		if (isset($this->_parents[$parent]) && $this->_parents[$parent] == $this->_current) {
+			throw new LogicException(__d('cake_dev', 'You cannot have views extend in a loop.'));
+		}
 		$this->_parents[$this->_current] = $parent;
 	}
 
@@ -876,6 +890,7 @@ class View extends Object {
 			$name = $this->view;
 		}
 		$name = str_replace('/', DS, $name);
+		list($plugin, $name) = $this->_pluginSplit($name);
 
 		if (strpos($name, DS) === false && $name[0] !== '.') {
 			$name = $this->viewPath . DS . $subDir . Inflector::underscore($name);
@@ -891,8 +906,7 @@ class View extends Object {
 				$name = $this->viewPath . DS . $subDir . $name;
 			}
 		}
-		$paths = $this->_paths($this->plugin);
-
+		$paths = $this->_paths($plugin);
 		$exts = $this->_getExtensions();
 		foreach ($exts as $ext) {
 			foreach ($paths as $path) {
@@ -916,6 +930,27 @@ class View extends Object {
 	}
 
 /**
+ * Splits a dot syntax plugin name into its plugin and filename.
+ * If $name does not have a dot, then index 0 will be null.
+ * 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.
+ * @return array Array with 2 indexes.  0 => plugin name, 1 => filename
+ */
+	protected function _pluginSplit($name) {
+		$plugin = null;
+		list($first, $second) = pluginSplit($name);
+		if (CakePlugin::loaded($first) === true) {
+			$name = $second;
+			$plugin = $first;
+		}
+		if (isset($this->plugin) && !$plugin) {
+			$plugin = $this->plugin;
+		}
+		return array($plugin, $name);
+	}
+
+/**
  * Returns layout filename for this template as a string.
  *
  * @param string $name The name of the layout to find.
@@ -931,7 +966,8 @@ class View extends Object {
 		if (!is_null($this->layoutPath)) {
 			$subDir = $this->layoutPath . DS;
 		}
-		$paths = $this->_paths($this->plugin);
+		list($plugin, $name) = $this->_pluginSplit($name);
+		$paths = $this->_paths($plugin);
 		$file = 'Layouts' . DS . $subDir . $name;
 
 		$exts = $this->_getExtensions();
@@ -963,10 +999,11 @@ class View extends Object {
  * Finds an element filename, returns false on failure.
  *
  * @param string $name The name of the element to find.
- * @param string $plugin The plugin name the element is in.
  * @return mixed Either a string to the element filename or false when one can't be found.
  */
-	protected function _getElementFileName($name, $plugin = null) {
+	protected function _getElementFileName($name) {
+		list($plugin, $name) = $this->_pluginSplit($name);
+
 		$paths = $this->_paths($plugin);
 		$exts = $this->_getExtensions();
 		foreach ($exts as $ext) {
@@ -1003,7 +1040,10 @@ class View extends Object {
 			$paths = array_merge($paths, App::path('View', $plugin));
 		}
 
-		$this->_paths = array_unique(array_merge($paths, $viewPaths, array_keys($corePaths)));
-		return $this->_paths;
+		$paths = array_unique(array_merge($paths, $viewPaths, array_keys($corePaths)));
+		if ($plugin !== null) {
+			return $paths;
+		}
+		return $this->_paths = $paths;
 	}
 }