[!!!][TASK] Allow quotes in titles of typolink 62/40162/12
authorMarkus Klein <markus.klein@typo3.org>
Wed, 10 Jun 2015 22:59:34 +0000 (00:59 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 29 Jun 2015 19:59:52 +0000 (21:59 +0200)
Introduce proper handling of quotes in titles of typolinks.

Example title: 'Here "I\\" am'
Expected link field value: '15 - - "Here \"I\\\\\" am"'

Do not use the GeneralUtility::unQuoteFilenames() method anymore, since
it was not made for the purpose to decode CSV-like strings.
Use the newly introduced TypoLinkCodecService instead.

Resolves: #55759
Releases: master
Change-Id: Iea612842b4b9c70924ecf56b87513350dd9383a8
Reviewed-on: http://review.typo3.org/40162
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
14 files changed:
typo3/sysext/core/Classes/Database/SoftReferenceIndex.php
typo3/sysext/core/Classes/Html/RteHtmlParser.php
typo3/sysext/core/Classes/Utility/GeneralUtility.php
typo3/sysext/core/Documentation/Changelog/master/Breaking-55759-HTMLInLinkTitlesNotWorkingAnymore.rst [new file with mode: 0644]
typo3/sysext/core/Tests/Unit/Utility/GeneralUtilityTest.php
typo3/sysext/fluid/Classes/ViewHelpers/Link/TypolinkViewHelper.php
typo3/sysext/fluid/Classes/ViewHelpers/Uri/TypolinkViewHelper.php
typo3/sysext/fluid/Tests/Unit/ViewHelpers/Link/TypolinkViewHelperTest.php
typo3/sysext/fluid/Tests/Unit/ViewHelpers/Uri/TypolinkViewHelperTest.php
typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
typo3/sysext/impexp/Tests/Functional/Fixtures/ImportExportXml/pages-and-ttcontent-with-rte-image-n-file-link.xml
typo3/sysext/impexp/Tests/Functional/Import/PagesAndTtContentWithRteImagesAndFileLink/DataSet/Assertion/importPagesAndRelatedTtContentWithRteImagesAndFileLink.csv
typo3/sysext/recordlist/Classes/Browser/ElementBrowser.php
typo3/sysext/rtehtmlarea/Classes/BrowseLinks.php

index 4422466..add802f 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Core\Database;
  */
 
 use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 
 /**
  * Soft Reference processing class
@@ -542,35 +543,12 @@ class SoftReferenceIndex {
         * @see \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::typolink(), setTypoLinkPartsElement()
         */
        public function getTypoLinkParts($typolinkValue) {
-               $finalTagParts = array();
-               $browserTarget = '';
-               $cssClass = '';
-               $titleAttribute = '';
-               $additionalParams = '';
-               // Split into link / target / class / title / additionalParams
-               $linkParameter = GeneralUtility::unQuoteFilenames($typolinkValue, TRUE);
-               // Link parameter value
-               $link_param = trim($linkParameter[0]);
-               // Target value
-               if (isset($linkParameter[1])) {
-                       $browserTarget = trim($linkParameter[1]);
-               }
-               // Link class
-               if (isset($linkParameter[2])) {
-                       $cssClass = trim($linkParameter[2]);
-               }
-               // Title value
-               if (isset($linkParameter[3])) {
-                       $titleAttribute = trim($linkParameter[3]);
-               }
-               if (isset($linkParameter[4]) && trim($linkParameter[4]) !== '') {
-                       $additionalParams = trim($linkParameter[4]);
-               }
-               // set all tag parts because setTypoLinkPartsElement() rely on them
-               $finalTagParts['target'] = $browserTarget;
-               $finalTagParts['class'] = $cssClass;
-               $finalTagParts['title'] = $titleAttribute;
-               $finalTagParts['additionalParams'] = $additionalParams;
+
+               $finalTagParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typolinkValue);
+
+               $link_param = $finalTagParts['url'];
+               // we define various keys below, "url" might be misleading
+               unset($finalTagParts['url']);
 
                // Parse URL:
                $pU = @parse_url($link_param);
@@ -781,18 +759,9 @@ class SoftReferenceIndex {
                                }
                }
                // Finally, for all entries that was rebuild with tokens, add target, class, title and additionalParams in the end:
-               if ($content !== '' && isset($tLP['target']) && $tLP['target'] !== '') {
-                       $content .= ' ' . $tLP['target'];
-                       if (isset($tLP['class']) && $tLP['class'] !== '') {
-                               $content .= ' "' . $tLP['class'] . '"';
-                               if (isset($tLP['title']) && $tLP['title'] !== '') {
-                                       $content .= ' "' . $tLP['title'] . '"';
-                                       if (isset($tLP['additionalParams']) && $tLP['additionalParams'] !== '') {
-                                               $content .= ' ' . $tLP['additionalParams'];
-                                       }
-                               }
-                       }
-               }
+               $tLP['url'] = $content;
+               $content = GeneralUtility::makeInstance(TypoLinkCodecService::class)->encode($tLP);
+
                // Return rebuilt typolink value:
                return $content;
        }
