Passed
Push — master ( e7b45a...2ed7e8 )
by
unknown
13:15
created

TypolinkViewHelper::invokeContentObjectRenderer()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 31
rs 8.9617
c 0
b 0
f 0
cc 6
nc 16
nop 3
1
<?php
2
3
/*
4
 * This file is part of the TYPO3 CMS project.
5
 *
6
 * It is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License, either version 2
8
 * of the License, or any later version.
9
 *
10
 * For the full copyright and license information, please read the
11
 * LICENSE.txt file that was distributed with this source code.
12
 *
13
 * The TYPO3 project - inspiring people to share!
14
 */
15
16
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
17
18
use TYPO3\CMS\Core\Utility\GeneralUtility;
19
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
20
use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
21
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
22
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
23
use TYPO3Fluid\Fluid\Core\ViewHelper\Exception;
24
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
25
26
/**
27
 * A ViewHelper to create links from fields supported by the link wizard
28
 *
29
 * Example
30
 * =======
31
 *
32
 * ``{link}`` contains: ``t3://page?uid=2&arg1=val1#9 _blank some-css-class "Title containing Whitespace"``.
33
 *
34
 * Or a legacy version from older TYPO3 versions:
35
 * ``{link}`` contains: ``9 _blank - "testtitle with whitespace" &X=y``.
36
 *
37
 * Minimal usage
38
 * -------------
39
 *
40
 * ::
41
 *
42
 *    <f:link.typolink parameter="{link}">
43
 *       Linktext
44
 *    </f:link.typolink>
45
 *
46
 * Output::
47
 *
48
 *    <a href="/page/path/name.html?X=y" title="testtitle with whitespace" target="_blank">
49
 *       Linktext
50
 *    </a>
51
 *
52
 * Depending on current page, routing and page path configuration.
53
 *
54
 * TextWrap usage
55
 * --------------
56
 *
57
 * ::
58
 *
59
 *    <f:link.typolink parameter="123" textWrap="<span>|</span>"/>
60
 *
61
 * Output::
62
 *
63
 *    <a href="/some/page">
64
 *       <span>Page title of some page wrapped in span</span>
65
 *    </a>
66
 *
67
 * Depending on current page, routing and page path configuration.
68
 *
69
 * Full parameter usage
70
 * --------------------
71
 *
72
 * ::
73
 *
74
 *    <f:link.typolink parameter="{link}" additionalParams="&u=b"
75
 *        target="_blank"
76
 *        class="ico-class" title="some title"
77
 *        additionalAttributes="{type:'button'}"
78
 *    >
79
 *       Linktext
80
 *    </f:link.typolink>
81
 *
82
 * Output::
83
 *
84
 *    <a href="/page/path/name.html?X=y&u=b" title="some title" target="_blank" class="ico-class" type="button">
85
 *        Linktext
86
 *    </a>
87
 *
88
 * Depending on routing and page path configuration.
89
 */
