[FOLLOWUP][BUGFIX] Disable children escaping in CropViewHelper
[Packages/TYPO3.CMS.git] / typo3 / sysext / fluid / Classes / ViewHelpers / Format / CropViewHelper.php
1 <?php
2 namespace TYPO3\CMS\Fluid\ViewHelpers\Format;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Charset\CharsetConverter;
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
20 use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
21 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
22
23 /**
24 * Use this view helper to crop the text between its opening and closing tags.
25 *
26 * = Examples =
27 *
28 * <code title="Defaults">
29 * <f:format.crop maxCharacters="10">This is some very long text</f:format.crop>
30 * </code>
31 * <output>
32 * This is...
33 * </output>
34 *
35 * <code title="Custom suffix">
36 * <f:format.crop maxCharacters="17" append="&nbsp;[more]">This is some very long text</f:format.crop>
37 * </code>
38 * <output>
39 * This is some&nbsp;[more]
40 * </output>
41 *
42 * <code title="Don't respect word boundaries">
43 * <f:format.crop maxCharacters="10" respectWordBoundaries="false">This is some very long text</f:format.crop>
44 * </code>
45 * <output>
46 * This is so...
47 * </output>
48 *
49 * <code title="Don't respect HTML tags">
50 * <f:format.crop maxCharacters="28" respectWordBoundaries="false" respectHtml="false">This is some text with <strong>HTML</strong> tags</f:format.crop>
51 * </code>
52 * <output>
53 * This is some text with <stro
54 * </output>
55 *
56 * <code title="Inline notation">
57 * {someLongText -> f:format.crop(maxCharacters: 10)}
58 * </code>
59 * <output>
60 * someLongText cropped after 10 characters...
61 * (depending on the value of {someLongText})
62 * </output>
63 */
64 class CropViewHelper extends AbstractViewHelper
65 {
66 /**
67 * The output may contain HTML and can not be escaped
68 *
69 * @var bool
70 */
71 protected $escapeOutput = false;
72
73 /**
74 * @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController contains a backup of the current $GLOBALS['TSFE'] if used in BE mode
75 */
76 protected static $tsfeBackup;
77
78 /**
79 * Render the cropped text
80 *
81 * @param int $maxCharacters Place where to truncate the string
82 * @param string $append What to append, if truncation happened
83 * @param bool $respectWordBoundaries If TRUE and division is in the middle of a word, the remains of that word is removed.
84 * @param bool $respectHtml If TRUE the cropped string will respect HTML tags and entities. Technically that means, that cropHTML() is called rather than crop()
85 * @return string cropped text
86 */
87 public function render($maxCharacters, $append = '...', $respectWordBoundaries = true, $respectHtml = true)
88 {
89 return static::renderStatic(
90 array(
91 'maxCharacters' => $maxCharacters,
92 'append' => $append,
93 'respectWordBoundaries' => $respectWordBoundaries,
94 'respectHtml' => $respectHtml,
95 ),
96 $this->buildRenderChildrenClosure(),
97 $this->renderingContext
98 );
99 }
100
101 /**
102 * @param array $arguments
103 * @param callable $renderChildrenClosure
104 * @param RenderingContextInterface $renderingContext
105 * @return string
106 */
107 public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
108 {
109 $maxCharacters = $arguments['maxCharacters'];
110 $append = $arguments['append'];
111 $respectWordBoundaries = $arguments['respectWordBoundaries'];
112 $respectHtml = $arguments['respectHtml'];
113
114 $stringToTruncate = $renderChildrenClosure();
115 if (TYPO3_MODE === 'BE') {
116 self::simulateFrontendEnvironment();
117 }
118
119 // Even if we are in extbase/fluid context here, we're switching to a casual class of the framework here
120 // that has no dependency injection and other stuff. Therefor it is ok to use makeInstance instead of
121 // the ObjectManager here directly for additional performance
122 // Additionally, it would be possible to retrieve the "current" content object via ConfigurationManager->getContentObject(),
123 // but both crop() and cropHTML() are "nearly" static and do not depend on current content object settings, so
124 // it is safe to use a fresh instance here directly.
125 /** @var ContentObjectRenderer $contentObject */
126 $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
127 if ($respectHtml) {
128 $content = $contentObject->cropHTML($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries);
129 } else {
130 $content = $contentObject->crop($stringToTruncate, $maxCharacters . '|' . $append . '|' . $respectWordBoundaries);
131 }
132 if (TYPO3_MODE === 'BE') {
133 self::resetFrontendEnvironment();
134 }
135 return $content;
136 }
137
138 /**
139 * Sets the global variable $GLOBALS['TSFE']->csConvObj in Backend mode
140 * This somewhat hacky work around is currently needed because the crop() and cropHTML() functions of
141 * ContentObjectRenderer rely on those variables to be set
142 *
143 * @return void
144 */
145 protected static function simulateFrontendEnvironment()
146 {
147 self::$tsfeBackup = isset($GLOBALS['TSFE']) ? $GLOBALS['TSFE'] : null;
148 $GLOBALS['TSFE'] = new \stdClass();
149 // preparing csConvObj
150 if (!is_object($GLOBALS['TSFE']->csConvObj)) {
151 $GLOBALS['TSFE']->csConvObj = GeneralUtility::makeInstance(CharsetConverter::class);
152 }
153 }
154
155 /**
156 * Resets $GLOBALS['TSFE'] if it was previously changed by simulateFrontendEnvironment()
157 *
158 * @return void
159 * @see simulateFrontendEnvironment()
160 */
161 protected static function resetFrontendEnvironment()
162 {
163 $GLOBALS['TSFE'] = self::$tsfeBackup;
164 }
165 }