index 3bdf0ce..5f04dff 100644 (file)
@@ -18,6 +18,7 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\StringUtility;
 use TYPO3\CMS\Core\Resource;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 
 /**
  * Class for parsing HTML for the Rich Text Editor. (also called transformations)
@@ -665,9 +666,11 @@ class RteHtmlParser extends \TYPO3\CMS\Core\Html\HtmlParser {
                        $external = FALSE;
                        // Block
                        if ($k % 2) {
-                               $tagCode = GeneralUtility::unQuoteFilenames(trim(substr($this->getFirstTag($v), 0, -1)), TRUE);
-                               $link_param = $tagCode[1];
-                               $href = '';
+                               // split away the first "<link" part
+                               $typolink = explode(' ', substr($this->getFirstTag($v), 0, -1), 2)[1];
+                               $tagCode = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typolink);
+
+                               $link_param = $tagCode['url'];
                                // Parsing the typolink data. This parsing is roughly done like in \TYPO3\CMS\Frontend\ContentObject->typolink()
                                // Parse URL:
                                $pU = parse_url($link_param);
@@ -755,7 +758,12 @@ class RteHtmlParser extends \TYPO3\CMS\Core\Html\HtmlParser {
                                        }
                                }
                                // Setting the A-tag:
-                               $bTag = '<a href="' . htmlspecialchars($href) . '"' . ($tagCode[2] && $tagCode[2] != '-' ? ' target="' . htmlspecialchars($tagCode[2]) . '"' : '') . ($tagCode[3] && $tagCode[3] != '-' ? ' class="' . htmlspecialchars($tagCode[3]) . '"' : '') . ($tagCode[4] ? ' title="' . htmlspecialchars($tagCode[4]) . '"' : '') . ($external ? ' data-htmlarea-external="1"' : '') . ($error ? ' rteerror="' . htmlspecialchars($error) . '" style="background-color: yellow; border:2px red solid; color: black;"' : '') . '>';
+                               $bTag = '<a href="' . htmlspecialchars($href) . '"'
+                                       . ($tagCode['target'] ? ' target="' . htmlspecialchars($tagCode['target']) . '"' : '')
+                                       . ($tagCode['class'] ? ' class="' . htmlspecialchars($tagCode['class']) . '"' : '')
+                                       . ($tagCode['title'] ? ' title="' . htmlspecialchars($tagCode['title']) . '"' : '')
+                                       . ($external ? ' data-htmlarea-external="1"' : '')
+                                       . ($error ? ' rteerror="' . htmlspecialchars($error) . '" style="background-color: yellow; border:2px red solid; color: black;"' : '') . '>';
                                $eTag = '</a>';
                                // Modify parameters
                                if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksRte_PostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksRte_PostProc'])) {
index 9f95bd8..28552b7 100755 (executable)
@@ -5050,7 +5050,9 @@ Connection: close
        }
 
        /**
-        * Explode a string (normally a list of filenames) with whitespaces by considering quotes in that string. This is mostly needed by the imageMagickCommand function above.
+        * Explode a string (normally a list of filenames) with whitespaces by considering quotes in that string.
+        *
+        * This is mostly needed by the imageMagickCommand function above.
         *
         * @param string $parameters The whole parameters string
         * @param bool $unQuote If set, the elements of the resulting array are unquoted.
@@ -5076,8 +5078,8 @@ Connection: close
                }
                if ($unQuote) {
                        foreach ($paramsArr as $key => &$val) {
-                               $val = preg_replace('/(^"|"$)/', '', $val);
-                               $val = preg_replace('/(^\'|\'$)/', '', $val);
+                               $val = preg_replace('/(?:^"|"$)/', '', $val);
+                               $val = preg_replace('/(?:^\'|\'$)/', '', $val);
                        }
                        unset($val);
                }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Breaking-55759-HTMLInLinkTitlesNotWorkingAnymore.rst b/typo3/sysext/core/Documentation/Changelog/master/Breaking-55759-HTMLInLinkTitlesNotWorkingAnymore.rst
new file mode 100644 (file)
index 0000000..f983eb6
--- /dev/null
@@ -0,0 +1,32 @@
+==========================================================
+Breaking: #55759 - HTML in link titles not working anymore
+==========================================================
+
+Description
+===========
+
+By introducing proper handling of double quotes in link titles (TypoLink fields) the processing of the link title is adjusted. Escaping will be done automatically now.
+
+
+Impact
+======
+
+Existing link titles, which contain HTML escape sequences, will not be shown correctly anymore in Frontend.
+
+Example: A link title ``Some &quot;special&quot; title`` will be output as ``Some &amp;quot;special&amp;quot; title``
+
+
+Affected Installations
+======================
+
+Any installation using links with titles containing HTML escape sequences like ``&quot;`` or ``&gt;``
+
+
+Migration
+=========
+
+Change the affected link titles to contain the plain characters, the correct encoding will be taken care of automatically.
+
+Example: ``Some "special" title``
+
+If you need to encode a TypoLink manually in code, use the ``TypoLinkCodecService`` class, which provides a convenient way to encode a TypoLink from its fragments.
index 1292e22..fca6517 100644 (file)
@@ -3457,6 +3457,11 @@ class GeneralUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                return array(
                        // Some theoretical tests first
                        array(
+                               '',
+                               array(),
+                               array()
+                       ),
+                       array(
                                'aa bb "cc" "dd"',
                                array('aa', 'bb', '"cc"', '"dd"'),
                                array('aa', 'bb', 'cc', 'dd')
@@ -3608,14 +3613,14 @@ class GeneralUtilityTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * Tests if the commands are exploded and unquoted correctly
         *
-        * @dataProvider        imageMagickCommandsDataProvider
+        * @dataProvider imageMagickCommandsDataProvider
         * @test
         */
        public function explodeAndUnquoteImageMagickCommands($source, $expectedQuoted, $expectedUnquoted) {
                $actualQuoted = Utility\GeneralUtility::unQuoteFilenames($source);
-               $acutalUnquoted = Utility\GeneralUtility::unQuoteFilenames($source, TRUE);
+               $actualUnquoted = Utility\GeneralUtility::unQuoteFilenames($source, TRUE);
                $this->assertEquals($expectedQuoted, $actualQuoted, 'The exploded command does not match the expected');
-               $this->assertEquals($expectedUnquoted, $acutalUnquoted, 'The exploded and unquoted command does not match the expected');
+               $this->assertEquals($expectedUnquoted, $actualUnquoted, 'The exploded and unquoted command does not match the expected');
        }
 
        ///////////////////////////////
