[TASK] Create own response instance in controller actions
[Packages/TYPO3.CMS.git] / typo3 / sysext / taskcenter / Classes / Controller / TaskModuleController.php
1 <?php
2 namespace TYPO3\CMS\Taskcenter\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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Module\BaseScriptClass;
20 use TYPO3\CMS\Backend\Template\ModuleTemplate;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Http\HtmlResponse;
23 use TYPO3\CMS\Core\Messaging\FlashMessage;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Core\Utility\PathUtility;
26 use TYPO3\CMS\Fluid\View\StandaloneView;
27 use TYPO3\CMS\Taskcenter\TaskInterface;
28
29 /**
30 * This class provides a taskcenter for BE users
31 */
32 class TaskModuleController extends BaseScriptClass
33 {
34 /**
35 * @var array
36 */
37 protected $pageinfo;
38
39 /**
40 * ModuleTemplate Container
41 *
42 * @var ModuleTemplate
43 */
44 protected $moduleTemplate;
45
46 /**
47 * The name of the module
48 *
49 * @var string
50 */
51 protected $moduleName = 'user_task';
52
53 /**
54 * Initializes the Module
55 */
56 public function __construct()
57 {
58 $this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
59 $this->getLanguageService()->includeLLFile('EXT:taskcenter/Resources/Private/Language/locallang_task.xlf');
60 $this->MCONF = [
61 'name' => $this->moduleName
62 ];
63 parent::init();
64 }
65
66 /**
67 * Adds items to the ->MOD_MENU array. Used for the function menu selector.
68 */
69 public function menuConfig()
70 {
71 $this->MOD_MENU = ['mode' => []];
72 $this->MOD_MENU['mode']['information'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_overview');
73 $this->MOD_MENU['mode']['tasks'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_tasks');
74 /* Copied from parent::menuConfig, because parent is hardcoded to menu.function,
75 * however menu.function is already used for the individual tasks.
76 * Therefore we use menu.mode here.
77 */
78 // Page/be_user TSconfig settings and blinding of menu-items
79 $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
80 $this->MOD_MENU['mode'] = $this->mergeExternalItems($this->MCONF['name'], 'mode', $this->MOD_MENU['mode']);
81 $this->MOD_MENU['mode'] = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $this->MOD_MENU['mode'], 'menu.mode');
82 parent::menuConfig();
83 }
84
85 /**
86 * Generates the menu based on $this->MOD_MENU
87 *
88 * @throws \InvalidArgumentException
89 */
90 protected function generateMenu()
91 {
92 $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
93 $menu->setIdentifier('WebFuncJumpMenu');
94 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
95 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
96 foreach ($this->MOD_MENU['mode'] as $controller => $title) {
97 $item = $menu
98 ->makeMenuItem()
99 ->setHref(
100 (string)$uriBuilder->buildUriFromRoute(
101 $this->moduleName,
102 [
103 'id' => $this->id,
104 'SET' => [
105 'mode' => $controller
106 ]
107 ]
108 )
109 )
110 ->setTitle($title);
111 if ($controller === $this->MOD_SETTINGS['mode']) {
112 $item->setActive(true);
113 }
114 $menu->addMenuItem($item);
115 }
116 $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
117 }
118
119 /**
120 * Injects the request object for the current request or subrequest
121 * Simply calls main() and writes the content to the response
122 *
123 * @param ServerRequestInterface $request the current request
124 * @return ResponseInterface the response with the content
125 */
126 public function mainAction(ServerRequestInterface $request): ResponseInterface
127 {
128 $GLOBALS['SOBE'] = $this;
129 $this->main();
130
131 $this->moduleTemplate->setContent($this->content);
132 return new HtmlResponse($this->moduleTemplate->renderContent());
133 }
134
135 /**
136 * Creates the module's content. In this case it rather acts as a kind of #
137 * dispatcher redirecting requests to specific tasks.
138 */
139 public function main()
140 {
141 $this->getButtons();
142 $this->generateMenu();
143 $this->moduleTemplate->addJavaScriptCode(
144 'TaskCenterInlineJavascript',
145 'if (top.fsMod) { top.fsMod.recentIds["web"] = 0; }'
146 );
147
148 // Render content depending on the mode
149 $mode = (string)$this->MOD_SETTINGS['mode'];
150 if ($mode === 'information') {
151 $this->renderInformationContent();
152 } else {
153 $this->renderModuleContent();
154 }
155 // Renders the module page
156 $this->moduleTemplate->setTitle($this->getLanguageService()->getLL('title'));
157 }
158
159 /**
160 * Generates the module content by calling the selected task
161 */
162 protected function renderModuleContent()
163 {
164 $chosenTask = (string)$this->MOD_SETTINGS['function'];
165 // Render the taskcenter task as default
166 if (empty($chosenTask) || $chosenTask === 'index') {
167 $chosenTask = 'taskcenter.tasks';
168 }
169 // Render the task
170 $actionContent = '';
171 $flashMessage = null;
172 list($extKey, $taskClass) = explode('.', $chosenTask, 2);
173 if (class_exists($taskClass)) {
174 $taskInstance = GeneralUtility::makeInstance($taskClass, $this);
175 if ($taskInstance instanceof TaskInterface) {
176 // Check if the task is restricted to admins only
177 if ($this->checkAccess($extKey, $taskClass)) {
178 $actionContent .= $taskInstance->getTask();
179 } else {
180 $flashMessage = GeneralUtility::makeInstance(
181 FlashMessage::class,
182 $this->getLanguageService()->getLL('error-access'),
183 $this->getLanguageService()->getLL('error_header'),
184 FlashMessage::ERROR
185 );
186 }
187 } else {
188 // Error if the task is not an instance of \TYPO3\CMS\Taskcenter\TaskInterface
189 $flashMessage = GeneralUtility::makeInstance(
190 FlashMessage::class,
191 sprintf($this->getLanguageService()->getLL('error_no-instance'), $taskClass, TaskInterface::class),
192 $this->getLanguageService()->getLL('error_header'),
193 FlashMessage::ERROR
194 );
195 }
196 } else {
197 $flashMessage = GeneralUtility::makeInstance(
198 FlashMessage::class,
199 $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tabdescr'),
200 $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
201 FlashMessage::INFO
202 );
203 }
204
205 if ($flashMessage) {
206 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
207 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
208 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
209 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
210 $defaultFlashMessageQueue->enqueue($flashMessage);
211 }
212
213 $assigns = [];
214 $assigns['reports'] = $this->indexAction();
215 $assigns['taskClass'] = strtolower(str_replace('\\', '-', htmlspecialchars(($extKey . '-' . $taskClass))));
216 $assigns['actionContent'] = $actionContent;
217
218 // Rendering of the output via fluid
219 $view = GeneralUtility::makeInstance(StandaloneView::class);
220 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
221 'EXT:taskcenter/Resources/Private/Templates/ModuleContent.html'
222 ));
223 $view->assignMultiple($assigns);
224 $this->content .= $view->render();
225 }
226
227 /**
228 * Generates the information content
229 */
230 protected function renderInformationContent()
231 {
232 $assigns = [];
233 $assigns['LLPrefix'] = 'LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:';
234 $assigns['LLPrefixMod'] = 'LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:';
235 $assigns['LLPrefixTask'] = 'LLL:EXT:taskcenter/Resources/Private/Language/locallang_task.xlf:';
236 $assigns['admin'] = $this->getBackendUser()->isAdmin();
237
238 // Rendering of the output via fluid
239 $view = GeneralUtility::makeInstance(StandaloneView::class);
240 $view->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:taskcenter/Resources/Private/Templates')]);
241 $view->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:taskcenter/Resources/Private/Partials')]);
242 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
243 'EXT:taskcenter/Resources/Private/Templates/InformationContent.html'
244 ));
245 $view->assignMultiple($assigns);
246 $this->content .= $view->render();
247 }
248
249 /**
250 * Render the headline of a task including a title and an optional description.
251 *
252 * @param string $title Title
253 * @param string $description Description
254 * @return string formatted title and description
255 */
256 public function description($title, $description = '')
257 {
258 $descriptionView = GeneralUtility::makeInstance(StandaloneView::class);
259 $descriptionView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
260 'EXT:taskcenter/Resources/Private/Partials/Description.html'
261 ));
262 $descriptionView->assign('title', $title);
263 $descriptionView->assign('description', $description);
264 return $descriptionView->render();
265 }
266
267 /**
268 * Render a list of items as a nicely formatted definition list including a
269 * link, icon, title and description.
270 * The keys of a single item are:
271 * - title: Title of the item
272 * - link: Link to the task
273 * - icon: Path to the icon or Icon as HTML if it begins with <img
274 * - description: Description of the task, using htmlspecialchars()
275 * - descriptionHtml: Description allowing HTML tags which will override the
276 * description
277 *
278 * @param array $items List of items to be displayed in the definition list.
279 * @param bool $mainMenu Set it to TRUE to render the main menu
280 * @return string Formatted definition list
281 */
282 public function renderListMenu($items, $mainMenu = false)
283 {
284 $assigns = [];
285 $assigns['mainMenu'] = $mainMenu;
286
287 // Change the sorting of items to the user's one
288 if ($mainMenu) {
289 $userSorting = unserialize($this->getBackendUser()->uc['taskcenter']['sorting']);
290 if (is_array($userSorting)) {
291 $newSorting = [];
292 foreach ($userSorting as $item) {
293 if (isset($items[$item])) {
294 $newSorting[] = $items[$item];
295 unset($items[$item]);
296 }
297 }
298 $items = $newSorting + $items;
299 }
300 }
301 if (is_array($items) && !empty($items)) {
302 foreach ($items as $itemKey => &$item) {
303 // Check for custom icon
304 if (!empty($item['icon'])) {
305 if (strpos($item['icon'], '<img ') === false) {
306 $iconFile = GeneralUtility::getFileAbsFileName($item['icon']);
307 if (@is_file($iconFile)) {
308 $item['iconFile'] = PathUtility::getAbsoluteWebPath($iconFile);
309 }
310 }
311 }
312 $id = $this->getUniqueKey($item['uid']);
313 $contentId = strtolower(str_replace('\\', '-', $id));
314 $item['uniqueKey'] = $id;
315 $item['contentId'] = $contentId;
316 // Collapsed & expanded menu items
317 if (isset($this->getBackendUser()->uc['taskcenter']['states'][$id]) && $this->getBackendUser()->uc['taskcenter']['states'][$id]) {
318 $item['ariaExpanded'] = 'true';
319 $item['collapseIcon'] = 'actions-view-list-expand';
320 $item['collapsed'] = '';
321 } else {
322 $item['ariaExpanded'] = 'false';
323 $item['collapseIcon'] = 'actions-view-list-collapse';
324 $item['collapsed'] = 'in';
325 }
326 // Active menu item
327 $panelState = (string)$this->MOD_SETTINGS['function'] == $item['uid'] ? 'panel-active' : 'panel-default';
328 $item['panelState'] = $panelState;
329 }
330 }
331 $assigns['items'] = $items;
332
333 // Rendering of the output via fluid
334 $view = GeneralUtility::makeInstance(StandaloneView::class);
335 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
336 'EXT:taskcenter/Resources/Private/Templates/ListMenu.html'
337 ));
338 $view->assignMultiple($assigns);
339 return $view->render();
340 }
341
342 /**
343 * Shows an overview list of available reports.
344 *
345 * @return string List of available reports
346 */
347 protected function indexAction()
348 {
349 $content = '';
350 $tasks = [];
351 $defaultIcon = 'EXT:taskcenter/Resources/Public/Icons/module-taskcenter.svg';
352 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
353 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
354 // Render the tasks only if there are any available
355 if (count($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'] ?? [])) {
356 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'] as $extKey => $extensionReports) {
357 foreach ($extensionReports as $taskClass => $task) {
358 if (!$this->checkAccess($extKey, $taskClass)) {
359 continue;
360 }
361 $link = (string)$uriBuilder->buildUriFromRoute('user_task') . '&SET[function]=' . $extKey . '.' . $taskClass;
362 $taskTitle = $this->getLanguageService()->sL($task['title']);
363 $taskDescriptionHtml = '';
364
365 if (class_exists($taskClass)) {
366 $taskInstance = GeneralUtility::makeInstance($taskClass, $this);
367 if ($taskInstance instanceof TaskInterface) {
368 $taskDescriptionHtml = $taskInstance->getOverview();
369 }
370 }
371 // Generate an array of all tasks
372 $uniqueKey = $this->getUniqueKey($extKey . '.' . $taskClass);
373 $tasks[$uniqueKey] = [
374 'title' => $taskTitle,
375 'descriptionHtml' => $taskDescriptionHtml,
376 'description' => $this->getLanguageService()->sL($task['description']),
377 'icon' => !empty($task['icon']) ? $task['icon'] : $defaultIcon,
378 'link' => $link,
379 'uid' => $extKey . '.' . $taskClass
380 ];
381 }
382 }
383 $content .= $this->renderListMenu($tasks, true);
384 } else {
385 $flashMessage = GeneralUtility::makeInstance(
386 FlashMessage::class,
387 $this->getLanguageService()->getLL('no-tasks'),
388 '',
389 FlashMessage::INFO
390 );
391 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
392 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
393 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
394 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
395 $defaultFlashMessageQueue->enqueue($flashMessage);
396 }
397 return $content;
398 }
399
400 /**
401 * Create the panel of buttons for submitting the form or otherwise
402 * perform operations.
403 */
404 protected function getButtons()
405 {
406 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
407
408 // Shortcut
409 $shortcutButton = $buttonBar->makeShortcutButton()
410 ->setModuleName($this->moduleName)
411 ->setSetVariables(['function']);
412 $buttonBar->addButton($shortcutButton);
413 }
414
415 /**
416 * Check the access to a task. Considered are:
417 * - Admins are always allowed
418 * - Tasks can be restriced to admins only
419 * - Tasks can be blinded for Users with TsConfig taskcenter.<extensionkey>.<taskName> = 0
420 *
421 * @param string $extKey Extension key
422 * @param string $taskClass Name of the task
423 * @return bool Access to the task allowed or not
424 */
425 protected function checkAccess($extKey, $taskClass)
426 {
427 // Check if task is blinded with TsConfig (taskcenter.<extkey>.<taskName>
428 $tsConfig = $this->getBackendUser()->getTSConfig('taskcenter.' . $extKey . '.' . $taskClass);
429 if (isset($tsConfig['value']) && (int)$tsConfig['value'] === 0) {
430 return false;
431 }
432 // Admins are always allowed
433 if ($this->getBackendUser()->isAdmin()) {
434 return true;
435 }
436 // Check if task is restricted to admins
437 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'][$extKey][$taskClass]['admin'] === 1) {
438 return false;
439 }
440 return true;
441 }
442
443 /**
444 * Returns HTML code to dislay an url in an iframe at the right side of the taskcenter
445 *
446 * @param string $url Url to display
447 * @return string Code that inserts the iframe (HTML)
448 */
449 public function urlInIframe($url)
450 {
451 $urlView = GeneralUtility::makeInstance(StandaloneView::class);
452 $urlView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
453 'EXT:taskcenter/Resources/Private/Partials/UrlInIframe.html'
454 ));
455 $urlView->assign('url', $url);
456 return $urlView->render();
457 }
458
459 /**
460 * Create a unique key from a string which can be used in JS for sorting
461 * Therefore '_' are replaced
462 *
463 * @param string $string string which is used to generate the identifier
464 * @return string Modified string
465 */
466 protected function getUniqueKey($string)
467 {
468 $search = ['.', '_'];
469 $replace = ['-', ''];
470 return str_replace($search, $replace, $string);
471 }
472
473 /**
474 * Returns the current BE user.
475 *
476 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
477 */
478 protected function getBackendUser()
479 {
480 return $GLOBALS['BE_USER'];
481 }
482
483 /**
484 * Returns LanguageService
485 *
486 * @return \TYPO3\CMS\Core\Localization\LanguageService
487 */
488 protected function getLanguageService()
489 {
490 return $GLOBALS['LANG'];
491 }
492
493 /**
494 * @return ModuleTemplate
495 */
496 public function getModuleTemplate()
497 {
498 return $this->moduleTemplate;
499 }
500 }