Skip to content

Commit

Permalink
Adding escaping by default for TextHelper::autoLink()
Browse files Browse the repository at this point in the history
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
markstory committed Oct 29, 2011
1 parent 4fd3bcc commit bcdf61a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 29 deletions.
29 changes: 29 additions & 0 deletions lib/Cake/Test/Case/View/Helper/TextHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,23 @@ public function testAutoLink() {
$this->assertEqual($expected, $result);
}

/**
* 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
*
Expand Down Expand Up @@ -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));
Expand All @@ -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);
}

/**
Expand Down
106 changes: 77 additions & 29 deletions lib/Cake/View/Helper/TextHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,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.
Expand Down Expand Up @@ -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)));
}

/**
Expand Down

0 comments on commit bcdf61a

Please sign in to comment.