index 514d81d..3047e0c 100644 (file)
@@ -19,6 +19,7 @@ use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
 use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 
 /**
  * A ViewHelper to create links from fields supported by the link wizard
@@ -116,7 +117,7 @@ class TypolinkViewHelper extends AbstractViewHelper implements CompilableInterfa
                                $content,
                                array(
                                        'typolink.' => array(
-                                               'parameter' => implode(' ', $typolinkParameter),
+                                               'parameter' => $typolinkParameter,
                                                'ATagParams' => $aTagParams,
                                        )
                                )
@@ -129,61 +130,45 @@ class TypolinkViewHelper extends AbstractViewHelper implements CompilableInterfa
        /**
         * Transforms ViewHelper arguments to typo3link.parameters.typoscript option as array.
         *
-        * @param string $parameter Example: 19 _blank - "testtitle with whitespace" &X=y
+        * @param string $parameter Example: 19 _blank - "testtitle \"with whitespace\"" &X=y
         * @param string $target
         * @param string $class
         * @param string $title
         * @param string $additionalParams
         *
-        * @return array Final merged typolink.parameter as array to be imploded with empty string later
+        * @return string The final TypoLink string
         */
        static protected function createTypolinkParameterArrayFromArguments($parameter, $target = '', $class = '', $title = '', $additionalParams = '') {
-               // Explode $parameter by whitespace and remove any " around resulting array values
-               $parameterArray = GeneralUtility::unQuoteFilenames($parameter, TRUE);
-
-               if (empty($parameterArray)) {
-                       return array();
+               $typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
+               $typolinkConfiguration = $typoLinkCodec->decode($parameter);
+               if (empty($typolinkConfiguration)) {
+                       return $typolinkConfiguration;
                }
 
-               // Extend to 4 elements
-               $typolinkConfiguration = array_pad($parameterArray, 4, '-');
-
                // Override target if given in target argument
                if ($target) {
-                       $typolinkConfiguration[1] = $target;
+                       $typolinkConfiguration['target'] = $target;
                }
 
                // Combine classes if given in both "parameter" string and "class" argument
                if ($class) {
-                       $typolinkConfiguration[2] = $typolinkConfiguration[2] !== '-' ? $typolinkConfiguration[2] . ' ' : '';
-                       $typolinkConfiguration[2] .= $class;
+                       if ($typolinkConfiguration['class']) {
+                               $typolinkConfiguration['class'] .= ' ';
+                       }
+                       $typolinkConfiguration['class'] .= $class;
                }
 
                // Override title if given in title argument
                if ($title) {
-                       $typolinkConfiguration[3] = $title;
+                       $typolinkConfiguration['title'] = $title;
                }
 
                // Combine additionalParams
                if ($additionalParams) {
-                       $typolinkConfiguration[4] .= $additionalParams;
-               }
-
-               // Unset unused parameters again from the end, wrap all given values with "
-               $reverseSortedParameters = array_reverse($typolinkConfiguration, TRUE);
-               $aValueWasSet = FALSE;
-               foreach ($reverseSortedParameters as $position => $value) {
-                       if ($value === '-' && !$aValueWasSet) {
-                               unset($typolinkConfiguration[$position]);
-                       } else {
-                               $aValueWasSet = TRUE;
-                               if ($value !== '-') {
-                                       $typolinkConfiguration[$position] = '"' . $value . '"';
-                               }
-                       }
+                       $typolinkConfiguration['additionalParams'] .= $additionalParams;
                }
 
-               return $typolinkConfiguration;
+               return $typoLinkCodec->encode($typolinkConfiguration);
        }
 
 }
