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