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