index a07ced3..fce751f 100644 (file)
@@ -19,6 +19,7 @@ use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper;
 use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 
 /**
  * A ViewHelper to create uris from fields supported by the link wizard
@@ -77,16 +78,12 @@ class TypolinkViewHelper extends AbstractViewHelper implements CompilableInterfa
                $parameter = $arguments['parameter'];
                $additionalParams = $arguments['additionalParams'];
 
-               // Merge the $parameter with other arguments
-               $typolinkParameter = self::createTypolinkParameterArrayFromArguments($parameter, $additionalParams);
-
                $content = '';
-
                if ($parameter) {
                        $contentObject = GeneralUtility::makeInstance(ContentObjectRenderer::class);
                        $content = $contentObject->typoLink_URL(
                                array(
-                                       'parameter' => implode(' ', $typolinkParameter),
+                                       'parameter' => self::createTypolinkParameterArrayFromArguments($parameter, $additionalParams),
                                )
                        );
                }
@@ -100,38 +97,21 @@ class TypolinkViewHelper extends AbstractViewHelper implements CompilableInterfa
         * @param string $parameter Example: 19 _blank - "testtitle with whitespace" &X=y
         * @param string $additionalParameters
         *
-        * @return array Final merged typolink.parameter as array to be imploded with empty string later
+        * @return string The final TypoLink string
         */
        static protected function createTypolinkParameterArrayFromArguments($parameter, $additionalParameters = '') {
-               // Explode $parameter by whitespace and remove any " around resulting array values
-               $parameterArray = GeneralUtility::unQuoteFilenames($parameter, TRUE);
-
-               if (empty($parameterArray)) {
-                       return array();
+               $typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
+               $typolinkConfiguration = $typoLinkCodec->decode($parameter);
+               if (empty($typolinkConfiguration)) {
+                       return $typolinkConfiguration;
                }
 
-               // Extend to 4 elements
-               $typolinkConfiguration = array_pad($parameterArray, 4, '-');
-
-               // Combine additionalParameters
+               // Combine additionalParams
                if ($additionalParameters) {
-                       $typolinkConfiguration[4] .= $additionalParameters;
+                       $typolinkConfiguration['additionalParams'] .= $additionalParameters;
                }
 
-               // Unset unused parameters again from the end, wrap all given values with "
-               $reverseSortedParameters = array_reverse($typolinkConfiguration, TRUE);
-               $aValueWasSet = FALSE;
-               foreach ($reverseSortedParameters as $position => $value) {
-                       if ($value === '-' && !$aValueWasSet) {
-                               unset($typolinkConfiguration[$position]);
-                       } else {
-                               $aValueWasSet = TRUE;
-                               if ($value !== '-') {
-                                       $typolinkConfiguration[$position] = '"' . $value . '"';
-                               }
-                       }
-               }
-               return $typolinkConfiguration;
+               return $typoLinkCodec->encode($typolinkConfiguration);
        }
 
 }
index 5be197e..b0c63b2 100644 (file)
@@ -18,6 +18,7 @@ use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext;
 use TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\ViewHelperBaseTestcase;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Fluid\ViewHelpers\Link\TypolinkViewHelper;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
 
 /**
  * Class TypolinkViewHelperTest
@@ -25,10 +26,13 @@ use TYPO3\CMS\Fluid\ViewHelpers\Link\TypolinkViewHelper;
 class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
 
        /**
-        * @var TypolinkViewHelper|\PHPUnit_Framework_MockObject_MockObject $subject
+        * @var TypolinkViewHelper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
         */
        protected $subject;
 
+       /**
+        * @throws \InvalidArgumentException
+        */
        public function setUp() {
                $this->subject = $this->getAccessibleMock(TypolinkViewHelper::class, array('renderChildren'));
                /** @var RenderingContext  $renderingContext */
@@ -41,9 +45,9 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
         */
        public function renderReturnsResultOfContentObjectRenderer() {
                $this->subject->expects($this->any())->method('renderChildren')->will($this->returnValue('innerContent'));
-               $contentObjectRendererMock = $this->getMock(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class, array(), array(), '', FALSE);
+               $contentObjectRendererMock = $this->getMock(ContentObjectRenderer::class, array(), array(), '', FALSE);
                $contentObjectRendererMock->expects($this->once())->method('stdWrap')->will($this->returnValue('foo'));
-               GeneralUtility::addInstance(\TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer::class, $contentObjectRendererMock);
+               GeneralUtility::addInstance(ContentObjectRenderer::class, $contentObjectRendererMock);
                $this->assertEquals('foo', $this->subject->render('42'));
        }
 
@@ -58,7 +62,7 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                '', // class from fluid
                                '', // title from fluid
                                '', // additional parameters from fluid
-                               array(),
+                               '',
                        ),
                        'simple id input' => array(
                                19,
@@ -66,9 +70,7 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                '',
                                '',
                                '',
-                               array(
-                                       0 => '"19"',
-                               ),
+                               '19',
                        ),
                        'external url with target' => array(
                                'www.web.de _blank',
@@ -76,22 +78,7 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                '',
                                '',
                                '',
-                               array(
-                                       0 => '"www.web.de"',
-                                       1 => '"_blank"',
-                               ),
-                       ),
-                       'page with class' => array(
-                               '42 - css-class',
-                               '',
-                               '',
-                               '',
-                               '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '"css-class"',
-                               ),
+                               'www.web.de _blank',
                        ),
                        'page with extended class' => array(
                                '42 - css-class',
@@ -99,24 +86,7 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                'fluid_class',
                                '',
                                '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '"css-class fluid_class"',
-                               ),
-                       ),
-                       'page with title' => array(
-                               '42 - - "a link title"',
-                               '',
-                               '',
-                               '',
-                               '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"a link title"'
-                               )
+                               '42 - "css-class fluid_class"',
                        ),
                        'page with overridden title' => array(
                                '42 - - "a link title"',
@@ -124,40 +94,23 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                '',
                                'another link title',
                                '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"another link title"',
-                               ),
+                               '42 - - "another link title"',
                        ),
-                       'page with title and parameters' => array(
+                       'page with title and extended parameters' => array(
                                '42 - - "a link title" &x=y',
                                '',
                                '',
                                '',
-                               '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"a link title"',
-                                       4 => '"&x=y"',
-                               ),
+                               '&a=b',
+                               '42 - - "a link title" &x=y&a=b',
                        ),
