[TASK] Cleanup 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 TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Backend\Utility\IconUtility;
19 use TYPO3\CMS\Core\Messaging\FlashMessage;
20 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22 use TYPO3\CMS\Taskcenter\TaskInterface;
23
24 /**
25 * This class provides a taskcenter for BE users
26 *
27 * @author Georg Ringer <typo3@ringerge.org>
28 */
29 class TaskModuleController extends \TYPO3\CMS\Backend\Module\BaseScriptClass {
30
31 /**
32 * @var array
33 */
34 protected $pageinfo;
35
36 /**
37 * The name of the module
38 *
39 * @var string
40 */
41 protected $moduleName = 'user_task';
42
43 /**
44 * Initializes the Module
45 */
46 public function __construct() {
47 $this->getLanguageService()->includeLLFile('EXT:taskcenter/task/locallang.xlf');
48 $this->MCONF = array(
49 'name' => $this->moduleName
50 );
51 parent::init();
52 // Initialize document
53 $this->doc = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Template\DocumentTemplate::class);
54 $this->doc->setModuleTemplate(ExtensionManagementUtility::extPath('taskcenter') . 'Resources/Private/Templates/mod_template.html');
55 $this->doc->backPath = $GLOBALS['BACK_PATH'];
56 $this->doc->getPageRenderer()->loadJquery();
57 $this->doc->addStyleSheet('tx_taskcenter', '../' . ExtensionManagementUtility::siteRelPath('taskcenter') . 'Resources/Public/Styles/styles.css');
58 }
59
60 /**
61 * Adds items to the ->MOD_MENU array. Used for the function menu selector.
62 *
63 * @return void
64 */
65 public function menuConfig() {
66 $this->MOD_MENU = array('mode' => array());
67 $this->MOD_MENU['mode']['information'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/locallang.xlf:task_overview');
68 $this->MOD_MENU['mode']['tasks'] = $this->getLanguageService()->sL('LLL:EXT:taskcenter/locallang.xlf:task_tasks');
69 /* Copied from parent::menuConfig, because parent is hardcoded to menu.function,
70 * however menu.function is already used for the individual tasks.
71 * Therefore we use menu.mode here.
72 */
73 // Page/be_user TSconfig settings and blinding of menu-items
74 $this->modTSconfig = BackendUtility::getModTSconfig($this->id, 'mod.' . $this->moduleName);
75 $this->MOD_MENU['mode'] = $this->mergeExternalItems($this->MCONF['name'], 'mode', $this->MOD_MENU['mode']);
76 $this->MOD_MENU['mode'] = BackendUtility::unsetMenuItems($this->modTSconfig['properties'], $this->MOD_MENU['mode'], 'menu.mode');
77 parent::menuConfig();
78 }
79
80 /**
81 * Creates the module's content. In this case it rather acts as a kind of #
82 * dispatcher redirecting requests to specific tasks.
83 *
84 * @return void
85 */
86 public function main() {
87 $docHeaderButtons = $this->getButtons();
88 $markers = array();
89 $this->doc->postCode = $this->doc->wrapScriptTags('if (top.fsMod) { top.fsMod.recentIds["web"] = 0; }');
90
91 // Render content depending on the mode
92 $mode = (string)$this->MOD_SETTINGS['mode'];
93 if ($mode === 'information') {
94 $this->renderInformationContent();
95 } else {
96 $this->renderModuleContent();
97 }
98 // Compile document
99 $markers['FUNC_MENU'] = BackendUtility::getFuncMenu(0, 'SET[mode]', $this->MOD_SETTINGS['mode'], $this->MOD_MENU['mode']);
100 $markers['CONTENT'] = $this->content;
101 // Build the <body> for the module
102 $this->content = $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
103 // Renders the module page
104 $this->content = $this->doc->render($this->getLanguageService()->getLL('title'), $this->content);
105 }
106
107 /**
108 * Prints out the module's HTML
109 *
110 * @return void
111 */
112 public function printContent() {
113 echo $this->content;
114 }
115
116 /**
117 * Generates the module content by calling the selected task
118 *
119 * @return void
120 */
121 protected function renderModuleContent() {
122 $chosenTask = (string)$this->MOD_SETTINGS['function'];
123 // Render the taskcenter task as default
124 if (empty($chosenTask) || $chosenTask == 'index') {
125 $chosenTask = 'taskcenter.tasks';
126 }
127 // Render the task
128 $actionContent = '';
129 list($extKey, $taskClass) = explode('.', $chosenTask, 2);
130 if (class_exists($taskClass)) {
131 $taskInstance = GeneralUtility::makeInstance($taskClass, $this);
132 if ($taskInstance instanceof TaskInterface) {
133 // Check if the task is restricted to admins only
134 if ($this->checkAccess($extKey, $taskClass)) {
135 $actionContent .= $taskInstance->getTask();
136 } else {
137 $flashMessage = GeneralUtility::makeInstance(
138 FlashMessage::class,
139 $this->getLanguageService()->getLL('error-access', TRUE),
140 $this->getLanguageService()->getLL('error_header'),
141 FlashMessage::ERROR
142 );
143 $actionContent .= $flashMessage->render();
144 }
145 } else {
146 // Error if the task is not an instance of \TYPO3\CMS\Taskcenter\TaskInterface
147 $flashMessage = GeneralUtility::makeInstance(
148 FlashMessage::class,
149 sprintf($this->getLanguageService()->getLL('error_no-instance', TRUE), $taskClass, TaskInterface::class),
150 $this->getLanguageService()->getLL('error_header'),
151 FlashMessage::ERROR
152 );
153 $actionContent .= $flashMessage->render();
154 }
155 } else {
156 $flashMessage = GeneralUtility::makeInstance(
157 FlashMessage::class,
158 $this->getLanguageService()->sL('LLL:EXT:taskcenter/task/locallang_mod.xlf:mlang_labels_tabdescr'),
159 $this->getLanguageService()->sL('LLL:EXT:taskcenter/task/locallang_mod.xlf:mlang_tabs_tab'),
160 FlashMessage::INFO
161 );
162 $actionContent .= $flashMessage->render();
163 }
164 $content = '<div id="taskcenter-main">
165 <div id="taskcenter-menu">' . $this->indexAction() . '</div>
166 <div id="taskcenter-item" class="' . htmlspecialchars(($extKey . '-' . $taskClass)) . '">' . $actionContent . '
167 </div>
168 </div>';
169 $this->content .= $content;
170 }
171
172 /**
173 * Generates the information content
174 *
175 * @return void
176 */
177 protected function renderInformationContent() {
178 $content = $this->description($this->getLanguageService()->getLL('mlang_tabs_tab'), $this->getLanguageService()->sL('LLL:EXT:taskcenter/task/locallang_mod.xlf:mlang_labels_tabdescr'));
179 $content .= $this->getLanguageService()->getLL('taskcenter-about');
180 if ($this->getBackendUser()->isAdmin()) {
181 $content .= '<br /><br />' . $this->description($this->getLanguageService()->getLL('taskcenter-adminheader'), $this->getLanguageService()->getLL('taskcenter-admin'));
182 }
183 $this->content .= $content;
184 }
185
186 /**
187 * Render the headline of a task including a title and an optional description.
188 *
189 * @param string $title Title
190 * @param string $description Description
191 * @return string formatted title and description
192 */
193 public function description($title, $description = '') {
194 $content = '<h1>' . nl2br(htmlspecialchars($title)) . '</h1>';
195 if (!empty($description)) {
196 $content .= '<p class="description">' . nl2br(htmlspecialchars($description)) . '</p>';
197 }
198 return $content;
199 }
200
201 /**
202 * Render a list of items as a nicely formated definition list including a
203 * link, icon, title and description.
204 * The keys of a single item are:
205 * - title: Title of the item
206 * - link: Link to the task
207 * - icon: Path to the icon or Icon as HTML if it begins with <img
208 * - description: Description of the task, using htmlspecialchars()
209 * - descriptionHtml: Description allowing HTML tags which will override the
210 * description
211 *
212 * @param array $items List of items to be displayed in the definition list.
213 * @param bool $mainMenu Set it to TRUE to render the main menu
214 * @return string Fefinition list
215 */
216 public function renderListMenu($items, $mainMenu = FALSE) {
217 $content = ($section = '');
218 $count = 0;
219 // Change the sorting of items to the user's one
220 if ($mainMenu) {
221 $this->doc->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Taskcenter/Taskcenter');
222 $userSorting = unserialize($this->getBackendUser()->uc['taskcenter']['sorting']);
223 if (is_array($userSorting)) {
224 $newSorting = array();
225 foreach ($userSorting as $item) {
226 if (isset($items[$item])) {
227 $newSorting[] = $items[$item];
228 unset($items[$item]);
229 }
230 }
231 $items = $newSorting + $items;
232 }
233 }
234 if (is_array($items) && count($items) > 0) {
235 foreach ($items as $item) {
236 $title = htmlspecialchars($item['title']);
237 $icon = ($additionalClass = ($collapsedStyle = ''));
238 // Check for custom icon
239 if (!empty($item['icon'])) {
240 if (strpos($item['icon'], '<img ') === FALSE) {
241 $absIconPath = GeneralUtility::getFileAbsFilename($item['icon']);
242 // If the file indeed exists, assemble relative path to it
243 if (file_exists($absIconPath)) {
244 $icon = $GLOBALS['BACK_PATH'] . '../' . str_replace(PATH_site, '', $absIconPath);
245 $icon = '<img src="' . $icon . '" title="' . $title . '" alt="' . $title . '" />';
246 }
247 if (@is_file($icon)) {
248 $icon = '<img' . IconUtility::skinImg($GLOBALS['BACK_PATH'], $icon, 'width="16" height="16"') . ' title="' . $title . '" alt="' . $title . '" />';
249 }
250 } else {
251 $icon = $item['icon'];
252 }
253 }
254 $description = $item['descriptionHtml'] ?: '<p>' . nl2br(htmlspecialchars($item['description'])) . '</p>';
255 $id = $this->getUniqueKey($item['uid']);
256 // Collapsed & expanded menu items
257 if ($mainMenu && isset($this->getBackendUser()->uc['taskcenter']['states'][$id]) && $this->getBackendUser()->uc['taskcenter']['states'][$id]) {
258 $collapsedStyle = 'style="display:none"';
259 $additionalClass = 'collapsed';
260 } else {
261 $additionalClass = 'expanded';
262 }
263 // First & last menu item
264 if ($count == 0) {
265 $additionalClass .= ' first-item';
266 } elseif ($count + 1 === count($items)) {
267 $additionalClass .= ' last-item';
268 }
269 // Active menu item
270 $active = (string)$this->MOD_SETTINGS['function'] == $item['uid'] ? ' active-task' : '';
271 // Main menu: Render additional syntax to sort tasks
272 if ($mainMenu) {
273 $dragIcon = IconUtility::getSpriteIcon('actions-document-move', array('title' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.move', TRUE)));
274 $section = '<div class="down">&nbsp;</div>
275 <div class="drag">' . $dragIcon . '</div>';
276 $backgroundClass = 't3-row-header ';
277 } else {
278 $backgroundClass = '';
279 }
280 $content .= '<li class="' . $additionalClass . $active . '" id="el_' . $id . '">
281 ' . $section . '
282 <div class="image">' . $icon . '</div>
283 <div class="' . $backgroundClass . 'link"><a href="' . $item['link'] . '">' . $title . '</a></div>
284 <div class="content " ' . $collapsedStyle . '>' . $description . '</div>
285 </li>';
286 $count++;
287 }
288 $navigationId = $mainMenu ? 'id="task-list"' : '';
289 $content = '<ul ' . $navigationId . ' class="task-list">' . $content . '</ul>';
290 }
291 return $content;
292 }
293
294 /**
295 * Shows an overview list of available reports.
296 *
297 * @return string List of available reports
298 */
299 protected function indexAction() {
300 $content = '';
301 $tasks = array();
302 $icon = ExtensionManagementUtility::extRelPath('taskcenter') . 'Resources/Public/Icons/module-taskcenter.png';
303 // Render the tasks only if there are any available
304 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter']) && count($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter']) > 0) {
305 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'] as $extKey => $extensionReports) {
306 foreach ($extensionReports as $taskClass => $task) {
307 if (!$this->checkAccess($extKey, $taskClass)) {
308 continue;
309 }
310 $link = BackendUtility::getModuleUrl('user_task') . '&SET[function]=' . $extKey . '.' . $taskClass;
311 $taskTitle = $this->getLanguageService()->sL($task['title']);
312 $taskDescriptionHtml = '';
313 // Check for custom icon
314 if (!empty($task['icon'])) {
315 $icon = GeneralUtility::getFileAbsFilename($task['icon']);
316 }
317 if (class_exists($taskClass)) {
318 $taskInstance = GeneralUtility::makeInstance($taskClass, $this);
319 if ($taskInstance instanceof TaskInterface) {
320 $taskDescriptionHtml = $taskInstance->getOverview();
321 }
322 }
323 // Generate an array of all tasks
324 $uniqueKey = $this->getUniqueKey($extKey . '.' . $taskClass);
325 $tasks[$uniqueKey] = array(
326 'title' => $taskTitle,
327 'descriptionHtml' => $taskDescriptionHtml,
328 'description' => $this->getLanguageService()->sL($task['description']),
329 'icon' => $icon,
330 'link' => $link,
331 'uid' => $extKey . '.' . $taskClass
332 );
333 }
334 }
335 $content .= $this->renderListMenu($tasks, TRUE);
336 } else {
337 $flashMessage = GeneralUtility::makeInstance(
338 FlashMessage::class,
339 $this->getLanguageService()->getLL('no-tasks', TRUE),
340 '',
341 FlashMessage::INFO
342 );
343 $this->content .= $flashMessage->render();
344 }
345 return $content;
346 }
347
348 /**
349 * Create the panel of buttons for submitting the form or otherwise
350 * perform operations.
351 *
352 * @return array All available buttons as an assoc. array
353 */
354 protected function getButtons() {
355 $buttons = array(
356 'shortcut' => '',
357 'open_new_window' => $this->openInNewWindow()
358 );
359 // Shortcut
360 if ($this->getBackendUser()->mayMakeShortcut()) {
361 $buttons['shortcut'] = $this->doc->makeShortcutIcon('', 'function', $this->moduleName);
362 }
363 return $buttons;
364 }
365
366 /**
367 * Check the access to a task. Considered are:
368 * - Admins are always allowed
369 * - Tasks can be restriced to admins only
370 * - Tasks can be blinded for Users with TsConfig taskcenter.<extensionkey>.<taskName> = 0
371 *
372 * @param string $extKey Extension key
373 * @param string $taskClass Name of the task
374 * @return bool Access to the task allowed or not
375 */
376 protected function checkAccess($extKey, $taskClass) {
377 // Check if task is blinded with TsConfig (taskcenter.<extkey>.<taskName>
378 $tsConfig = $this->getBackendUser()->getTSConfig('taskcenter.' . $extKey . '.' . $taskClass);
379 if (isset($tsConfig['value']) && (int)$tsConfig['value'] === 0) {
380 return FALSE;
381 }
382 // Admins are always allowed
383 if ($this->getBackendUser()->isAdmin()) {
384 return TRUE;
385 }
386 // Check if task is restricted to admins
387 if ((int)$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['taskcenter'][$extKey][$taskClass]['admin'] === 1) {
388 return FALSE;
389 }
390 return TRUE;
391 }
392
393 /**
394 * Returns HTML code to dislay an url in an iframe at the right side of the taskcenter
395 *
396 * @param string $url Url to display
397 * @param int $max
398 * @return string Code that inserts the iframe (HTML)
399 */
400 public function urlInIframe($url, $max = 0) {
401 return '<iframe scrolling="auto" width="100%" src="' . $url . '" name="list_frame" id="list_frame" frameborder="no"></iframe>';
402 }
403
404 /**
405 * Create a unique key from a string which can be used in Prototype's Sortable
406 * Therefore '_' are replaced
407 *
408 * @param string $string string which is used to generate the identifier
409 * @return string Modified string
410 */
411 protected function getUniqueKey($string) {
412 $search = array('.', '_');
413 $replace = array('-', '');
414 return str_replace($search, $replace, $string);
415 }
416
417 /**
418 * This method prepares the link for opening the devlog in a new window
419 *
420 * @return string Hyperlink with icon and appropriate JavaScript
421 */
422 protected function openInNewWindow() {
423 $url = GeneralUtility::getIndpEnv('TYPO3_REQUEST_URL');
424 $onClick = 'devlogWin=window.open(' . GeneralUtility::quoteJSvalue($url) . ',\'taskcenter\',\'width=790,status=0,menubar=1,resizable=1,location=0,scrollbars=1,toolbar=0\');return false;';
425 $content = '<a href="#" onclick="' . htmlspecialchars($onClick) . '">' .
426 IconUtility::getSpriteIcon('actions-window-open', array('title' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:labels.openInNewWindow', TRUE))) .
427 '</a>';
428 return $content;
429 }
430
431 /**
432 * Returns the current BE user.
433 *
434 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
435 */
436 protected function getBackendUser() {
437 return $GLOBALS['BE_USER'];
438 }
439
440 /**
441 * Returns LanguageService
442 *
443 * @return \TYPO3\CMS\Lang\LanguageService
444 */
445 protected function getLanguageService() {
446 return $GLOBALS['LANG'];
447 }
448
449 }