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