浏览代码

Adding escaping by default for TextHelper::autoLink()

TextHelper::autoLink, autoLinkEmails, and autoLinkUrls now all
escape HTML by default, this can be disabled using the escape => false option.

Fixes #1625
mark_story 14 年之前
父节点
当前提交
bcdf61a9d5
共有 2 个文件被更改,包括 106 次插入29 次删除
  1. 29 0
      lib/Cake/Test/Case/View/Helper/TextHelperTest.php
  2. 77 29
      lib/Cake/View/Helper/TextHelper.php

+ 29 - 0
lib/Cake/Test/Case/View/Helper/TextHelperTest.php

@@ -231,6 +231,23 @@ class TextHelperTest extends CakeTestCase {
 	}
 
 /**
+ * Test escaping for autoLink
+ *
+ * @return void
+ */
+	public function testAutoLinkEscape() {
+		$text = 'This is a <b>test</b> text with URL http://www.cakephp.org';
+		$expected = 'This is a &lt;b&gt;test&lt;/b&gt; text with URL <a href="http://www.cakephp.org">http://www.cakephp.org</a>';
+		$result = $this->Text->autoLink($text);
+		$this->assertEqual($expected, $result);
+
+		$text = 'This is a <b>test</b> text with URL http://www.cakephp.org';
+		$expected = 'This is a <b>test</b> text with URL <a href="http://www.cakephp.org">http://www.cakephp.org</a>';
+		$result = $this->Text->autoLink($text, array('escape' => false));
+		$this->assertEqual($expected, $result);
+	}
+
+/**
  * testAutoLinkUrls method
  *
  * @return void
@@ -280,7 +297,14 @@ class TextHelperTest extends CakeTestCase {
 		$expected = 'Text with a url <a href="http://www.not--work.com">http://www.not--work.com</a> and more';
 		$result = $this->Text->autoLinkUrls($text);
 		$this->assertEqual($expected, $result);
+	}
 
+/**
+ * Test autoLinkUrls with the escape option.
+ *
+ * @return void
+ */
+	public function testAutoLinkUrlsEscape() {
 		$text = 'Text with a partial <a href="http://www.cakephp.org">link</a> link';
 		$expected = 'Text with a partial <a href="http://www.cakephp.org">link</a> link';
 		$result = $this->Text->autoLinkUrls($text, array('escape' => false));
@@ -290,6 +314,11 @@ class TextHelperTest extends CakeTestCase {
 		$expected = 'Text with a partial <iframe src="http://www.cakephp.org" /> link';
 		$result = $this->Text->autoLinkUrls($text, array('escape' => false));
 		$this->assertEqual($expected, $result);
+
+		$text = 'Text with a partial <iframe src="http://www.cakephp.org" /> link';
+		$expected = 'Text with a partial &lt;iframe src=&quot;http://www.cakephp.org&quot; /&gt; link';
+		$result = $this->Text->autoLinkUrls($text, array('escape' => true));
+		$this->assertEqual($expected, $result);
 	}
 
 /**

+ 77 - 29
lib/Cake/View/Helper/TextHelper.php

@@ -46,6 +46,14 @@ class TextHelper extends AppHelper {
 	public $helpers = array('Html');
 
 /**
+ * An array of md5sums and their contents.
+ * Used when inserting links into text.
+ *
+ * @var array
+ */
+	protected $_placeholders = array();
+
+/**
  * Highlights a given phrase in a text. You can specify any expression in highlighter that
  * may include the \1 expression to include the $phrase found.
  *
@@ -112,86 +120,126 @@ class TextHelper extends AppHelper {
  * Adds links (<a href=....) to a given text, by finding text that begins with
  * strings like http:// and ftp://.
  *
- * @param string $text Text to add links to
- * @param array $htmlOptions Array of HTML options.
+ * ### Options
+ *
+ * - `escape` Control HTML escaping of input. Defaults to true.
+ *
+ * @param string $text Text
+ * @param array $options Array of HTML options, and options listed above.
  * @return string The text with links
  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::autoLinkUrls
  */
-	public function autoLinkUrls($text, $htmlOptions = array()) {
-		$this->_linkOptions = $htmlOptions;
+	public function autoLinkUrls($text, $options = array()) {
+		$this->_placeholders = array();
+		$options += array('escape' => true);
+
 		$text = preg_replace_callback(
 			'#(?<!href="|src="|">)((?:https?|ftp|nntp)://[^\s<>()]+)#i',
-			array(&$this, '_linkBareUrl'),
+			array(&$this, '_insertPlaceHolder'),
 			$text
 		);
-		return preg_replace_callback(
+		$text = preg_replace_callback(
 			'#(?<!href="|">)(?<!http://|https://|ftp://|nntp://)(www\.[^\n\%\ <]+[^<\n\%\,\.\ <])(?<!\))#i',
-			array(&$this, '_linkUrls'),
+			array(&$this, '_insertPlaceHolder'),
 			$text
 		);
+		if ($options['escape']) {
+			$text = h($text);
+		}
+		return $this->_linkUrls($text, $options);
 	}
 
 /**
- * Links urls that include http://
+ * Saves the placeholder for a string, for later use.  This gets around double
+ * escaping content in URL's.
  *
- * @param array $matches
- * @return string
- * @see TextHelper::autoLinkUrls()
+ * @param array $matches An array of regexp matches.
+ * @return string Replaced values.
  */
-	protected function _linkBareUrl($matches) {
-		return $this->Html->link($matches[0], $matches[0], $this->_linkOptions);
+	protected function _insertPlaceHolder($matches) {
+		$key = md5($matches[0]);
+		$this->_placeholders[$key] = $matches[0];
+		return $key;
 	}
 
 /**
- * Links urls missing http://
+ * Replace placeholders with links.
  *
- * @param array $matches
- * @return string
- * @see TextHelper::autoLinkUrls()
+ * @param string $text The text to operate on.
+ * @param array $htmlOptions The options for the generated links.
+ * @return string The text with links inserted.
  */
-	protected function _linkUrls($matches) {
-		return $this->Html->link($matches[0], 'http://' . $matches[0], $this->_linkOptions);
+	protected function _linkUrls($text, $htmlOptions) {
+		$replace = array();
+		foreach ($this->_placeholders as $md5 => $url) {
+			$link = $url;
+			if (!preg_match('#^[a-z]+\://#', $url)) {
+				$url = 'http://' . $url;
+			}
+			$replace[$md5] = $this->Html->link($link, $url, $htmlOptions);
+		}
+		return strtr($text, $replace);
 	}
 
 /**
  * Links email addresses
  *
- * @param array $matches
+ * @param string $text The text to operate on
+ * @param array $options An array of options to use for the HTML.
  * @return string
- * @see TextHelper::autoLinkUrls()
+ * @see TextHelper::autoLinkEmails()
  */
-	protected function _linkEmails($matches) {
-		return $this->Html->link($matches[0], 'mailto:' . $matches[0], $this->_linkOptions);
+	protected function _linkEmails($text, $options) {
+		$replace = array();
+		foreach ($this->_placeholders as $md5 => $url) {
+			$replace[$md5] = $this->Html->link($url, 'mailto:' . $url, $options);
+		}
+		return strtr($text, $replace);
 	}
 
 /**
  * Adds email links (<a href="mailto:....) to a given text.
  *
+ * ### Options
+ *
+ * - `escape` Control HTML escaping of input. Defaults to true.
+ *
  * @param string $text Text
- * @param array $options Array of HTML options.
+ * @param array $options Array of HTML options, and options listed above.
  * @return string The text with links
  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::autoLinkEmails
  */
 	public function autoLinkEmails($text, $options = array()) {
-		$this->_linkOptions = $options;
+		$options += array('escape' => true);
+		$this->_placeholders = array();
+
 		$atom = '[a-z0-9!#$%&\'*+\/=?^_`{|}~-]';
-		return preg_replace_callback(
+		$text = preg_replace_callback(
 			'/(' . $atom . '+(?:\.' . $atom . '+)*@[a-z0-9-]+(?:\.[a-z0-9-]+)+)/i',
-			array(&$this, '_linkEmails'),
+			array(&$this, '_insertPlaceholder'),
 			$text
 		);
+		if ($options['escape']) {
+			$text = h($text);
+		}
+		return $this->_linkEmails($text, $options);
 	}
 
 /**
  * Convert all links and email adresses to HTML links.
  *
+ * ### Options
+ *
+ * - `escape` Control HTML escaping of input. Defaults to true.
+ *
  * @param string $text Text
- * @param array $options Array of HTML options.
+ * @param array $options Array of HTML options, and options listed above.
  * @return string The text with links
  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/text.html#TextHelper::autoLink
  */
 	public function autoLink($text, $options = array()) {
-		return $this->autoLinkEmails($this->autoLinkUrls($text, $options), $options);
+		$text = $this->autoLinkUrls($text, $options);
+		return $this->autoLinkEmails($text, array_merge($options, array('escape' => false)));
 	}
 
 /**