43887db4784fa748e76442b78641976bf26080de
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Controller / FrontendController.php
1 <?php
2 namespace TYPO3\CMS\Form\Controller;
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\Extbase\Mvc\Controller\ActionController;
18 use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
19 use TYPO3\CMS\Form\Domain\Builder\FormBuilder;
20 use TYPO3\CMS\Form\Domain\Builder\ValidationBuilder;
21 use TYPO3\CMS\Form\Domain\Model\Configuration;
22 use TYPO3\CMS\Form\Domain\Model\ValidationElement;
23 use TYPO3\CMS\Form\Mvc\Controller\ControllerContext;
24 use TYPO3\CMS\Form\Utility\FormUtility;
25
26 /**
27 * The form wizard controller
28 */
29 class FrontendController extends ActionController
30 {
31 /**
32 * @var FormBuilder
33 */
34 protected $formBuilder;
35
36 /**
37 * @var ValidationBuilder
38 */
39 protected $validationBuilder;
40
41 /**
42 * @var \TYPO3\CMS\Form\Utility\SessionUtility
43 */
44 protected $sessionUtility;
45
46 /**
47 * @var FormUtility
48 */
49 protected $formUtility;
50
51 /**
52 * The TypoScript array
53 *
54 * @var array
55 */
56 protected $typoscript = array();
57
58 /**
59 * TRUE if the validation of the form should be skipped
60 *
61 * @var bool
62 */
63 protected $skipValidation = false;
64
65 /**
66 * @var ControllerContext
67 */
68 protected $controllerContext;
69
70 /**
71 * @var Configuration
72 */
73 protected $configuration;
74
75 /**
76 * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility
77 * @return void
78 */
79 public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility)
80 {
81 $this->sessionUtility = $sessionUtility;
82 }
83
84 /**
85 * initialize action
86 *
87 * @return void
88 */
89 protected function initializeAction()
90 {
91 $this->configuration = Configuration::create()->setTypoScript($this->settings['typoscript']);
92 $this->formUtility = FormUtility::create($this->configuration);
93 $this->validationBuilder = ValidationBuilder::create($this->configuration);
94 $this->validationBuilder->setFormUtility($this->formUtility);
95 $this->formBuilder = FormBuilder::create($this->configuration);
96 $this->formBuilder->setValidationBuilder($this->validationBuilder);
97 $this->formBuilder->setFormUtility($this->formUtility);
98 $this->typoscript = $this->settings['typoscript'];
99
100 // uploaded file storage
101 $this->sessionUtility->initSession($this->configuration->getPrefix());
102 // move the incoming "formPrefix" data to the $model argument
103 // now we can validate the $model argument
104 if ($this->request->hasArgument($this->configuration->getPrefix())) {
105 $this->skipValidation = false;
106 $argument = $this->request->getArgument($this->configuration->getPrefix());
107 $this->request->setArgument('model', $argument);
108 } else {
109 // If there are more forms at a page we have to skip
110 // the validation of not submitted forms
111 $this->skipValidation = true;
112 $this->request->setArgument('model', array());
113 }
114 }
115
116 /**
117 * initialize show action
118 *
119 * @return void
120 */
121 protected function initializeShowAction()
122 {
123 // set validation errors
124 $validationResults = $this->request->getOriginalRequestMappingResults()->forProperty('model');
125 if (!$validationResults->hasErrors()) {
126 // If there are errors, the rules already build
127 // but if there are errors, we need to build the rules here,
128 // because of the mandatory message rendering
129 $this->validationBuilder->buildRules();
130 return;
131 }
132 $this->formBuilder->setValidationErrors($validationResults);
133 }
134
135 /**
136 * initialize the confirmation action
137 *
138 * @return void
139 */
140 protected function initializeConfirmationAction()
141 {
142 $this->prepareValidations();
143 }
144
145 /**
146 * initialize the process action
147 *
148 * @return void
149 */
150 protected function initializeProcessAction()
151 {
152 $this->prepareValidations();
153 }
154
155 /**
156 * Builds the controller context by extending
157 * the Extbase context with custom additions.
158 *
159 * @return ControllerContext
160 */
161 protected function buildControllerContext()
162 {
163 $controllerContext = ControllerContext::extend(parent::buildControllerContext())
164 ->setConfiguration($this->configuration);
165 $this->formBuilder->setControllerContext($controllerContext);
166 return $controllerContext;
167 }
168
169 /**
170 * Handles show action, presenting the actual form.
171 *
172 * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $incomingData
173 * @ignorevalidation $incomingData
174 * @return void
175 */
176 public function showAction(ValidationElement $incomingData = null)
177 {
178 if ($incomingData !== null) {
179 $this->controllerContext->setValidationElement($incomingData);
180 }
181 $form = $this->formBuilder->buildModel();
182 $this->view->assign('model', $form);
183 }
184
185 /**
186 * Handles confirmation action, presenting the user submitted
187 * data again for final confirmation.
188 *
189 * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $model
190 * @return void
191 */
192 public function confirmationAction(ValidationElement $model)
193 {
194 $this->skipForeignFormProcessing();
195
196 if (count($model->getIncomingFields()) === 0) {
197 $this->sessionUtility->destroySession();
198 $this->forward('show');
199 }
200 $this->controllerContext->setValidationElement($model);
201 $form = $this->formBuilder->buildModel();
202 // store uploaded files
203 $this->sessionUtility->storeSession();
204 $this->view->assign('model', $form);
205
206 $message = $this->formUtility->renderItem(
207 $this->typoscript['confirmation.']['message.'],
208 $this->typoscript['confirmation.']['message'],
209 LocalizationUtility::translate('tx_form_view_confirmation.message', 'form')
210 );
211 $this->view->assign('message', $message);
212 }
213
214 /**
215 * action dispatchConfirmationButtonClick
216 *
217 * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $model
218 * @return void
219 */
220 public function dispatchConfirmationButtonClickAction(ValidationElement $model)
221 {
222 $this->skipForeignFormProcessing();
223
224 if ($this->request->hasArgument('confirmation-true')) {
225 $this->forward('process', null, null, array($this->configuration->getPrefix() => $this->request->getArgument('model')));
226 } else {
227 $this->sessionUtility->destroySession();
228 $this->forward('show', null, null, array('incomingData' => $this->request->getArgument('model')));
229 }
230 }
231
232 /**
233 * Handles process action, actually processing the user
234 * submitted data and forwarding it to post-processors
235 * (e.g. sending out mail messages).
236 *
237 * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $model
238 * @return void
239 */
240 public function processAction(ValidationElement $model)
241 {
242 $this->skipForeignFormProcessing();
243
244 $this->controllerContext->setValidationElement($model);
245 $form = $this->formBuilder->buildModel();
246 $postProcessorTypoScript = array();
247 if (isset($this->typoscript['postProcessor.'])) {
248 $postProcessorTypoScript = $this->typoscript['postProcessor.'];
249 }
250
251 /** @var $postProcessor \TYPO3\CMS\Form\PostProcess\PostProcessor */
252 $postProcessor = $this->objectManager->get(
253 \TYPO3\CMS\Form\PostProcess\PostProcessor::class,
254 $form, $postProcessorTypoScript
255 );
256 $postProcessor->setControllerContext($this->controllerContext);
257
258 // @todo What is happening here?
259 $content = $postProcessor->process();
260 $this->sessionUtility->destroySession();
261 $this->forward('afterProcess', null, null, array('postProcessorContent' => $content));
262 }
263
264 /**
265 * action after process
266 *
267 * @param string $postProcessorContent
268 * @return void
269 */
270 public function afterProcessAction($postProcessorContent)
271 {
272 $this->view->assign('postProcessorContent', $postProcessorContent);
273 }
274
275 /**
276 * Skip the processing of foreign forms.
277 * If there is more than one form on a page
278 * we have to be sure that only the submitted form will be
279 * processed. On data submission, the extbase action "confirmation" or
280 * "process" is called. The detection which form is submitted
281 * is done by the form prefix. All forms which do not have any
282 * submitted data are skipped and forwarded to the show action.
283 *
284 * @return void
285 */
286 protected function skipForeignFormProcessing()
287 {
288 if (
289 !$this->request->hasArgument($this->configuration->getPrefix())
290 && !$this->sessionUtility->getSessionData()
291 ) {
292 $this->forward('show');
293 }
294 }
295
296 /**
297 * If the current form should be validated
298 * then set the dynamic validation
299 *
300 * @return void
301 */
302 protected function prepareValidations()
303 {
304 if ($this->skipValidation || !$this->arguments->hasArgument('model')) {
305 return;
306 }
307
308 $this->validationBuilder->buildRules($this->request->getArgument('model'));
309 $this->setDynamicValidation($this->validationBuilder->getRules());
310 $this->skipValidation = false;
311 }
312
313 /**
314 * Sets the dynamic validation rules.
315 *
316 * @param array $toValidate
317 * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException
318 * @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException
319 */
320 protected function setDynamicValidation(array $toValidate = array())
321 {
322 // build custom validation chain
323 /** @var \TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver */
324 $validatorResolver = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\ValidatorResolver::class);
325
326 /** @var \TYPO3\CMS\Form\Domain\Validator\ValidationElementValidator $modelValidator */
327 $modelValidator = $validatorResolver->createValidator(\TYPO3\CMS\Form\Domain\Validator\ValidationElementValidator::class);
328 foreach ($toValidate as $propertyName => $validations) {
329 foreach ($validations as $validation) {
330 if (empty($validation['validator'])) {
331 throw new \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException('Invalid validate configuration for ' . $propertyName . ': Could not resolve class name for validator "' . $validation['validatorName'] . '".', 1441893777);
332 }
333 $modelValidator->addPropertyValidator($propertyName, $validation['validator']);
334 }
335 }
336
337 if ($modelValidator->countPropertyValidators()) {
338 /** @var \TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator $baseConjunctionValidator */
339 $baseConjunctionValidator = $this->arguments->getArgument('model')->getValidator();
340 if ($baseConjunctionValidator === null) {
341 $baseConjunctionValidator = $validatorResolver->createValidator(\TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator::class);
342 $this->arguments->getArgument('model')->setValidator($baseConjunctionValidator);
343 }
344 $baseConjunctionValidator->addValidator($modelValidator);
345 }
346 }
347 }