diff --git a/lib/moodlelib.php b/lib/moodlelib.php index 8879042dfa8..427e15fb8dd 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -7735,13 +7735,19 @@ function shorten_text($text, $ideal=30, $exact = false, $ending='...') { return $text; } - // splits all html-tags to scanable lines + // Splits on HTML tags. Each open/close/empty tag will be the first thing + // and only tag in its 'line' preg_match_all('/(<.+?>)?([^<>]*)/s', $text, $lines, PREG_SET_ORDER); $total_length = strlen($ending); - $open_tags = array(); $truncate = ''; + // This array stores information about open and close tags and their position + // in the truncated string. Each item in the array is an object with fields + // ->open (true if open), ->tag (tag name in lower case), and ->pos + // (byte position in truncated text) + $tagdetails = array(); + foreach ($lines as $line_matchings) { // if there is any html-tag in this line, handle it and add it (uncounted) to the output if (!empty($line_matchings[1])) { @@ -7750,15 +7756,14 @@ function shorten_text($text, $ideal=30, $exact = false, $ending='...') { // do nothing // if tag is a closing tag (f.e. ) } else if (preg_match('/^<\s*\/([^\s]+?)\s*>$/s', $line_matchings[1], $tag_matchings)) { - // delete tag from $open_tags list - $pos = array_search($tag_matchings[1], array_reverse($open_tags, true)); // can have multiple exact same open tags, close the last one - if ($pos !== false) { - unset($open_tags[$pos]); - } + // record closing tag + $tagdetails[] = (object)array('open'=>false, + 'tag'=>strtolower($tag_matchings[1]), 'pos'=>strlen($truncate)); // if tag is an opening tag (f.e. ) } else if (preg_match('/^<\s*([^\s>!]+).*?>$/s', $line_matchings[1], $tag_matchings)) { - // add tag to the beginning of $open_tags list - array_unshift($open_tags, strtolower($tag_matchings[1])); + // record opening tag + $tagdetails[] = (object)array('open'=>true, + 'tag'=>strtolower($tag_matchings[1]), 'pos'=>strlen($truncate)); } // add html-tag to $truncate'd text $truncate .= $line_matchings[1]; @@ -7821,6 +7826,24 @@ function shorten_text($text, $ideal=30, $exact = false, $ending='...') { // add the defined ending to the text $truncate .= $ending; + // Now calculate the list of open html tags based on the truncate position + $open_tags = array(); + foreach ($tagdetails as $taginfo) { + if(isset($breakpos) && $taginfo->pos >= $breakpos) { + // Don't include tags after we made the break! + break; + } + if($taginfo->open) { + // add tag to the beginning of $open_tags list + array_unshift($open_tags, $taginfo->tag); + } else { + $pos = array_search($taginfo->tag, array_reverse($open_tags, true)); // can have multiple exact same open tags, close the last one + if ($pos !== false) { + unset($open_tags[$pos]); + } + } + } + // close all unclosed html-tags foreach ($open_tags as $tag) { $truncate .= ''; diff --git a/lib/simpletest/testmoodlelib.php b/lib/simpletest/testmoodlelib.php index af3384d8779..e81c8cd9fad 100644 --- a/lib/simpletest/testmoodlelib.php +++ b/lib/simpletest/testmoodlelib.php @@ -354,6 +354,34 @@ class moodlelib_test extends UnitTestCase { $this->assertFalse(make_user_directory(true, true)); } + + function test_shorten_text() { + $text = "short text already no tags"; + $this->assertEqual($text, shorten_text($text)); + + $text = "

short text already

with tags

"; + $this->assertEqual($text, shorten_text($text)); + + $text = "long text without any tags blah de blah blah blah what"; + $this->assertEqual('long text without any tags ...', shorten_text($text)); + + $text = "

Long text with tags that will ". + "be chopped off but should be added back again

"; + $this->assertEqual("

Long text with " . + "tags that ...

", shorten_text($text)); + + $text = "some text which shouldn't   break there"; + $this->assertEqual("some text which shouldn't   ...", + shorten_text($text, 31)); + $this->assertEqual("some text which shouldn't ...", + shorten_text($text, 30)); + + // This case caused a bug up to 1.9.5 + $text = "

standard 'break-out' sub groups in TGs?

 <<There are several"; + $this->assertEqual("

standard 'break-out' sub groups in ...

", + shorten_text($text, 43)); + } + } ?>