Permalink
Browse files

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
  • Loading branch information...
1 parent 4fd3bcc commit bcdf61a9d526f167eb732e838d39f59a7f10efb3 @markstory markstory committed Oct 29, 2011
Showing with 106 additions and 29 deletions.
  1. +29 −0 lib/Cake/Test/Case/View/Helper/TextHelperTest.php
  2. +77 −29 lib/Cake/View/Helper/TextHelper.php
View
29 lib/Cake/Test/Case/View/Helper/TextHelperTest.php
@@ -231,6 +231,23 @@ public function testAutoLink() {
}
/**
+ * 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 @@ public function testAutoLinkUrls() {
$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 @@ public function testAutoLinkUrls() {
$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);
}
/**
View
106 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 @@ public function stripLinks($text) {
* 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)));
}
/**

0 comments on commit bcdf61a

Please sign in to comment.