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