1e8fb57cea8eb8b3ef0055d4133f94b7c06f8de0
[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\Imaging\Icon;
23 use TYPO3\CMS\Core\Messaging\FlashMessage;
24 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Core\Utility\PathUtility;
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 * @return void
70 */
71 public function menuConfig()
72 {
73 $this->MOD_MENU = ['mode' => []];
74 $this->MOD_MENU['mode']['information'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_overview');
75 $this->MOD_MENU['mode']['tasks'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang.xlf:task_tasks');
76 /* Copied from parent::menuConfig, because parent is hardcoded to menu.function,
77 * however menu.function is already used for the individual tasks.
78 * Therefore we use menu.mode here.
79 */
80 // Page/be_user TSconfig settings and blinding of menu-items
81 $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
82 $this->MOD_MENU['mode'] = $this->mergeExternalItems($this->MCONF['name'], 'mode', $this->MOD_MENU['mode']);
83 $this->MOD_MENU['mode'] = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $this->MOD_MENU['mode'], 'menu.mode');
84 parent::menuConfig();
85 }
86
87 /**
88 * Generates the menu based on $this->MOD_MENU
89 *
90 * @throws \InvalidArgumentException
91 */
92 protected function generateMenu()
93 {
94 $menu = $this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
95 $menu->setIdentifier('WebFuncJumpMenu');
96 foreach ($this->MOD_MENU['mode'] as $controller => $title) {
97 $item = $menu
98 ->makeMenuItem()
99 ->setHref(
100 BackendUtility::getModuleUrl(
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 * @param ResponseInterface $response
125 * @return ResponseInterface the response with the content
126 */
127 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
128 {
129 $GLOBALS['SOBE'] = $this;
130 $this->main();
131
132 $this->moduleTemplate->setContent($this->content);
133
134 $response->getBody()->write($this->moduleTemplate->renderContent());
135 return $response;
136 }
137
138 /**
139 * Creates the module's content. In this case it rather acts as a kind of #
140 * dispatcher redirecting requests to specific tasks.
141 *
142 * @return void
143 */
144 public function main()
145 {
146 $this->getButtons();
147 $this->generateMenu();
148 $this->moduleTemplate->addJavaScriptCode(
149 'TaskCenterInlineJavascript',
150 'if (top.fsMod) { top.fsMod.recentIds["web"] = 0; }'
151 );
152
153 // Render content depending on the mode
154 $mode = (string)$this->MOD_SETTINGS['mode'];
155 if ($mode === 'information') {
156 $this->renderInformationContent();
157 } else {
158 $this->renderModuleContent();
159 }
160 // Renders the module page
161 $this->moduleTemplate->setTitle($this->getLanguageService()->getLL('title'));
162 }
163
164 /**
165 * Prints out the module's HTML
166 *
167 * @return void
168 */
169 public function printContent()
170 {
171 echo $this->content;
172 }
173
174 /**
175 * Generates the module content by calling the selected task
176 *
177 * @return void
178 */
179 protected function renderModuleContent()
180 {
181 $chosenTask = (string)$this->MOD_SETTINGS['function'];
182 // Render the taskcenter task as default
183 if (empty($chosenTask) || $chosenTask == 'index') {
184 $chosenTask = 'taskcenter.tasks';
185 }
186 // Render the task
187 $actionContent = '';
188 $flashMessage = null;
189 list($extKey, $taskClass) = explode('.', $chosenTask, 2);
190 if (class_exists($taskClass)) {
191 $taskInstance = GeneralUtility::makeInstance($taskClass, $this);
192 if ($taskInstance instanceof TaskInterface) {
193 // Check if the task is restricted to admins only
194 if ($this->checkAccess($extKey, $taskClass)) {
195 $actionContent .= $taskInstance->getTask();
196 } else {
197 $flashMessage = GeneralUtility::makeInstance(
198 FlashMessage::class,
199 $this->getLanguageService()->getLL('error-access'),
200 $this->getLanguageService()->getLL('error_header'),
201 FlashMessage::ERROR
202 );
203 }
204 } else {
205 // Error if the task is not an instance of \TYPO3\CMS\Taskcenter\TaskInterface
206 $flashMessage = GeneralUtility::makeInstance(
207 FlashMessage::class,
208 sprintf($this->getLanguageService()->getLL('error_no-instance'), $taskClass, TaskInterface::class),
209 $this->getLanguageService()->getLL('error_header'),
210 FlashMessage::ERROR
211 );
212 }
213 } else {
214 $flashMessage = GeneralUtility::makeInstance(
215 FlashMessage::class,
216 $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tabdescr'),
217 $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_tabs_tab'),
218 FlashMessage::INFO
219 );
220 }
221
222 if ($flashMessage) {
223 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
224 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
225 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
226 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
227 $defaultFlashMessageQueue->enqueue($flashMessage);
228 }
229
230 $content = '<div class="taskcenter">
231 <div class="row">
232 <div class="col-sm-4 col-md-3">
233 <div class="taskcenter-menu">
234 ' . $this->indexAction() . '
235 </div>
236 </div>
237 <div class="col-sm-8 col-md-9">
238 <div class="taskcenter-content taskcenter-content-' . strtolower(str_replace('\\', '-', htmlspecialchars(($extKey . '-' . $taskClass)))) . '">
239 ' . $actionContent . '
240 </div>
241 </div>
242 </div>
243 </div>';
244 $this->content .= $content;
245 }
246
247 /**
248 * Generates the information content
249 *
250 * @return void
251 */
252 protected function renderInformationContent()
253 {
254 $content = $this->description($this->getLanguageService()->getLL('mlang_tabs_tab'), $this->getLanguageService()->sL('LLL:EXT:taskcenter/Resources/Private/Language/locallang_mod.xlf:mlang_labels_tabdescr'));
255 $content .= $this->getLanguageService()->getLL('taskcenter-about');
256 if ($this->getBackendUser()->isAdmin()) {
257 $content .= '<br /><br />' . $this->description($this->getLanguageService()->getLL('taskcenter-adminheader'), $this->getLanguageService()->getLL('taskcenter-admin'));
258 }
259 $this->content .= $content;
260 }
261
262 /**
263 * Render the headline of a task including a title and an optional description.
264 *
265 * @param string $title Title
266 * @param string $description Description
267 * @return string formatted title and description
268 */
269 public function description($title, $description = '')
270 {
271 $content = '<h1>' . nl2br(htmlspecialchars($title)) . '</h1>';
272 if (!empty($description)) {
273 $content .= '<p>' . nl2br(htmlspecialchars($description)) . '</p>';
274 }
275 return $content;
276 }
277
278 /**
279 * Render a list of items as a nicely formated definition list including a
280 * link, icon, title and description.
281 * The keys of a single item are:
282 * - title: Title of the item
283 * - link: Link to the task
284 * - icon: Path to the icon or Icon as HTML if it begins with <img
285 * - description: Description of the task, using htmlspecialchars()
286 * - descriptionHtml: Description allowing HTML tags which will override the
287 * description
288 *
289 * @param array $items List of items to be displayed in the definition list.
290 * @param bool $mainMenu Set it to TRUE to render the main menu
291 * @return string Fefinition list
292 */
293 public function renderListMenu($items, $mainMenu = false)
294 {
295 $content = ($section = '');
296 $count = 0;
297 // Change the sorting of items to the user's one
298 if ($mainMenu) {
299 $this->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Taskcenter/Taskcenter');
300 $userSorting = unserialize($this->getBackendUser()->uc['taskcenter']['sorting']);
301 if (is_array($userSorting)) {
302 $newSorting = [];
303 foreach ($userSorting as $item) {
304 if (isset($items[$item])) {
305 $newSorting[] = $items[$item];
306 unset($items[$item]);
307 }
308 }
309 $items = $newSorting + $items;
310 }
311 }
312 if (is_array($items) && !empty($items)) {
313 foreach ($items as $itemKey => $item) {
314 $title = htmlspecialchars($item['title']);
315 $icon = ($additionalClass = ($collapsedStyle = ''));
316 // Check for custom icon
317 if (!empty($item['icon'])) {
318 if (strpos($item['icon'], '<img ') === false) {
319 $absIconPath = GeneralUtility::getFileAbsFileName($item['icon']);
320 // If the file indeed exists, assemble relative path to it
321 if (file_exists($absIconPath)) {
322 $icon = '../' . str_replace(PATH_site, '', $absIconPath);
323 $icon = '<img src="' . $icon . '" title="' . $title . '" alt="' . $title . '" />';
324 }
325 if (@is_file($icon)) {
326 $icon = '<img src="' . PathUtility::getAbsoluteWebPath($icon) . '" width="16" height="16" title="' . $title . '" alt="' . $title . '" />';
327 }
328 } else {
329 $icon = $item['icon'];
330 }
331 }
332 $description = $item['descriptionHtml'] ?: '<p>' . nl2br(htmlspecialchars($item['description'])) . '</p>';
333 $id = $this->getUniqueKey($item['uid']);
334 $contentId = strtolower(str_replace('\\', '-', $id));
335 // Collapsed & expanded menu items
336 if (isset($this->getBackendUser()->uc['taskcenter']['states'][$id]) && $this->getBackendUser()->uc['taskcenter']['states'][$id]) {
337 $collapsed = true;
338 $collapseIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-view-list-expand', Icon::SIZE_SMALL)->render('inline');
339 } else {
340 $collapsed = false;
341 $collapseIcon = $this->moduleTemplate->getIconFactory()->getIcon('actions-view-list-collapse', Icon::SIZE_SMALL)->render('inline');
342 }
343 // Active menu item
344 $panelState = (string)$this->MOD_SETTINGS['function'] == $item['uid'] ? 'panel-active' : 'panel-default';
345 $content .= '<li id="el_' . $id . '">
346 <div id="' . $contentId . '" data-taskcenter-id="' . $id . '" class="panel ' . $panelState . '">
347 <div class="panel-heading">
348 <div class="panel-heading-right">
349 <a href="#task_content_' . $contentId . '" class="panel-header-collapse t3js-taskcenter-header-collapse" role="button" data-toggle="collapse" data-uid="' . $contentId . '" aria-expanded="' . ($collapsed ? 'false' : 'true') . '">
350 ' . $collapseIcon . '
351 </a>
352 </div>
353 <div class="panel-heading-left">
354 <a href="' . $item['link'] . '" class="panel-title">
355 ' . ($icon ? '<span class="panel-title-icon">' . $icon . '</span>' : '') . '
356 <span class="panel-title-name">'
357 . $title . ' '
358 . $this->moduleTemplate->getIconFactory()->getIcon(
359 'actions-view-table-expand',
360 Icon::SIZE_SMALL
361 )->render('inline')
362 . '</span>
363 </a>
364 </div>
365 </div>
366 <div id="task_content_' . $contentId . '" class="panel-collapse collapse t3js-taskcenter-collapse ' . ($collapsed ? '' : 'in') . '" aria-expanded="true">
367 <div class="panel-body">
368 ' . $description . '
369 </div>
370 </div>
371 </div>
372 </li>';
373 $count++;
374 }
375 $navigationId = $mainMenu ? 'id="task-list"' : '';
376 $content = '<ul ' . $navigationId . ' class="list-unstyled">' . $content . '</ul>';
377 }
378 return $content;
379 }
380
381 /**
382 * Shows an overview list of available reports.
383 *
384 * @return string List of available reports
385 */
386 protected function indexAction()
387 {
388 $content = '';
389 $tasks = [];
390 $icon = ExtensionManagementUtility::extRelPath('taskcenter') . 'Resources/Public/Icons/module-taskcenter.svg';
391 // Render the tasks only if there are any available
392 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter']) && !empty($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'])) {
393 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'] as $extKey => $extensionReports) {
394 foreach ($extensionReports as $taskClass => $task) {
395 if (!$this->checkAccess($extKey, $taskClass)) {
396 continue;
397 }
398 $link = BackendUtility::getModuleUrl('user_task') . '&SET[function]=' . $extKey . '.' . $taskClass;
399 $taskTitle = $this->getLanguageService()->sL($task['title']);
400 $taskDescriptionHtml = '';
401 // Check for custom icon
402 if (!empty($task['icon'])) {
403 $icon = GeneralUtility::getFileAbsFileName($task['icon']);
404 }
405 if (class_exists($taskClass)) {
406 $taskInstance = GeneralUtility::makeInstance($taskClass, $this);
407 if ($taskInstance instanceof TaskInterface) {
408 $taskDescriptionHtml = $taskInstance->getOverview();
409 }
410 }
411 // Generate an array of all tasks
412 $uniqueKey = $this->getUniqueKey($extKey . '.' . $taskClass);
413 $tasks[$uniqueKey] = [
414 'title' => $taskTitle,
415 'descriptionHtml' => $taskDescriptionHtml,
416 'description' => $this->getLanguageService()->sL($task['description']),
417 'icon' => $icon,
418 'link' => $link,
419 'uid' => $extKey . '.' . $taskClass
420 ];
421 }
422 }
423 $content .= $this->renderListMenu($tasks, true);
424 } else {
425 $flashMessage = GeneralUtility::makeInstance(
426 FlashMessage::class,
427 $this->getLanguageService()->getLL('no-tasks'),
428 '',
429 FlashMessage::INFO
430 );
431 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
432 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
433 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
434 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
435 $defaultFlashMessageQueue->enqueue($flashMessage);
436 }
437 return $content;
438 }
439
440 /**
441 * Create the panel of buttons for submitting the form or otherwise
442 * perform operations.
443 *
444 * @return void
445 */
446 protected function getButtons()
447 {
448 $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
449
450 // Shortcut
451 $shortcutButton = $buttonBar->makeShortcutButton()
452 ->setModuleName($this->moduleName)
453 ->setSetVariables(['function']);
454 $buttonBar->addButton($shortcutButton);
455 }
456
457 /**
458 * Check the access to a task. Considered are:
459 * - Admins are always allowed
460 * - Tasks can be restriced to admins only
461 * - Tasks can be blinded for Users with TsConfig taskcenter.<extensionkey>.<taskName> = 0
462 *
463 * @param string $extKey Extension key
464 * @param string $taskClass Name of the task
465 * @return bool Access to the task allowed or not
466 */
467 protected function checkAccess($extKey, $taskClass)
468 {
469 // Check if task is blinded with TsConfig (taskcenter.<extkey>.<taskName>
470 $tsConfig = $this->getBackendUser()->getTSConfig('taskcenter.' . $extKey . '.' . $taskClass);
471 if (isset($tsConfig['value']) && (int)$tsConfig['value'] === 0) {
472 return false;
473 }
474 // Admins are always allowed
475 if ($this->getBackendUser()->isAdmin()) {
476 return true;
477 }
478 // Check if task is restricted to admins
479 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'][$extKey][$taskClass]['admin'] === 1) {
480 return false;
481 }
482 return true;
483 }
484
485 /**
486 * Returns HTML code to dislay an url in an iframe at the right side of the taskcenter
487 *
488 * @param string $url Url to display
489 * @return string Code that inserts the iframe (HTML)
490 */
491 public function urlInIframe($url)
492 {
493 return '<div class="panel panel-default"><iframe scrolling="auto" width="100%" src="' . $url . '" name="list_frame" id="list_frame" frameborder="no" style="height: 500px"></iframe></div>';
494 }
495
496 /**
497 * Create a unique key from a string which can be used in JS for sorting
498 * Therefore '_' are replaced
499 *
500 * @param string $string string which is used to generate the identifier
501 * @return string Modified string
502 */
503 protected function getUniqueKey($string)
504 {
505 $search = ['.', '_'];
506 $replace = ['-', ''];
507 return str_replace($search, $replace, $string);
508 }
509
510 /**
511 * Returns the current BE user.
512 *
513 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
514 */
515 protected function getBackendUser()
516 {
517 return $GLOBALS['BE_USER'];
518 }
519
520 /**
521 * Returns LanguageService
522 *
523 * @return \TYPO3\CMS\Lang\LanguageService
524 */
525 protected function getLanguageService()
526 {
527 return $GLOBALS['LANG'];
528 }
529
530 /**
531 * @return ModuleTemplate
532 */
533 public function getModuleTemplate()
534 {
535 return $this->moduleTemplate;
536 }
537 }