b5a2cc9e7ccd585691c366318ae5c7c170662c40
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Domain / Finishers / AbstractFinisher.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Form\Domain\Finishers;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It originated from the Neos.Form package (www.neos.io)
9 *
10 * It is free software; you can redistribute it and/or modify it under
11 * the terms of the GNU General Public License, either version 2
12 * of the License, or any later version.
13 *
14 * For the full copyright and license information, please read the
15 * LICENSE.txt file that was distributed with this source code.
16 *
17 * The TYPO3 project - inspiring people to share!
18 */
19
20 use TYPO3\CMS\Core\Utility\ArrayUtility;
21 use TYPO3\CMS\Extbase\Reflection\ObjectAccess;
22 use TYPO3\CMS\Form\Service\TranslationService;
23 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
24
25 /**
26 * Finisher base class.
27 *
28 * Scope: frontend
29 * **This class is meant to be sub classed by developers**
30 */
31 abstract class AbstractFinisher implements FinisherInterface
32 {
33
34 /**
35 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
36 */
37 protected $objectManager;
38
39 /**
40 * @var string
41 */
42 protected $finisherIdentifier = '';
43
44 /**
45 * @var string
46 */
47 protected $shortFinisherIdentifier = '';
48
49 /**
50 * The options which have been set from the outside. Instead of directly
51 * accessing them, you should rather use parseOption().
52 *
53 * @var array
54 */
55 protected $options = [];
56
57 /**
58 * These are the default options of the finisher.
59 * Override them in your concrete implementation.
60 * Default options should not be changed from "outside"
61 *
62 * @var array
63 */
64 protected $defaultOptions = [];
65
66 /**
67 * @var \TYPO3\CMS\Form\Domain\Finishers\FinisherContext
68 */
69 protected $finisherContext;
70
71 /**
72 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
73 * @internal
74 */
75 public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager)
76 {
77 $this->objectManager = $objectManager;
78 }
79
80 /**
81 * @param string $finisherIdentifier The identifier for this finisher
82 */
83 public function __construct(string $finisherIdentifier = '')
84 {
85 if (empty($finisherIdentifier)) {
86 $this->finisherIdentifier = (new \ReflectionClass($this))->getShortName();
87 } else {
88 $this->finisherIdentifier = $finisherIdentifier;
89 }
90
91 $this->shortFinisherIdentifier = preg_replace('/Finisher$/', '', $this->finisherIdentifier);
92 }
93
94 /**
95 * @param array $options configuration options in the format ['option1' => 'value1', 'option2' => 'value2', ...]
96 * @api
97 */
98 public function setOptions(array $options)
99 {
100 $this->options = $options;
101 }
102
103 /**
104 * Sets a single finisher option (@see setOptions())
105 *
106 * @param string $optionName name of the option to be set
107 * @param mixed $optionValue value of the option
108 * @api
109 */
110 public function setOption(string $optionName, $optionValue)
111 {
112 $this->options[$optionName] = $optionValue;
113 }
114
115 /**
116 * Executes the finisher
117 *
118 * @param FinisherContext $finisherContext The Finisher context that contains the current Form Runtime and Response
119 * @api
120 */
121 final public function execute(FinisherContext $finisherContext)
122 {
123 $this->finisherContext = $finisherContext;
124 $this->executeInternal();
125 }
126
127 /**
128 * This method is called in the concrete finisher whenever self::execute() is called.
129 *
130 * Override and fill with your own implementation!
131 *
132 * @api
133 */
134 abstract protected function executeInternal();
135
136 /**
137 * Read the option called $optionName from $this->options, and parse {...}
138 * as object accessors.
139 *
140 * Then translate the value.
141 *
142 * If $optionName was not found, the corresponding default option is returned (from $this->defaultOptions)
143 *
144 * @param string $optionName
145 * @return string|array|null
146 * @api
147 */
148 protected function parseOption(string $optionName)
149 {
150 if ($optionName === 'translation') {
151 return null;
152 }
153
154 try {
155 $optionValue = ArrayUtility::getValueByPath($this->options, $optionName, '.');
156 } catch (\RuntimeException $exception) {
157 $optionValue = null;
158 }
159 try {
160 $defaultValue = ArrayUtility::getValueByPath($this->defaultOptions, $optionName, '.');
161 } catch (\RuntimeException $exception) {
162 $defaultValue = null;
163 }
164
165 if ($optionValue === null && $defaultValue !== null) {
166 $optionValue = $defaultValue;
167 }
168
169 if ($optionValue === null) {
170 return null;
171 }
172
173 if (is_array($optionValue) || is_bool($optionValue)) {
174 return $optionValue;
175 }
176
177 if ($optionValue instanceof \Closure) {
178 return $optionValue;
179 }
180
181 $formRuntime = $this->finisherContext->getFormRuntime();
182
183 // You can encapsulate a option value with {}.
184 // This enables you to access every getable property from the
185 // TYPO3\CMS\Form\Domain\Runtime\FormRuntime.
186 //
187 // For example: {formState.formValues.<elemenIdentifier>}
188 // or {<elemenIdentifier>}
189 //
190 // Both examples are equal to "$formRuntime->getFormState()->getFormValues()[<elemenIdentifier>]"
191 // If the value is not a string nothing will be replaced.
192 // There is a special option value '{__currentTimestamp}'.
193 // This will be replaced with the current timestamp.
194 $optionValue = preg_replace_callback('/{([^}]+)}/', function ($match) use ($formRuntime) {
195 if ($match[1] === '__currentTimestamp') {
196 $value = time();
197 } else {
198 // try to resolve the path '{...}' within the FormRuntime
199 $value = ObjectAccess::getPropertyPath($formRuntime, $match[1]);
200 if ($value === null) {
201 // try to resolve the path '{...}' within the FinisherVariableProvider
202 $value = ObjectAccess::getPropertyPath(
203 $this->finisherContext->getFinisherVariableProvider(),
204 $match[1]
205 );
206 }
207 }
208 if (!is_string($value) && !is_numeric($value)) {
209 $value = '{' . $match[1] . '}';
210 }
211 return $value;
212 }, $optionValue);
213
214 $renderingOptions = is_array($this->options['translation'])
215 ? $this->options['translation']
216 : [];
217
218 $optionValue = TranslationService::getInstance()->translateFinisherOption(
219 $formRuntime,
220 $this->finisherIdentifier,
221 $optionName,
222 $optionValue,
223 $renderingOptions
224 );
225
226 if (empty($optionValue)) {
227 if ($defaultValue !== null) {
228 $optionValue = $defaultValue;
229 }
230 }
231 return $optionValue;
232 }
233
234 /**
235 * @return TypoScriptFrontendController
236 */
237 protected function getTypoScriptFrontendController()
238 {
239 return $GLOBALS['TSFE'];
240 }
241 }