[BUGFIX] EXT:form - parse finisher values only if string 81/51381/4
authorRalf Zimmermann <ralf.zimmermann@tritum.de>
Sun, 22 Jan 2017 16:40:24 +0000 (17:40 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Thu, 26 Jan 2017 15:21:26 +0000 (16:21 +0100)
Parse finisher option values only if the referenced formelement value
is a string.

Resolves: #79412
Releases: master
Change-Id: Ie379a396473c31907d52c8aa096168fed803510b
Reviewed-on: https://review.typo3.org/51381
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Daniel Lorenz <daniel.lorenz@extco.de>
Tested-by: Daniel Lorenz <daniel.lorenz@extco.de>
Reviewed-by: Bjoern Jacob <bjoern.jacob@tritum.de>
Tested-by: Bjoern Jacob <bjoern.jacob@tritum.de>
Reviewed-by: Jan Helke <typo3@helke.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php
typo3/sysext/form/Tests/Unit/Domain/Finishers/AbstractFinisherTest.php [new file with mode: 0644]

index 9207bd5..b60ec5e 100644 (file)
@@ -161,34 +161,23 @@ abstract class AbstractFinisher implements FinisherInterface
         }
 
         $formRuntime = $this->finisherContext->getFormRuntime();
-        $optionToCompare = $optionValue;
 
         // You can encapsulate a option value with {}.
         // This enables you to access every getable property from the
         // TYPO3\CMS\Form\Domain\Runtime.
         //
         // For example: {formState.formValues.<elemenIdentifier>}
