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