/** * Render the cropped text * * @param integer $maxCharacters Place where to truncate the string * @param string $append What to append, if truncation happened * @param boolean $respectBoundaries If TRUE and division is in the middle of a word, the remains of that word is removed. * @param boolean $respectHtml If TRUE the cropped string will respect HTML tags and entities. Technically that means, that cropHTML() is called rather than crop() * @return string cropped text * @author Andreas Pattynama <*****@*****.**> * @author Sebastian Kurfürst <*****@*****.**> * @author Bastian Waidelich <*****@*****.**> * @author Felix Oertel <*****@*****.**> */ public function render($maxCharacters, $append = '...', $respectWordBoundaries = TRUE, $respectHtml = TRUE) { $stringToTruncate = $this->renderChildren(); if (TYPO3_MODE === 'BE') { $this->simulateFrontendEnvironment(); } if ($respectHtml) { $content = $this->contentObject->cropHTML($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries); } else { $content = $this->contentObject->crop($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries); } if (TYPO3_MODE === 'BE') { $this->resetFrontendEnvironment(); } return $content; }
/** * Render the cropped text * * @param integer $maxCharacters Place where to truncate the string * @param string $append What to append, if truncation happened * @param boolean $respectBoundaries If TRUE and division is in the middle of a word, the remains of that word is removed. This is currently ignored in backend mode! * @return string cropped text * @author Andreas Pattynama <*****@*****.**> * @author Sebastian Kurfürst <*****@*****.**> * @author Bastian Waidelich <*****@*****.**> * @author Felix Oertel <*****@*****.**> */ public function render($maxCharacters, $append = '...', $respectWordBoundaries = TRUE) { $stringToTruncate = $this->renderChildren(); if (TYPO3_MODE === 'BE') { if (strlen($stringToTruncate) > $maxCharacters) { $stringToTruncate = substr($stringToTruncate, 0, $maxCharacters - strlen($append)) . $append; } return $stringToTruncate; } else { if (strip_tags($stringToTruncate) != $stringToTruncate) { return $this->contentObject->cropHTML($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries); } else { return $this->contentObject->crop($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries); } } }
/** * Checks if stdWrap.cropHTML works with a complex content with many tags. Currently cropHTML * counts multiple invisible characters not as one (as the browser will output the content). * * @test */ public function cropHtmlWorksWithComplexContent() { $GLOBALS['TSFE']->renderCharset = 'iso-8859-1'; $subject = ' <h1>Blog Example</h1> <hr> <div class="csc-header csc-header-n1"> <h2 class="csc-firstHeader">Welcome to Blog #1</h2> </div> <p class="bodytext"> A blog about TYPO3 extension development. In order to start blogging, read the <a href="#">Help section</a>. If you have any further questions, feel free to contact the administrator John Doe (<a href="mailto:john.doe@example.com">john.doe@example.com)</a>. </p> <div class="tx-blogexample-list-container"> <p class="bodytext"> Below are the most recent posts: </p> <ul> <li> <h3> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[blog]=&tx_blogexample_pi1[action]=show&tx_blogexample_pi1[controller]=Post&cHash=003b0131ed">The Post #1</a> </h3> <p class="bodytext"> Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut... </p> <p class="metadata"> Published on 26.08.2009 by Jochen Rau </p> <p> Tags: [MVC] [Domain Driven Design] <br> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[action]=show&tx_blogexample_pi1[controller]=Post&cHash=f982643bc3">read more >></a><br> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[blog][uid]=70&tx_blogexample_pi1[action]=edit&tx_blogexample_pi1[controller]=Post&cHash=5b481bc8f0">Edit</a> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[blog][uid]=70&tx_blogexample_pi1[action]=delete&tx_blogexample_pi1[controller]=Post&cHash=4e52879656">Delete</a> </p> </li> </ul> <p> <a href="index.php?id=99&tx_blogexample_pi1[blog][uid]=70&tx_blogexample_pi1[action]=new&tx_blogexample_pi1[controller]=Post&cHash=2718a4b1a0">Create a new Post</a> </p> </div> <hr> <p> ? TYPO3 Association </p> '; $result = $this->cObj->cropHTML($subject, '300'); $expected = ' <h1>Blog Example</h1> <hr> <div class="csc-header csc-header-n1"> <h2 class="csc-firstHeader">Welcome to Blog #1</h2> </div> <p class="bodytext"> A blog about TYPO3 extension development. In order to start blogging, read the <a href="#">Help section</a>. If you have any further questions, feel free to contact the administrator John Doe (<a href="mailto:john.doe@example.com">john.doe@example.com)</a>. </p> <div class="tx-blogexample-list-container"> <p class="bodytext"> Below are the most recent posts: </p> <ul> <li> <h3> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[blog]=&tx_blogexample_pi1[action]=show&tx_blogexample_pi1[controller]=Post&cHash=003b0131ed">The Pos</a></h3></li></ul></div>'; $this->assertEquals($expected, $result); $result = $this->cObj->cropHTML($subject, '-100'); $expected = '<div class="tx-blogexample-list-container"><ul><li><p>Design] <br> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[action]=show&tx_blogexample_pi1[controller]=Post&cHash=f982643bc3">read more >></a><br> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[blog][uid]=70&tx_blogexample_pi1[action]=edit&tx_blogexample_pi1[controller]=Post&cHash=5b481bc8f0">Edit</a> <a href="index.php?id=99&tx_blogexample_pi1[post][uid]=211&tx_blogexample_pi1[blog][uid]=70&tx_blogexample_pi1[action]=delete&tx_blogexample_pi1[controller]=Post&cHash=4e52879656">Delete</a> </p> </li> </ul> <p> <a href="index.php?id=99&tx_blogexample_pi1[blog][uid]=70&tx_blogexample_pi1[action]=new&tx_blogexample_pi1[controller]=Post&cHash=2718a4b1a0">Create a new Post</a> </p> </div> <hr> <p> ? TYPO3 Association </p> '; $this->assertEquals($expected, $result); }
/** * Highlighting of terms within a text * * @param string $text Text * @param string|\Zend_Search_Lucene_Search_Query $search Terms to be highlighted (string or lucene search query) * @param int $crop Max. number of characters length * @param string $append Suffix in case of text being cropped at the end * @param string $prepend Prefix in case of text being cropped at the beginning * @param string $field Lucene document field to be used (if the search terms have to be found retroactively) * @return string Text with highlighting * @see http://www.mail-archive.com/fw-general@lists.zend.com/msg09013.html */ public function render($text = null, $search = null, $crop = null, $append = ' ...', $prepend = ' ... ', $field = 'bodytext') { $text = trim(strlen(trim($text)) ? $text : $this->renderChildren()); $terms = array(); // If there is a reasonable text given if (strlen($text)) { // If a list with search terms have been given ... if (is_array($search)) { $terms = $search; usort($terms, array($this, 'sortByLengthDesc')); // Else: If query hits have been given ... } elseif ($search instanceof \Tollwerk\TwLucenesearch\Domain\Model\QueryHits) { $terms = (array) $search->getHighlight($field); usort($terms, array($this, 'sortByLengthDesc')); // Else: If a lucene search query or a literal search term has been given } elseif ($search instanceof \Zend_Search_Lucene_Search_Query || strlen($search)) { // Instanciation of the lucene index service /* @var $indexerService \Tollwerk\TwLucenesearch\Service\Lucene */ $indexerService = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstanceService('index', 'lucene'); if ($indexerService instanceof \TYPO3\CMS\Core\Service\AbstractService) { // Converting search term to lucene search query if necessary if (!$search instanceof \Zend_Search_Lucene_Search_Query) { $search = $indexerService->query($search); } // If there is a valid lucene search query available if ($search instanceof \Zend_Search_Lucene_Search_Query) { $searchHash = md5("{$search}"); if (!array_key_exists($searchHash, self::$_queryTermCache)) { self::$_queryTermCache[$searchHash] = array(); foreach ($indexerService->getQueryTerms($search) as $termField => $fieldTerms) { usort($fieldTerms, array($this, 'sortByLengthDesc')); self::$_queryTermCache[$searchHash][$termField] = $fieldTerms; } } $terms = self::$_queryTermCache[$searchHash]; $field = trim($field); $terms = strlen($field) && array_key_exists($field, $terms) ? $terms[$field] : array(); } } } } // Check if the text has to be cropped ... $crop = $crop !== null && intval($crop) && strlen($text) > $crop ? intval($crop) : false; // If the text is more than 33% too long and highlighting has to be applied: Cropping also at the beginning of the text if ($crop && strlen($text) / $crop > 1.5 && count($terms)) { // Find the first highlighting in the text ... $firstHighlight = $this->firstMatch($terms, $text); // If there is at least one highlighting ... if ($firstHighlight !== false) { $beforeHighlight = strrev(trim(substr($text, 0, $firstHighlight))); // Keep the last 3 words before the highlighting ... $words = preg_split("%\\s+%", $beforeHighlight, 4); if (count($words) > 3) { $beforeHighlight = strrev(implode(' ', array_slice($words, 0, 3))); $text = $prepend . $beforeHighlight . ' ' . substr($text, $firstHighlight); } } } // If there are search terms to be highlighted in the text ... if (count($terms)) { $text = $this->highlight($terms, $text, $crop); } // If the text has to be cropped ... if ($crop) { if (TYPO3_MODE === 'BE') { $this->simulateFrontendEnvironment(); } $respectHtml = true; $text = $respectHtml ? $this->contentObject->cropHTML($text, $crop . '|' . $append . '|1') : $this->contentObject->crop($text, $crop . '|' . $append . '|1'); if (TYPO3_MODE === 'BE') { $this->resetFrontendEnvironment(); } } return $text; }