-        // This is equal to "$formRuntime->getFormState()->getFormValues()[<elemenIdentifier>]"
+        // or {<elemenIdentifier>}
+        //
+        // Both examples are equal to "$formRuntime->getFormState()->getFormValues()[<elemenIdentifier>]"
+        // If the value is not a string nothing will be replaced.
         $optionValue = preg_replace_callback('/{([^}]+)}/', function ($match) use ($formRuntime) {
-            return ObjectAccess::getPropertyPath($formRuntime, $match[1]);
-        }, $optionValue);
-
-        if ($optionToCompare === $optionValue) {
-
-            // This is just a shortcut for a {formState.formValues.<elementIdentifier>} notation.
-            // If one of the finisher option values is equal
-            // to a identifier from the form definition then
-            // the value of the submitted form element is used
-            // insteed.
-            // Lets say you have a textfield in your form with the
-            // identifier "Text1". If you put "Text1"
-            // in the email finisher option "subject" then the submited value
-            // from the "Text1" element is used as the email subject.
-            $formValues = $this->finisherContext->getFormValues();
-            if (!is_bool($optionValue) && array_key_exists($optionValue, $formValues)) {
-                $optionValue = $formRuntime[$optionValue];
+            $value = ObjectAccess::getPropertyPath($formRuntime, $match[1]);
+            if (!is_string($value)) {
+                $value = '{' . $match[1] . '}';
             }
-        }
+            return $value;
+        }, $optionValue);
 
         if (isset($this->options['translation']['translationFile'])) {
             $optionValue = TranslationService::getInstance()->translateFinisherOption(
diff --git a/typo3/sysext/form/Tests/Unit/Domain/Finishers/AbstractFinisherTest.php b/typo3/sysext/form/Tests/Unit/Domain/Finishers/AbstractFinisherTest.php
new file mode 100644 (file)
index 0000000..2d041f1
--- /dev/null
@@ -0,0 +1,266 @@
+<?php
+namespace TYPO3\CMS\Form\Tests\Unit\Domain\Finishers;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Prophecy\Argument;
+use TYPO3\CMS\Form\Domain\Finishers\AbstractFinisher;
+use TYPO3\CMS\Form\Domain\Finishers\FinisherContext;
+use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
+
+/**
+ * Test case
+ */
+class AbstractFinisherTest extends \TYPO3\CMS\Components\TestingFramework\Core\UnitTestCase
+{
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsNullIfOptionNameIsTranslation()
+    {
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $this->assertNull($mockAbstractFinisher->_call('parseOption', 'translation'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsNullIfOptionNameNotExistsWithinOptions()
+    {
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', []);
+
+        $this->assertNull($mockAbstractFinisher->_call('parseOption', 'foo'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsNullIfOptionNameNotExistsWithinDefaultOptions()
+    {
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', []);
+
+        $this->assertNull($mockAbstractFinisher->_call('parseOption', 'foo'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsArrayOptionValuesAsArray()
+    {
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', [
+            'foo' => ['bar', 'foobar']
+        ]);
+
+        $expected = ['bar', 'foobar'];
+
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'foo'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsValueFromFormRuntimeIfOptionNameReferenceAFormElementIdentifierWhoseValueIsAString()
+    {
+        $expected = 'element-value';
+        $elementIdentifier = 'element-identifier-1';
+
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', [
+            'subject' => '{' . $elementIdentifier . '}'
+        ]);
+
+        $finisherContextProphecy = $this->prophesize(FinisherContext::class);
+
+        $formRuntimeProphecy = $this->prophesize(FormRuntime::class);
+        $formRuntimeProphecy->offsetExists(Argument::exact($elementIdentifier))->willReturn(true);
+        $formRuntimeProphecy->offsetGet(Argument::exact($elementIdentifier))->willReturn($expected);
+
+        $finisherContextProphecy->getFormRuntime(Argument::cetera())
+            ->willReturn($formRuntimeProphecy->reveal());
+
+        $mockAbstractFinisher->_set('finisherContext', $finisherContextProphecy->reveal());
+
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'subject'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsNoReplacedValueFromFormRuntimeIfOptionNameReferenceAFormElementIdentifierWhoseValueIsNotAString()
+    {
+        $elementIdentifier = 'element-identifier-1';
+        $expected = '{' . $elementIdentifier . '}';
+
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', [
+            'subject' => '{' . $elementIdentifier . '}'
+        ]);
+
+        $finisherContextProphecy = $this->prophesize(FinisherContext::class);
+
+        $formRuntimeProphecy = $this->prophesize(FormRuntime::class);
+        $formRuntimeProphecy->offsetExists(Argument::exact($elementIdentifier))->willReturn(true);
+        $formElementValue = new \DateTime;
+        $formRuntimeProphecy->offsetGet(Argument::exact($elementIdentifier))->willReturn($formElementValue);
+
+        $finisherContextProphecy->getFormRuntime(Argument::cetera())
+            ->willReturn($formRuntimeProphecy->reveal());
+
+        $mockAbstractFinisher->_set('finisherContext', $finisherContextProphecy->reveal());
+
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'subject'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsNoReplacedValueFromFormRuntimeIfOptionNameReferenceANonExistingFormElement()
+    {
+        $elementIdentifier = 'element-identifier-1';
+
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', [
+            'subject' => '{' . $elementIdentifier . '}'
+        ]);
+
+        $finisherContextProphecy = $this->prophesize(FinisherContext::class);
+
+        $formRuntimeProphecy = $this->prophesize(FormRuntime::class);
+        $formRuntimeProphecy->offsetExists(Argument::cetera())->willReturn(true);
+        $formRuntimeProphecy->offsetGet(Argument::cetera())->willReturn(false);
+
+        $finisherContextProphecy->getFormRuntime(Argument::cetera())
+            ->willReturn($formRuntimeProphecy->reveal());
+
+        $mockAbstractFinisher->_set('finisherContext', $finisherContextProphecy->reveal());
+
+        $expected = '{' . $elementIdentifier . '}';
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'subject'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsDefaultOptionValueIfOptionNameNotExistsWithinOptionsButWithinDefaultOptions()
+    {
+        $expected = 'defaultValue';
+
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', []);
+        $mockAbstractFinisher->_set('defaultOptions', [
+            'subject' => $expected
+        ]);
+
+        $finisherContextProphecy = $this->prophesize(FinisherContext::class);
+
+        $formRuntimeProphecy = $this->prophesize(FormRuntime::class);
+        $formRuntimeProphecy->offsetExists(Argument::cetera())->willReturn(true);
+        $formRuntimeProphecy->offsetGet(Argument::cetera())->willReturn(false);
+
+        $finisherContextProphecy->getFormRuntime(Argument::cetera())
+            ->willReturn($formRuntimeProphecy->reveal());
+
+        $mockAbstractFinisher->_set('finisherContext', $finisherContextProphecy->reveal());
+
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'subject'));
+    }
+
+    /**
+     * @test
+     */
+    public function parseOptionReturnsDefaultOptionValueIfOptionValueIsAFormElementReferenceAndTheFormElementValueIsEmpty()
+    {
+        $elementIdentifier = 'element-identifier-1';
+        $expected = 'defaultValue';
+
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', [
+            'subject' => '{' . $elementIdentifier . '}'
+        ]);
+        $mockAbstractFinisher->_set('defaultOptions', [
+            'subject' => $expected
+        ]);
+
+        $finisherContextProphecy = $this->prophesize(FinisherContext::class);
+
+        $formRuntimeProphecy = $this->prophesize(FormRuntime::class);
+        $formRuntimeProphecy->offsetExists(Argument::exact($elementIdentifier))->willReturn(true);
+        $formRuntimeProphecy->offsetGet(Argument::exact($elementIdentifier))->willReturn('');
+
+        $finisherContextProphecy->getFormRuntime(Argument::cetera())
+            ->willReturn($formRuntimeProphecy->reveal());
+
+        $mockAbstractFinisher->_set('finisherContext', $finisherContextProphecy->reveal());
+
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'subject'));
+    }
+}