-                       'page with title and extended parameters' => array(
-                               '42 - - "a link title" &x=y',
+                       'page with complex title and extended parameters' => array(
+                               '42 - - "a \\"link\\" title with \\\\" &x=y',
                                '',
                                '',
                                '',
                                '&a=b',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"a link title"',
-                                       4 => '"&x=y&a=b"',
-                               ),
+                               '42 - - "a \\"link\\" title with \\\\" &x=y&a=b',
                        ),
                        'full parameter usage' => array(
                                '19 _blank css-class "testtitle with whitespace" &X=y',
@@ -165,13 +118,7 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                'fluid_class',
                                'a new title',
                                '&a=b',
-                               array(
-                                       0 => '"19"',
-                                       1 => '-',
-                                       2 => '"css-class fluid_class"',
-                                       3 => '"a new title"',
-                                       4 => '"&X=y&a=b"',
-                               ),
+                               '19 - "css-class fluid_class" "a new title" &X=y&a=b',
                        ),
                        'only page id and overwrite' => array(
                                '42',
@@ -179,23 +126,7 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                                '',
                                '',
                                '&a=b',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '-',
-                                       4 => '"&a=b"',
-                               ),
-                       ),
-                       'email' => array(
-                               'a@b.tld',
-                               '',
-                               '',
-                               '',
-                               '',
-                               array(
-                                       '"a@b.tld"',
-                               ),
+                               '42 - - - &a=b',
                        ),
                );
        }