90
class TypolinkViewHelper extends AbstractViewHelper
91
{
92
    use CompileWithRenderStatic;
93
94
    /**
95
     * @var bool
96
     */
97
    protected $escapeOutput = false;
98
99
    /**
100
     * Initialize ViewHelper arguments
101
     *
102
     * @throws Exception
103
     */
104
    public function initializeArguments()
105
    {
106
        $this->registerArgument('parameter', 'string', 'stdWrap.typolink style parameter string', true);
107
        $this->registerArgument('target', 'string', 'Define where to display the linked URL', false, '');
108
        $this->registerArgument('class', 'string', 'Define classes for the link element', false, '');
109
        $this->registerArgument('title', 'string', 'Define the title for the link element', false, '');
110
        $this->registerArgument('language', 'string', 'link to a specific language - defaults to the current language, use a language ID or "current" to enforce a specific language', false, null);
111
        $this->registerArgument('additionalParams', 'string', 'Additional query parameters to be attached to the resulting URL', false, '');
112
        $this->registerArgument('additionalAttributes', 'array', 'Additional tag attributes to be added directly to the resulting HTML tag', false, []);
113
        $this->registerArgument('addQueryString', 'bool', 'If set, the current query parameters will be kept in the URL', false, false);
114
        $this->registerArgument('addQueryStringMethod', 'string', 'This argument is not evaluated anymore and will be removed in TYPO3 v12.');
115
        $this->registerArgument('addQueryStringExclude', 'string', 'Define parameters to be excluded from the query string (only active if addQueryString is set)', false, '');
116
        $this->registerArgument('absolute', 'bool', 'Ensure the resulting URL is an absolute URL', false, false);
117
        $this->registerArgument('parts-as', 'string', 'Variable name containing typoLink parts (if any)', false, 'typoLinkParts');
118
        $this->registerArgument('textWrap', 'string', 'Wrap the link using the typoscript "wrap" data type', false, '');
119
    }
120
121
    /**
122
     * Render
123
     *
124
     * @param array $arguments
125
     * @param \Closure $renderChildrenClosure
126
     * @param RenderingContextInterface $renderingContext
127
     * @return mixed|string
128
     * @throws \InvalidArgumentException
129
     * @throws \UnexpectedValueException
130
     */
131
    public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
132
    {
133
        $parameter = $arguments['parameter'] ?? '';
134
        $partsAs = $arguments['parts-as'] ?? 'typoLinkParts';
135
136
        $typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
137
        $typoLinkConfiguration = $typoLinkCodec->decode($parameter);
138
        // Merge the $parameter with other arguments
139
        $mergedTypoLinkConfiguration = static::mergeTypoLinkConfiguration($typoLinkConfiguration, $arguments);
140
        $typoLinkParameter = $typoLinkCodec->encode($mergedTypoLinkConfiguration);
141
142
        // expose internal typoLink configuration to Fluid child context
143
        $variableProvider = $renderingContext->getVariableProvider();
144
        $variableProvider->add($partsAs, $typoLinkConfiguration);
145
        // If no link has to be rendered, the inner content will be returned as such
146
        $content = (string)$renderChildrenClosure();
147
        // clean up exposed variables
148
        $variableProvider->remove($partsAs);
149
150
        if ($parameter) {
151
            $content = static::invokeContentObjectRenderer($arguments, $typoLinkParameter, $content);
152
        }
153
        return $content;
154
    }
155
156
    protected static function invokeContentObjectRenderer(array $arguments, string $typoLinkParameter, string $content): string
157
    {
158
        if (isset($arguments['addQueryStringMethod'])) {
159
            trigger_error('Using the argument "addQueryStringMethod" in <f:link.typolink> ViewHelper has no effect anymore and will be removed in TYPO3 v12. Remove the argument in your fluid template, as it will result in a fatal error.', E_USER_DEPRECATED);
160
        }
161
        $addQueryString = $arguments['addQueryString'] ?? false;
162
        $addQueryStringExclude = $arguments['addQueryStringExclude'] ?? '';
163
        $absolute = $arguments['absolute'] ?? false;
164
        $aTagParams = static::serializeTagParameters($arguments);
165
166
        $instructions = [
167
            'parameter' => $typoLinkParameter,
168
            'ATagParams' => $aTagParams,
169
            'forceAbsoluteUrl' => $absolute,
170
        ];
171
        if (isset($arguments['language']) && $arguments['language'] !== null) {
172
            $instructions['language'] = $arguments['language'];
173
        }
174
        if ($addQueryString) {
175
            $instructions['addQueryString'] = $addQueryString;
176
            $instructions['addQueryString.'] = [
177
                'exclude' => $addQueryStringExclude,
178
            ];
179
        }
180
        if ((string)($arguments['textWrap'] ?? '') !== '') {
181
            $instructions['ATagBeforeWrap'] = true;
182
            $instructions['wrap'] = $arguments['textWrap'];
183
        }
184
185
        $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
186
        return $contentObject->stdWrap($content, ['typolink.' => $instructions]);
187
    }
188
189
    protected static function serializeTagParameters(array $arguments): string
190
    {
191
        // array(param1 -> value1, param2 -> value2) --> param1="value1" param2="value2" for typolink.ATagParams
192
        $extraAttributes = [];
193
        $additionalAttributes = $arguments['additionalAttributes'] ?? [];
194
        foreach ($additionalAttributes as $attributeName => $attributeValue) {
195
            $extraAttributes[] = $attributeName . '="' . htmlspecialchars($attributeValue) . '"';
196
        }
197
        return implode(' ', $extraAttributes);
198
    }
199
200
    /**
201
     * Merges view helper arguments with typolink parts.
202
     *
203
     * @param array $typoLinkConfiguration
204
     * @param array $arguments
205
     * @return array
206
     */
207
    protected static function mergeTypoLinkConfiguration(array $typoLinkConfiguration, array $arguments): array
208
    {
209
        if ($typoLinkConfiguration === []) {
210
            return $typoLinkConfiguration;
211
        }
212
213
        $target = $arguments['target'] ?? '';
214
        $class = $arguments['class'] ?? '';
215
        $title = $arguments['title'] ?? '';
216
        $additionalParams = $arguments['additionalParams'] ?? '';
217
218
        // Override target if given in target argument
219
        if ($target) {
220
            $typoLinkConfiguration['target'] = $target;
221
        }
222
        // Combine classes if given in both "parameter" string and "class" argument
223
        if ($class) {
224
            $classes = explode(' ', trim($typoLinkConfiguration['class']) . ' ' . trim($class));
225
            $typoLinkConfiguration['class'] = implode(' ', array_unique(array_filter($classes)));
226
        }
227
        // Override title if given in title argument
228
        if ($title) {
229
            $typoLinkConfiguration['title'] = $title;
230
        }
231
        // Combine additionalParams
232
        if ($additionalParams) {
233
            $typoLinkConfiguration['additionalParams'] .= $additionalParams;
234
        }
235
236
        return $typoLinkConfiguration;
237
    }
238
}
239