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