@@ -203,6 +134,12 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
        /**
         * @test
         * @dataProvider typoScriptConfigurationData
+        * @param string $input
+        * @param string $targetFromFluid
+        * @param string $classFromFluid
+        * @param string $titleFromFluid
+        * @param string $additionalParametersFromFluid
+        * @param string $expected
         */
        public function createTypolinkParameterArrayFromArgumentsReturnsExpectedArray($input, $targetFromFluid, $classFromFluid, $titleFromFluid, $additionalParametersFromFluid, $expected) {
                $result = $this->subject->_call('createTypolinkParameterArrayFromArguments', $input, $targetFromFluid, $classFromFluid, $titleFromFluid, $additionalParametersFromFluid);
index df794bb..c7805c9 100644 (file)
@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\Uri;
  *                                                                        */
 
 use TYPO3\CMS\Fluid\Tests\Unit\ViewHelpers\ViewHelperBaseTestcase;
+use TYPO3\CMS\Fluid\ViewHelpers\Uri\TypolinkViewHelper;
 
 /**
  * Class TypolinkViewHelperTest
@@ -29,102 +30,42 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
                        'empty input' => array(
                                '', // input from link field
                                '', // additional parameters from fluid
-                               array(), //expected typolink Array
-                '' // expected URI
+                               '', //expected typolink
                        ),
                        'simple id input' => array(
                                19,
                                '',
-                               array(
-                                       0 => '"19"',
-                               ),
-                'index.php?id=19'
+                               '19',
                        ),
                        'external url with target' => array(
                                'www.web.de _blank',
                                '',
-                               array(
-                                       0 => '"www.web.de"',
-                                       1 => '"_blank"',
-                               ),
-                'http://www.web.de'
+                               'www.web.de _blank',
                        ),
                        'page with class' => array(
                                '42 - css-class',
                                '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '"css-class"',
-                               ),
-                "index.php?id=42"
+                               '42 - css-class',
                        ),
                        'page with title' => array(
                                '42 - - "a link title"',
                                '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"a link title"'
-                               ),
-                "index.php?id=42"
+                               '42 - - "a link title"',
                        ),
                        'page with title and parameters' => array(
                                '42 - - "a link title" &x=y',
                                '',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"a link title"',
-                                       4 => '"&x=y"',
-                               ),
-                "index.php?id=42&x=y"
+                               '42 - - "a link title" &x=y',
                        ),
                        'page with title and extended parameters' => array(
                                '42 - - "a link title" &x=y',
                                '&a=b',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '"a link title"',
-                                       4 => '"&x=y&a=b"',
-                               ),
-                "index.php?id=42&x=y&a=b"
-                       ),
-                       'full parameter usage' => array(
-                               '19 _blank css-class "testtitle with whitespace" &X=y',
-                               '&a=b',
-                               array(
-                                       0 => '"19"',
-                                       1 => '"_blank"',
-                                       2 => '"css-class"',
-                                       3 => '"testtitle with whitespace"',
-                                       4 => '"&X=y&a=b"',
-                               ),
-                "index.php?id=19&X=y&a=b"
+                               '42 - - "a link title" &x=y&a=b',
                        ),
                        'only page id and overwrite' => array(
                                '42',
                                '&a=b',
-                               array(
-                                       0 => '"42"',
-                                       1 => '-',
-                                       2 => '-',
-                                       3 => '-',
-                                       4 => '"&a=b"',
-                               ),
-                "index.php?id=42&a=b"
-                       ),
-                       'email' => array(
-                               'a@b.tld',
-                               '',
-                               array(
-                                       '"a@b.tld"',
-                               ),
-                'mailto:a@b.tld'
+                               '42 - - - &a=b',
                        ),
                );
        }
@@ -132,10 +73,14 @@ class TypolinkViewHelperTest extends ViewHelperBaseTestcase {
        /**
         * @test
         * @dataProvider typoScriptConfigurationData
+        * @param string $input
+        * @param string $additionalParametersFromFluid
+        * @param string $expected
+        * @throws \InvalidArgumentException
         */
        public function createTypolinkParameterArrayFromArgumentsReturnsExpectedArray($input, $additionalParametersFromFluid, $expected) {
                /** @var \TYPO3\CMS\Fluid\ViewHelpers\Uri\TypolinkViewHelper|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface $subject */
-               $subject = $this->getAccessibleMock('TYPO3\\CMS\\Fluid\\ViewHelpers\\Uri\\TypolinkViewHelper', array('dummy'));
+               $subject = $this->getAccessibleMock(TypolinkViewHelper::class, array('dummy'));
                $result = $subject->_call('createTypolinkParameterArrayFromArguments', $input, $additionalParametersFromFluid);
                $this->assertSame($expected, $result);
        }
index 2a46d25..0926f8d 100644 (file)
@@ -46,6 +46,7 @@ use TYPO3\CMS\Frontend\ContentObject\Exception\ExceptionHandlerInterface;
 use TYPO3\CMS\Frontend\Page\CacheHashCalculator;
 use TYPO3\CMS\Frontend\Page\PageRepository;
 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 
 /**
  * This class contains all main TypoScript features.
@@ -5964,10 +5965,10 @@ class ContentObjectRenderer {
                $linkParameter = NULL;
 
                // Link parameter value = first part
-               $linkParameterParts = GeneralUtility::unQuoteFilenames($mixedLinkParameter, TRUE);
+               $linkParameterParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($mixedLinkParameter);
 
                // Check for link-handler keyword:
-               list($linkHandlerKeyword, $linkHandlerValue) = explode(':', trim($linkParameterParts[0]), 2);
+               list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $linkParameterParts['url'], 2);
                if ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword] && (string)$linkHandlerValue !== '') {
                        $linkHandlerObj = GeneralUtility::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$linkHandlerKeyword]);
                        if (method_exists($linkHandlerObj, 'main')) {
@@ -5976,7 +5977,7 @@ class ContentObjectRenderer {
                }
 
                // Resolve FAL-api "file:UID-of-sys_file-record" and "file:combined-identifier"
-               if ($linkHandlerKeyword === 'file' && !StringUtility::beginsWith($linkParameterParts[0], 'file://')) {
+               if ($linkHandlerKeyword === 'file' && !StringUtility::beginsWith($linkParameterParts['url'], 'file://')) {
                        try {
                                $fileOrFolderObject = $this->getResourceFactory()->retrieveFileOrFolderObject($linkHandlerValue);
                                // Link to a folder or file
@@ -5993,46 +5994,21 @@ class ContentObjectRenderer {
                                return $linkText;
                        }
                } else {
-                       $linkParameter = trim($linkParameterParts[0]);
-               }
-
-               // Target value = second Part (overriding the default target)
-               $linkTarget = trim($linkParameterParts[1]);
-               // The '-' character means 'no target'. Necessary in order to
-               // specify a class as third parameter without setting the target!
-               if ($linkTarget == '-') {
-                       $linkTarget = '';
-               }
-
-               // Link class
-               $linkClass = trim($linkParameterParts[2]);
-
-               // The '-' character means 'no class'. Necessary in order to specify a
-               // title as fourth parameter without setting the target or class!
-               if ($linkClass == '-') {
-                       $linkClass = '';
-               }
-
-               // Title value
-               $forceTitle = trim($linkParameterParts[3]);
-               // The '-' character means 'no title'. Necessary in order to specify
-               // further parameters without setting the title!
-               if ($forceTitle == '-') {
-                       $forceTitle = '';
+                       $linkParameter = $linkParameterParts['url'];
                }
 
                // additional parameters that need to be set
-               if (isset($linkParameterParts[4]) && trim($linkParameterParts[4]) !== "") {
-                       $forceParams = trim($linkParameterParts[4]);
+               if ($linkParameterParts['additionalParams'] !== '') {
+                       $forceParams = $linkParameterParts['additionalParams'];
                        // params value
                        $configuration['additionalParams'] .= $forceParams[0] == '&' ? $forceParams : '&' . $forceParams;
                }
 
                return array(
                        'href'   => $linkParameter,
-                       'target' => $linkTarget,
-                       'class'  => $linkClass,
-                       'title'  => $forceTitle
+                       'target' => $linkParameterParts['target'],
+                       'class'  => $linkParameterParts['class'],
+                       'title'  => $linkParameterParts['title']
                );
        }
 
@@ -6492,14 +6468,14 @@ class ContentObjectRenderer {
                                        $target = '';
                                }
                                $onClick = 'vHWin=window.open(' . GeneralUtility::quoteJSvalue($GLOBALS['TSFE']->baseUrlWrap($finalTagParts['url']), TRUE) . ',\'FEopenLink\',' . GeneralUtility::quoteJSvalue($JSwindowParams) . ');vHWin.focus();return false;';
-                               $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"' . $target . ' onclick="' . htmlspecialchars($onClick) . '"' . ($title ? ' title="' . $title . '"' : '') . ($linkClass ? ' class="' . $linkClass . '"' : '') . $finalTagParts['aTagParams'] . '>';
+                               $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"' . $target . ' onclick="' . htmlspecialchars($onClick) . '"' . ($title ? ' title="' . htmlspecialchars($title) . '"' : '') . ($linkClass ? ' class="' . $linkClass . '"' : '') . $finalTagParts['aTagParams'] . '>';
                        } else {
                                if ($GLOBALS['TSFE']->spamProtectEmailAddresses === 'ascii' && $linkType === 'mailto') {
                                        $finalAnchorTag = '<a href="' . $finalTagParts['url'] . '"';
                                } else {
                                        $finalAnchorTag = '<a href="' . htmlspecialchars($finalTagParts['url']) . '"';
                                }
-                               $finalAnchorTag .= ($title ? ' title="' . $title . '"' : '') . $finalTagParts['targetParams'] . ($linkClass ? ' class="' . $linkClass . '"' : '') . $finalTagParts['aTagParams'] . '>';
+                               $finalAnchorTag .= ($title ? ' title="' . htmlspecialchars($title) . '"' : '') . $finalTagParts['targetParams'] . ($linkClass ? ' class="' . $linkClass . '"' : '') . $finalTagParts['aTagParams'] . '>';
                        }
 
                        // Call user function:
index a31553d..18406f2 100644 (file)
                                                                </softref_element>
                                                        </softref_key>
                                                </keys>
-                                               <tokenizedContent>&lt;img src=&quot;fileadmin/_processed_/csm_typo3_image2_5c2670fd59.jpg&quot; title=&quot;typo3_image2.jpg&quot; data-htmlarea-file-uid=&quot;{softref:00e731fe86bdecc2a8f81302b206db00}&quot; height=&quot;225&quot; width=&quot;300&quot; alt=&quot;&quot; style=&quot;&quot; /&gt;&amp;nbsp;&amp;nbsp;And here a &lt;LINK {softref:747a4a59a9f66015a82d1648e919e0b8} - "download" "Initiates file download"&gt;link to an image&lt;/link&gt;.</tokenizedContent>
+                                               <tokenizedContent>&lt;img src=&quot;fileadmin/_processed_/csm_typo3_image2_5c2670fd59.jpg&quot; title=&quot;typo3_image2.jpg&quot; data-htmlarea-file-uid=&quot;{softref:00e731fe86bdecc2a8f81302b206db00}&quot; height=&quot;225&quot; width=&quot;300&quot; alt=&quot;&quot; style=&quot;&quot; /&gt;&amp;nbsp;&amp;nbsp;And here a &lt;LINK {softref:747a4a59a9f66015a82d1648e919e0b8} - download "Initiates file download"&gt;link to an image&lt;/link&gt;.</tokenizedContent>
                                        </softrefs>
                                </field>
                        </related>
@@ -583,4 +583,4 @@ tiNwZbSZIYCCxuK0ABsLFY0aTIrYWKxkjYuQsVgSZAQAw//Z
                        <content_sha1>e873c1e2ffd0f191e183a1057de3eef4d62e782d</content_sha1>
                </file>
        </files_fal>
-</T3RecordDocument>
\ No newline at end of file
+</T3RecordDocument>
index 365e610..33f2aca 100644 (file)
@@ -4,7 +4,7 @@
 ,2,1,256,0,"Dummy 1-2"
 "tt_content"
 ,"uid","pid","sorting","deleted","header","bodytext"
-,1,2,256,0,"Text element with image in RTE",<img src="fileadmin/_processed_/csm_typo3_image2_5c2670fd59.jpg" title="typo3_image2.jpg" data-htmlarea-file-uid="1" height="225" width="300" alt="" style="" />&nbsp;&nbsp;And here a <LINK file:2 - "download" "Initiates file download">link to an image</link>.
+,1,2,256,0,"Text element with image in RTE",<img src="fileadmin/_processed_/csm_typo3_image2_5c2670fd59.jpg" title="typo3_image2.jpg" data-htmlarea-file-uid="1" height="225" width="300" alt="" style="" />&nbsp;&nbsp;And here a <LINK file:2 - download "Initiates file download">link to an image</link>.
 "sys_file_storage"
 ,"uid","pid","name","driver"
 ,1,0,"fileadmin/ (auto-created)","Local"
index d5d8f99..6e032be 100644 (file)
@@ -35,7 +35,7 @@ use TYPO3\CMS\Core\Utility\StringUtility;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Backend\Utility\IconUtility;
 use TYPO3\CMS\Core\Utility\MathUtility;
-use TYPO3\CMS\Core\Utility\PathUtility;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 use TYPO3\CMS\Lang\LanguageService;
 
 /**
@@ -395,20 +395,21 @@ class ElementBrowser {
         */
        protected function initCurrentUrl() {
                // CurrentUrl - the current link url must be passed around if it exists
-               if ($this->mode == 'wizard') {
+               if ($this->mode === 'wizard') {
                        $currentValues = GeneralUtility::trimExplode(LF, trim($this->P['currentValue']));
                        if (count($currentValues) > 0) {
                                $currentValue = array_pop($currentValues);
                        } else {
                                $currentValue = '';
                        }
-                       $currentLinkParts = GeneralUtility::unQuoteFilenames($currentValue, TRUE);
+                       $currentLinkParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($currentValue);
+
                        $initialCurUrlArray = array(
-                               'href' => $currentLinkParts[0],
-                               'target' => $currentLinkParts[1],
-                               'class' => $currentLinkParts[2],
-                               'title' => $currentLinkParts[3],
-                               'params' => $currentLinkParts[4]
+                               'href' => $currentLinkParts['url'],
+                               'target' => $currentLinkParts['target'],
+                               'class' => $currentLinkParts['class'],
+                               'title' => $currentLinkParts['title'],
+                               'params' => $currentLinkParts['additionalParams']
                        );
                        $this->curUrlArray = is_array(GeneralUtility::_GP('curUrl'))
                                ? array_merge($initialCurUrlArray, GeneralUtility::_GP('curUrl'))
@@ -420,7 +421,14 @@ class ElementBrowser {
                                $conf = array();
                                $_params = array(
                                        'conf' => &$conf,
-                                       'linkParts' => $currentLinkParts
+                                       'linkParts' => [
+                                               // the hook expects old numerical indexes
+                                               $currentLinkParts['url'],
+                                               $currentLinkParts['target'],
+                                               $currentLinkParts['class'],
+                                               $currentLinkParts['title'],
+                                               $currentLinkParts['additionalParams']
+                                       ]
                                );
                                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/class.browse_links.php']['extendUrlArray'] as $objRef) {
                                        $processor =& GeneralUtility::getUserObj($objRef);
@@ -433,25 +441,16 @@ class ElementBrowser {
                                // Check if there is the FAL API
                                if (GeneralUtility::isFirstPartOfStr($this->curUrlArray['href'], 'file:')) {
                                        $this->curUrlInfo = $this->parseCurUrl($this->curUrlArray['href'], $this->siteURL);
-                                       // Remove the "file:" prefix
-                                       $currentLinkParts[0] = rawurldecode(substr($this->curUrlArray['href'], 5));
                                } elseif (file_exists(PATH_site . rawurldecode($this->curUrlArray['href']))) {
-                                       if (GeneralUtility::isFirstPartOfStr($this->curUrlArray['href'], PATH_site)) {
-                                               $currentLinkParts[0] = PathUtility::stripPathSitePrefix($this->curUrlArray['href']);
-                                       }
                                        $this->curUrlInfo = $this->parseCurUrl($this->siteURL . $this->curUrlArray['href'], $this->siteURL);
                                } elseif (strstr($this->curUrlArray['href'], '@')) {
-                                       // check for email link
-                                       if (GeneralUtility::isFirstPartOfStr($this->curUrlArray['href'], 'mailto:')) {
-                                               $currentLinkParts[0] = substr($this->curUrlArray['href'], 7);
-                                       }
                                        $this->curUrlInfo = $this->parseCurUrl('mailto:' . $this->curUrlArray['href'], $this->siteURL);
                                } else {
                                        // nothing of the above. this is an external link
                                        if (strpos($this->curUrlArray['href'], '://') === FALSE) {
-                                               $currentLinkParts[0] = 'http://' . $this->curUrlArray['href'];
+                                               $currentLinkParts['url'] = 'http://' . $this->curUrlArray['href'];
                                        }
-                                       $this->curUrlInfo = $this->parseCurUrl($currentLinkParts[0], $this->siteURL);
+                                       $this->curUrlInfo = $this->parseCurUrl($currentLinkParts['url'], $this->siteURL);
                                }
                        } elseif (!$this->curUrlArray['href']) {
                                $this->curUrlInfo = array();
@@ -600,9 +599,12 @@ class ElementBrowser {
                                                        cur_class = "\\"" + cur_class + "\\"";
                                                }
                                                if (cur_title == "" && cur_params != "") {
-                                                       cur_title = "-";
-                                               }
-                                               cur_title = cur_title.replace(/(^\\")|(\\"$)/g, "");
+                                                       cur_title = "-";
+                                               }
+                                               // replace each \ with \\
+                                               cur_title = cur_title.replace(/\\\\/g, "\\\\\\\\");
+                                               // replace each " with \"
+                                               cur_title = cur_title.replace(/\\"/g, "\\\\\\"");
                                                if (cur_title.indexOf(" ") != -1) {
                                                        cur_title = "\\"" + cur_title + "\\"";
                                                }
index a3d7203..42939bf 100644 (file)
@@ -973,9 +973,9 @@ class BrowseLinks extends \TYPO3\CMS\Recordlist\Browser\ElementBrowser {
                                                        <td><label for="rtehtmlarea-browse-links-anchor_title" id="rtehtmlarea-browse-links-title-label">' . $GLOBALS['LANG']->getLL('anchor_title', TRUE) . ':</label></td>
                                                        <td colspan="3">
                                                                <span id="rtehtmlarea-browse-links-title-input" style="display: ' . ($readOnly ? 'none' : 'inline') . ';">
-                                                                       <input type="text" id="rtehtmlarea-browse-links-anchor_title" name="anchor_title" value="' . $title . '" ' . $this->doc->formWidth(30) . ' />
+                                                                       <input type="text" id="rtehtmlarea-browse-links-anchor_title" name="anchor_title" value="' . htmlspecialchars($title) . '" ' . $this->doc->formWidth(30) . ' />
                                                                </span>
-                                                               <span id="rtehtmlarea-browse-links-title-readonly" style="display: ' . ($readOnly ? 'inline' : 'none') . ';">' . $title . '</span>
+                                                               <span id="rtehtmlarea-browse-links-title-readonly" style="display: ' . ($readOnly ? 'inline' : 'none') . ';">' . htmlspecialchars($title) . '</span>
                                                        </td>
                                                </tr>';
        }