diff --git a/model/fieldtypes/StringField.php b/model/fieldtypes/StringField.php index ce79b69ed..e489c7d17 100644 --- a/model/fieldtypes/StringField.php +++ b/model/fieldtypes/StringField.php @@ -17,6 +17,7 @@ abstract class StringField extends DBField { */ private static $casting = array( "LimitCharacters" => "Text", + "LimitCharactersToClosestWord" => "Text", 'LimitWordCount' => 'Text', 'LimitWordCountXML' => 'HTMLText', "LowerCase" => "Text", @@ -116,7 +117,6 @@ abstract class StringField extends DBField { */ public function LimitCharacters($limit = 20, $add = '...') { $value = trim($this->value); - if($this->stat('escape_type') == 'xml') { $value = strip_tags($value); $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8'); @@ -126,11 +126,40 @@ abstract class StringField extends DBField { } else { $value = (mb_strlen($value) > $limit) ? mb_substr($value, 0, $limit) . $add : $value; } + return $value; + } + + /** + * Limit this field's content by a number of characters and truncate + * the field to the closest complete word. All HTML tags are stripped + * from the field. + * + * @param int $limit Number of characters to limit by + * @param string $add Ellipsis to add to the end of truncated string + * @return string + */ + public function LimitCharactersToClosestWord($limit = 20, $add = '...') { + // Strip HTML tags if they exist in the field + $this->value = strip_tags($this->value); + + // Determine if value exceeds limit before limiting characters + $exceedsLimit = mb_strlen($this->value) > $limit; + + // Limit to character limit + $value = $this->LimitCharacters($limit, ''); + + // If value exceeds limit, strip punctuation off the end to the last space and apply ellipsis + if($exceedsLimit) { + $value = html_entity_decode($value, ENT_COMPAT, 'UTF-8'); + + $value = rtrim(mb_substr($value, 0, mb_strrpos($value, " ")), "/[\.,-\/#!$%\^&\*;:{}=\-_`~()]\s") . $add; + + $value = htmlspecialchars($value, ENT_COMPAT, 'UTF-8'); + } return $value; } - /** * Limit this field's content by a number of words. * diff --git a/tests/model/TextTest.php b/tests/model/TextTest.php index ccd6b845d..05bfd2b7c 100644 --- a/tests/model/TextTest.php +++ b/tests/model/TextTest.php @@ -21,6 +21,35 @@ class TextTest extends SapphireTest { } } + /** + * Test {@link Text->LimitCharactersToClosestWord()} + */ + public function testLimitCharactersToClosestWord() { + $cases = array( + /* Standard words limited, ellipsis added if truncated */ + 'Lorem ipsum dolor sit amet' => 'Lorem ipsum dolor sit...', + + /* Complete words less than the character limit don't get truncated, ellipsis not added */ + 'Lorem ipsum' => 'Lorem ipsum', + 'Lorem' => 'Lorem', + '' => '', // No words produces nothing! + + /* HTML tags get stripped out, leaving the raw text */ + '

Lorem ipsum dolor sit amet

' => 'Lorem ipsum dolor sit...', + '

Lorem ipsum dolor sit amet

' => 'Lorem ipsum dolor sit...', + '

Lorem ipsum

' => 'Lorem ipsum', + + /* HTML entities are treated as a single character */ + 'Lorem & ipsum dolor sit amet' => 'Lorem & ipsum dolor...' + ); + + foreach($cases as $originalValue => $expectedValue) { + $textObj = new Text('Test'); + $textObj->setValue($originalValue); + $this->assertEquals($expectedValue, $textObj->LimitCharactersToClosestWord(24)); + } + } + /** * Test {@link Text->LimitWordCount()} */