[TASK] Use null coalescing operator where possible
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Scheduler / FieldProvider.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Scheduler;
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\Utility\TypeHandlingUtility;
18
19 /**
20 * Field provider for Extbase CommandController Scheduler task
21 */
22 class FieldProvider implements \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface
23 {
24 /**
25 * @var \TYPO3\CMS\Extbase\Mvc\Cli\CommandManager
26 */
27 protected $commandManager;
28
29 /**
30 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
31 */
32 protected $objectManager;
33
34 /**
35 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService
36 */
37 protected $reflectionService;
38
39 /**
40 * @var \TYPO3\CMS\Extbase\Scheduler\Task
41 */
42 protected $task;
43
44 /**
45 * Constructor
46 *
47 * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
48 * @param \TYPO3\CMS\Extbase\Mvc\Cli\CommandManager $commandManager
49 * @param \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService
50 */
51 public function __construct(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager = null, \TYPO3\CMS\Extbase\Mvc\Cli\CommandManager $commandManager = null, \TYPO3\CMS\Extbase\Reflection\ReflectionService $reflectionService = null)
52 {
53 $this->objectManager = $objectManager ?? \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
54 $this->commandManager = $commandManager ?? $this->objectManager->get(\TYPO3\CMS\Extbase\Mvc\Cli\CommandManager::class);
55 $this->reflectionService = $reflectionService ?? $this->objectManager->get(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class);
56 }
57
58 /**
59 * Render additional information fields within the scheduler backend.
60 *
61 * @param array &$taskInfo Array information of task to return
62 * @param mixed $task \TYPO3\CMS\Scheduler\Task\AbstractTask or \TYPO3\CMS\Scheduler\Execution instance
63 * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule Reference to the calling object (BE module of the Scheduler)
64 * @return array Additional fields
65 * @see \TYPO3\CMS\Scheduler\AdditionalFieldProvider#getAdditionalFields($taskInfo, $task, $schedulerModule)
66 */
67 public function getAdditionalFields(array &$taskInfo, $task, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule)
68 {
69 $this->task = $task;
70 if ($this->task !== null) {
71 $this->task->setScheduler();
72 }
73 $fields = [];
74 $fields['action'] = $this->getCommandControllerActionField();
75 if ($this->task !== null && $this->task->getCommandIdentifier()) {
76 $command = $this->commandManager->getCommandByIdentifier($this->task->getCommandIdentifier());
77 $fields['description'] = $this->getCommandControllerActionDescriptionField();
78 $argumentFields = $this->getCommandControllerActionArgumentFields($command->getArgumentDefinitions());
79 $fields = array_merge($fields, $argumentFields);
80 $this->task->save();
81 }
82 return $fields;
83 }
84
85 /**
86 * Validates additional selected fields
87 *
88 * @param array &$submittedData
89 * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule
90 * @return bool
91 */
92 public function validateAdditionalFields(array &$submittedData, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $schedulerModule)
93 {
94 return true;
95 }
96
97 /**
98 * Saves additional field values
99 *
100 * @param array $submittedData
101 * @param \TYPO3\CMS\Scheduler\Task\AbstractTask $task
102 * @return bool
103 */
104 public function saveAdditionalFields(array $submittedData, \TYPO3\CMS\Scheduler\Task\AbstractTask $task)
105 {
106 $task->setCommandIdentifier($submittedData['task_extbase']['action']);
107 $task->setArguments((array)$submittedData['task_extbase']['arguments']);
108 return true;
109 }
110
111 /**
112 * Get description of selected command
113 *
114 * @return string
115 */
116 protected function getCommandControllerActionDescriptionField()
117 {
118 $command = $this->commandManager->getCommandByIdentifier($this->task->getCommandIdentifier());
119 return [
120 'code' => '',
121 'label' => '<strong>' . $command->getDescription() . '</strong>'
122 ];
123 }
124
125 /**
126 * Gets a select field containing all possible CommandController actions
127 *
128 * @return array
129 */
130 protected function getCommandControllerActionField()
131 {
132 $commands = $this->commandManager->getAvailableCommands();
133 $options = [];
134 foreach ($commands as $command) {
135 if ($command->isInternal() === true || $command->isCliOnly() === true) {
136 continue;
137 }
138 $className = $command->getControllerClassName();
139 if (strpos($className, '\\')) {
140 $classNameParts = explode('\\', $className);
141 // Skip vendor and product name for core classes
142 if (strpos($className, 'TYPO3\\CMS\\') === 0) {
143 $classPartsToSkip = 2;
144 } else {
145 $classPartsToSkip = 1;
146 }
147 $classNameParts = array_slice($classNameParts, $classPartsToSkip);
148 $extensionName = $classNameParts[0];
149 $controllerName = $classNameParts[2];
150 } else {
151 $classNameParts = explode('_', $className);
152 $extensionName = $classNameParts[1];
153 $controllerName = $classNameParts[3];
154 }
155 $identifier = $command->getCommandIdentifier();
156 $options[$identifier] = $extensionName . ' ' . str_replace('CommandController', '', $controllerName) . ': ' . $command->getControllerCommandName();
157 }
158 $name = 'action';
159 $currentlySelectedCommand = $this->task !== null ? $this->task->getCommandIdentifier() : null;
160 return [
161 'code' => $this->renderSelectField($name, $options, $currentlySelectedCommand),
162 'label' => $this->getActionLabel()
163 ];
164 }
165
166 /**
167 * Gets a set of fields covering arguments which must be sent to $currentControllerAction.
168 * Also registers the default values of those fields with the Task, allowing
169 * them to be read upon execution.
170 *
171 * @param array $argumentDefinitions
172 * @return array
173 */
174 protected function getCommandControllerActionArgumentFields(array $argumentDefinitions)
175 {
176 $fields = [];
177 $argumentValues = $this->task->getArguments();
178 foreach ($argumentDefinitions as $argument) {
179 $name = $argument->getName();
180 $defaultValue = $this->getDefaultArgumentValue($argument);
181 $this->task->addDefaultValue($name, $defaultValue);
182 $value = $argumentValues[$name] ?? $defaultValue;
183 $fields[$name] = [
184 'code' => $this->renderField($argument, $value),
185 'label' => $this->getArgumentLabel($argument)
186 ];
187 }
188 return $fields;
189 }
190
191 /**
192 * Gets a label for $key based on either provided extension or currently
193 * selected CommandController extension,ยด
194 *
195 * @param string $localLanguageKey
196 * @param string $extensionName
197 * @return string
198 */
199 protected function getLanguageLabel($localLanguageKey, $extensionName = null)
200 {
201 if (!$extensionName) {
202 list($extensionName, $commandControllerName, $commandName) = explode(':', $this->task->getCommandIdentifier());
203 }
204 $label = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($localLanguageKey, $extensionName);
205 return $label;
206 }
207
208 /**
209 * Gets the data type required for the argument value
210 *
211 * @param \TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument
212 * @return string the argument type
213 */
214 protected function getArgumentType(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument)
215 {
216 $command = $this->commandManager->getCommandByIdentifier($this->task->getCommandIdentifier());
217 $controllerClassName = $command->getControllerClassName();
218 $methodName = $command->getControllerCommandName() . 'Command';
219 $tags = $this->reflectionService->getMethodTagsValues($controllerClassName, $methodName);
220 foreach ($tags['param'] as $tag) {
221 list($argumentType, $argumentVariableName) = explode(' ', $tag);
222 if (substr($argumentVariableName, 1) === $argument->getName()) {
223 return $argumentType;
224 }
225 }
226 return '';
227 }
228
229 /**
230 * Get a human-readable label for a command argument
231 *
232 * @param \TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument
233 * @return string
234 */
235 protected function getArgumentLabel(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument)
236 {
237 $argumentName = $argument->getName();
238 list($extensionName, $commandControllerName, $commandName) = explode(':', $this->task->getCommandIdentifier());
239 $path = ['command', $commandControllerName, $commandName, 'arguments', $argumentName];
240 $labelNameIndex = implode('.', $path);
241 $label = $this->getLanguageLabel($labelNameIndex);
242 if (!$label) {
243 $label = 'Argument: ' . $argumentName;
244 }
245 $descriptionIndex = $labelNameIndex . '.description';
246 $description = $this->getLanguageLabel($descriptionIndex);
247 if ((string)$description === '') {
248 $description = $argument->getDescription();
249 }
250 if ((string)$description !== '') {
251 $label .= '. <em>' . htmlspecialchars($description) . '</em>';
252 }
253 return $label;
254 }
255
256 /**
257 * Gets the default value of argument
258 *
259 * @param \TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument
260 * @return mixed
261 */
262 protected function getDefaultArgumentValue(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument)
263 {
264 $type = $this->getArgumentType($argument);
265 $argumentName = $argument->getName();
266 $command = $this->commandManager->getCommandByIdentifier($this->task->getCommandIdentifier());
267 $argumentReflection = $this->reflectionService->getMethodParameters($command->getControllerClassName(), $command->getControllerCommandName() . 'Command');
268 $defaultValue = $argumentReflection[$argumentName]['defaultValue'];
269 if (TypeHandlingUtility::normalizeType($type) === 'boolean') {
270 $defaultValue = (bool)$defaultValue ? 1 : 0;
271 }
272 return $defaultValue;
273 }
274
275 /**
276 * Get a human-readable label for the action field
277 *
278 * @return string
279 */
280 protected function getActionLabel()
281 {
282 $index = 'task.action';
283 $label = $this->getLanguageLabel($index, 'extbase');
284 if (!$label) {
285 $label = 'CommandController Command. <em>Save and reopen to define command arguments</em>';
286 }
287 return $label;
288 }
289
290 /**
291 * Render a select field with name $name and options $options
292 *
293 * @param string $name
294 * @param array $options
295 * @param string $selectedOptionValue
296 * @return string
297 */
298 protected function renderSelectField($name, array $options, $selectedOptionValue)
299 {
300 $html = [
301 '<select class="form-control" name="tx_scheduler[task_extbase][' . htmlspecialchars($name) . ']">'
302 ];
303 foreach ($options as $optionValue => $optionLabel) {
304 $selected = $optionValue === $selectedOptionValue ? ' selected="selected"' : '';
305 $html[] = '<option title="test" value="' . htmlspecialchars($optionValue) . '"' . $selected . '>' . htmlspecialchars($optionLabel) . '</option>';
306 }
307 $html[] = '</select>';
308 return implode(LF, $html);
309 }
310
311 /**
312 * Renders a field for defining an argument's value
313 *
314 * @param \TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument
315 * @param mixed $currentValue
316 * @return string
317 */
318 protected function renderField(\TYPO3\CMS\Extbase\Mvc\Cli\CommandArgumentDefinition $argument, $currentValue)
319 {
320 $type = $this->getArgumentType($argument);
321 $name = $argument->getName();
322 $fieldName = 'tx_scheduler[task_extbase][arguments][' . htmlspecialchars($name) . ']';
323 if (TypeHandlingUtility::normalizeType($type) === 'boolean') {
324 // checkbox field for boolean values.
325 $html = '<input type="hidden" name="' . $fieldName . '" value="0">';
326 $html .= '<div class="checkbox"><label><input type="checkbox" name="' . $fieldName . '" value="1" ' . ((bool)$currentValue ? ' checked="checked"' : '') . '></label></div>';
327 } else {
328 // regular string, also the default field type
329 $html = '<input class="form-control" type="text" name="' . $fieldName . '" value="' . htmlspecialchars($currentValue) . '"> ';
330 }
331 return $html;
332 }
333 }