[TASK] Streamline taskmanager
[Packages/TYPO3.CMS.git] / typo3 / sysext / sys_action / Classes / ActionTask.php
1 <?php
2 namespace TYPO3\CMS\SysAction;
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\Core\Imaging\Icon;
19 use TYPO3\CMS\Core\Imaging\IconFactory;
20 use TYPO3\CMS\Core\Messaging\FlashMessage;
21 use TYPO3\CMS\Core\Messaging\FlashMessageService;
22 use TYPO3\CMS\Core\Page\PageRenderer;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24
25 /**
26 * This class provides a task for the taskcenter
27 */
28 class ActionTask implements \TYPO3\CMS\Taskcenter\TaskInterface
29 {
30 /**
31 * @var \TYPO3\CMS\Taskcenter\Controller\TaskModuleController
32 */
33 protected $taskObject;
34
35 /**
36 * All hook objects get registered here for later use
37 *
38 * @var array
39 */
40 protected $hookObjects = array();
41
42 /**
43 * URL to task module
44 *
45 * @var string
46 */
47 protected $moduleUrl;
48
49 /**
50 * @var IconFactory
51 */
52 protected $iconFactory;
53
54 /**
55 * Constructor
56 */
57 public function __construct(\TYPO3\CMS\Taskcenter\Controller\TaskModuleController $taskObject)
58 {
59 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
60 $this->moduleUrl = BackendUtility::getModuleUrl('user_task');
61 $this->taskObject = $taskObject;
62 $this->getLanguageService()->includeLLFile('EXT:sys_action/Resources/Private/Language/locallang.xlf');
63 if (is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['sys_action']['tx_sysaction_task'])) {
64 foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['sys_action']['tx_sysaction_task'] as $classRef) {
65 $this->hookObjects[] = GeneralUtility::getUserObj($classRef);
66 }
67 }
68 }
69
70 /**
71 * This method renders the task
72 *
73 * @return string The task as HTML
74 */
75 public function getTask()
76 {
77 $content = '';
78 $show = (int)GeneralUtility::_GP('show');
79 foreach ($this->hookObjects as $hookObject) {
80 if (method_exists($hookObject, 'getTask')) {
81 $show = $hookObject->getTask($show, $this);
82 }
83 }
84 // If no task selected, render the menu
85 if ($show == 0) {
86 $content .= $this->taskObject->description($this->getLanguageService()->getLL('sys_action'), $this->getLanguageService()->getLL('description'));
87 $content .= $this->renderActionList();
88 } else {
89 $record = BackendUtility::getRecord('sys_action', $show);
90 // If the action is not found
91 if (empty($record)) {
92 $this->addMessage(
93 $this->getLanguageService()->getLL('action_error-not-found'),
94 $this->getLanguageService()->getLL('action_error'),
95 FlashMessage::ERROR
96 );
97 } else {
98 // Render the task
99 $content .= $this->taskObject->description($record['title'], $record['description']);
100 // Output depends on the type
101 switch ($record['type']) {
102 case 1:
103 $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
104 $pageRenderer->loadRequireJsModule('TYPO3/CMS/SysAction/ActionTask');
105 $content .= $this->viewNewBackendUser($record);
106 break;
107 case 2:
108 $content .= $this->viewSqlQuery($record);
109 break;
110 case 3:
111 $content .= $this->viewRecordList($record);
112 break;
113 case 4:
114 $content .= $this->viewEditRecord($record);
115 break;
116 case 5:
117 $content .= $this->viewNewRecord($record);
118 break;
119 default:
120 $this->addMessage(
121 $this->getLanguageService()->getLL('action_noType'),
122 $this->getLanguageService()->getLL('action_error'),
123 FlashMessage::ERROR
124 );
125 $content .= $this->renderFlashMessages();
126 }
127 }
128 }
129 return $content;
130 }
131
132 /**
133 * General overview over the task in the taskcenter menu
134 *
135 * @return string Overview as HTML
136 */
137 public function getOverview()
138 {
139 $content = '<p>' . $this->getLanguageService()->getLL('description') . '</p>';
140 // Get the actions
141 $actionList = $this->getActions();
142 if (!empty($actionList)) {
143 $items = '';
144 // Render a single action menu item
145 foreach ($actionList as $action) {
146 $active = GeneralUtility::_GP('show') === $action['uid'] ? 'active' : '';
147 $items .= '<a class="list-group-item ' . $active . '" href="' . $action['link'] . '" title="' . htmlspecialchars($action['description']) . '">' . htmlspecialchars($action['title']) . '</a>';
148 }
149 $content .= '<div class="list-group">' . $items . '</div>';
150 }
151 return $content;
152 }
153
154 /**
155 * Get all actions of an user. Admins can see any action, all others only those
156 * which are allowed in sys_action record itself.
157 *
158 * @return array Array holding every needed information of a sys_action
159 */
160 protected function getActions()
161 {
162 $actionList = array();
163 // admins can see any record
164 if ($this->getBackendUser()->isAdmin()) {
165 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'sys_action', '', '', 'sys_action.sorting');
166 } else {
167 // Editors can only see the actions which are assigned to a usergroup they belong to
168 $additionalWhere = 'be_groups.uid IN (' . ($this->getBackendUser()->groupList ?: 0) . ')';
169 $res = $this->getDatabaseConnection()->exec_SELECT_mm_query('sys_action.*', 'sys_action', 'sys_action_asgr_mm', 'be_groups', ' AND sys_action.hidden=0 AND ' . $additionalWhere, 'sys_action.uid', 'sys_action.sorting');
170 }
171 while ($actionRow = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
172 $editActionLink = '';
173 // Admins are allowed to edit sys_action records
174 if ($this->getBackendUser()->isAdmin()) {
175 $link = BackendUtility::getModuleUrl(
176 'record_edit',
177 array(
178 'edit[sys_action][' . $actionRow['uid'] . ']' => 'edit',
179 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
180 ),
181 false,
182 true
183 );
184 $title = 'title="' . $this->getLanguageService()->getLL('edit-sys_action') . '"';
185 $icon = $this->iconFactory->getIcon('actions-document-open', Icon::SIZE_SMALL)->render();
186 $editActionLink = '<a class="btn btn-default btn-sm" href="' . $link . '"' . $title . '>';
187 $editActionLink .= $icon . ' ' . $this->getLanguageService()->getLL('edit-sys_action') . '</a>';
188 }
189 $actionList[] = array(
190 'uid' => 'actiontask' . $actionRow['uid'],
191 'title' => $actionRow['title'],
192 'description' => $actionRow['description'],
193 'descriptionHtml' => ($actionRow['description'] ? '<p>' . nl2br(htmlspecialchars($actionRow['description'])) . '</p>' : '' ) . $editActionLink,
194 'link' => $this->moduleUrl . '&SET[function]=sys_action.TYPO3\\CMS\\SysAction\\ActionTask&show=' . $actionRow['uid']
195 );
196 }
197 $this->getDatabaseConnection()->sql_free_result($res);
198 return $actionList;
199 }
200
201 /**
202 * Render the menu of sys_actions
203 *
204 * @return string List of sys_actions as HTML
205 */
206 protected function renderActionList()
207 {
208 $content = '';
209 // Get the sys_action records
210 $actionList = $this->getActions();
211 // If any actions are found for the current users
212 if (!empty($actionList)) {
213 $content .= $this->taskObject->renderListMenu($actionList);
214 } else {
215 $this->addMessage(
216 $this->getLanguageService()->getLL('action_not-found-description'),
217 $this->getLanguageService()->getLL('action_not-found'),
218 FlashMessage::INFO
219 );
220 }
221 // Admin users can create a new action
222 if ($this->getBackendUser()->isAdmin()) {
223 $link = BackendUtility::getModuleUrl(
224 'record_edit',
225 array(
226 'edit[sys_action][0]' => 'new',
227 'returnUrl' => $this->moduleUrl
228 ),
229 false,
230 true
231 );
232 $content .= '<p>' .
233 '<a class="btn btn-default" href="' . $link . '" title="' . $this->getLanguageService()->getLL('new-sys_action') . '">' .
234 $this->iconFactory->getIcon('actions-document-new', Icon::SIZE_SMALL)->render() . ' ' .
235 $this->getLanguageService()->getLL('new-sys_action') .
236 '</a></p>';
237 }
238 return $content;
239 }
240
241 /**
242 * Action to create a new BE user
243 *
244 * @param array $record sys_action record
245 * @return string form to create a new user
246 */
247 protected function viewNewBackendUser($record)
248 {
249 $content = '';
250 $beRec = BackendUtility::getRecord('be_users', (int)$record['t1_copy_of_user']);
251 // A record is need which is used as copy for the new user
252 if (!is_array($beRec)) {
253 $this->addMessage(
254 $this->getLanguageService()->getLL('action_notReady'),
255 $this->getLanguageService()->getLL('action_error'),
256 FlashMessage::ERROR
257 );
258 $content .= $this->renderFlashMessages();
259 return $content;
260 }
261 $vars = GeneralUtility::_POST('data');
262 $key = 'NEW';
263 if ($vars['sent'] == 1) {
264 $errors = array();
265 // Basic error checks
266 if (!empty($vars['email']) && !GeneralUtility::validEmail($vars['email'])) {
267 $errors[] = $this->getLanguageService()->getLL('error-wrong-email');
268 }
269 if (empty($vars['username'])) {
270 $errors[] = $this->getLanguageService()->getLL('error-username-empty');
271 }
272 if ($vars['key'] === 'NEW' && empty($vars['password'])) {
273 $errors[] = $this->getLanguageService()->getLL('error-password-empty');
274 }
275 if ($vars['key'] !== 'NEW' && !$this->isCreatedByUser($vars['key'], $record)) {
276 $errors[] = $this->getLanguageService()->getLL('error-wrong-user');
277 }
278 foreach ($this->hookObjects as $hookObject) {
279 if (method_exists($hookObject, 'viewNewBackendUser_Error')) {
280 $errors = $hookObject->viewNewBackendUser_Error($vars, $errors, $this);
281 }
282 }
283 // Show errors if there are any
284 if (!empty($errors)) {
285 $this->addMessage(
286 implode(LF, $errors),
287 $this->getLanguageService()->getLL('action_error'),
288 FlashMessage::ERROR
289 );
290 } else {
291 // Save user
292 $key = $this->saveNewBackendUser($record, $vars);
293 // Success message
294 $message = $vars['key'] === 'NEW'
295 ? $this->getLanguageService()->getLL('success-user-created')
296 : $this->getLanguageService()->getLL('success-user-updated');
297 $this->addMessage(
298 $message,
299 $this->getLanguageService()->getLL('success')
300 );
301 }
302 $content .= $this->renderFlashMessages();
303 }
304 // Load BE user to edit
305 if ((int)GeneralUtility::_GP('be_users_uid') > 0) {
306 $tmpUserId = (int)GeneralUtility::_GP('be_users_uid');
307 // Check if the selected user is created by the current user
308 $rawRecord = $this->isCreatedByUser($tmpUserId, $record);
309 if ($rawRecord) {
310 // Delete user
311 if (GeneralUtility::_GP('delete') == 1) {
312 $this->deleteUser($tmpUserId, $record['uid']);
313 }
314 $key = $tmpUserId;
315 $vars = $rawRecord;
316 }
317 }
318 $content .= '<form action="" class="panel panel-default" method="post" enctype="multipart/form-data">
319 <fieldset class="form-section">
320 <h4 class="form-section-headline">' . $this->getLanguageService()->getLL('action_t1_legend_generalFields') . '</h4>
321 <div class="form-group">
322 <label for="field_disable">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_general.xlf:LGL.disable') . '</label>
323 <input type="checkbox" id="field_disable" name="data[disable]" value="1" class="checkbox" ' . ($vars['disable'] == 1 ? ' checked="checked" ' : '') . ' />
324 </div>
325 <div class="form-group">
326 <label for="field_realname">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_general.xlf:LGL.name') . '</label>
327 <input type="text" id="field_realname" class="form-control" name="data[realName]" value="' . htmlspecialchars($vars['realName']) . '" />
328 </div>
329 <div class="form-group">
330 <label for="field_username">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_tca.xlf:be_users.username') . '</label>
331 <input type="text" id="field_username" class="form-control" name="data[username]" value="' . htmlspecialchars($vars['username']) . '" />
332 </div>
333 <div class="form-group">
334 <label for="field_password">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_tca.xlf:be_users.password') . '</label>
335 <input type="password" id="field_password" class="form-control" name="data[password]" value="" />
336 </div>
337 <div class="form-group">
338 <label for="field_email">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_general.xlf:LGL.email') . '</label>
339 <input type="text" id="field_email" class="form-control" name="data[email]" value="' . htmlspecialchars($vars['email']) . '" />
340 </div>
341 </fieldset>
342 <fieldset class="form-section">
343 <h4 class="form-section-headline">' . $this->getLanguageService()->getLL('action_t1_legend_configuration') . '</h4>
344 <div class="form-group">
345 <label for="field_usergroup">' . $this->getLanguageService()->sL('LLL:EXT:lang/locallang_tca.xlf:be_users.usergroup') . '</label>
346 <select id="field_usergroup" class="form-control" name="data[usergroup][]" multiple="multiple">
347 ' . $this->getUsergroups($record, $vars) . '
348 </select>
349 </div>
350 <div class="form-group">
351 <input type="hidden" name="data[key]" value="' . $key . '" />
352 <input type="hidden" name="data[sent]" value="1" />
353 <input class="btn btn-default" type="submit" value="' . ($key === 'NEW' ? $this->getLanguageService()->getLL('action_Create') : $this->getLanguageService()->getLL('action_Update')) . '" />
354 </div>
355 </fieldset>
356 </form>';
357 $content .= $this->getCreatedUsers($record, $key);
358 return $content;
359 }
360
361 /**
362 * Delete a BE user and redirect to the action by its id
363 *
364 * @param int $userId Id of the BE user
365 * @param int $actionId Id of the action
366 * @return void
367 */
368 protected function deleteUser($userId, $actionId)
369 {
370 $this->getDatabaseConnection()->exec_UPDATEquery('be_users', 'uid=' . $userId, array(
371 'deleted' => 1,
372 'tstamp' => $GLOBALS['ACCESS_TIME']
373 ));
374 // redirect to the original task
375 $redirectUrl = $this->moduleUrl . '&show=' . $actionId;
376 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($redirectUrl);
377 }
378
379 /**
380 * Check if a BE user is created by the current user
381 *
382 * @param int $id Id of the BE user
383 * @param array $action sys_action record.
384 * @return mixed The record of the BE user if found, otherwise FALSE
385 */
386 protected function isCreatedByUser($id, $action)
387 {
388 $record = BackendUtility::getRecord('be_users', $id, '*', ' AND cruser_id=' . $this->getBackendUser()->user['uid'] . ' AND createdByAction=' . $action['uid']);
389 if (is_array($record)) {
390 return $record;
391 } else {
392 return false;
393 }
394 }
395
396 /**
397 * Render all users who are created by the current BE user including a link to edit the record
398 *
399 * @param array $action sys_action record.
400 * @param int $selectedUser Id of a selected user
401 * @return string html list of users
402 */
403 protected function getCreatedUsers($action, $selectedUser)
404 {
405 $content = '';
406 $userList = array();
407 // List of users
408 $res = $this->getDatabaseConnection()->exec_SELECTquery('*', 'be_users', 'cruser_id=' . $this->getBackendUser()->user['uid'] . ' AND createdByAction=' . (int)$action['uid'] . BackendUtility::deleteClause('be_users'), '', 'username');
409 // Render the user records
410 while ($row = $this->getDatabaseConnection()->sql_fetch_assoc($res)) {
411 $icon = '<span title="' . htmlspecialchars('uid=' . $row['uid']) . '">' . $this->iconFactory->getIconForRecord('be_users', $row, Icon::SIZE_SMALL)->render() . '</span>';
412 $line = $icon . $this->action_linkUserName($row['username'], $row['realName'], $action['uid'], $row['uid']);
413 // Selected user
414 if ($row['uid'] == $selectedUser) {
415 $line = '<strong>' . $line . '</strong>';
416 }
417 $userList[] = '<li class="list-group-item">' . $line . '</li>';
418 }
419 $this->getDatabaseConnection()->sql_free_result($res);
420 // If any records found
421 if (!empty($userList)) {
422 $content .= '<div class="panel panel-default">';
423 $content .= '<div class="panel-heading">';
424 $content .= '<h3 class="panel-title">' . $this->getLanguageService()->getLL('action_t1_listOfUsers', true) . '</h3>';
425 $content .= '</div>';
426 $content .= '<ul class="list-group">' . implode($userList) . '</ul>';
427 $content .= '</div>';
428 }
429 return $content;
430 }
431
432 /**
433 * Create a link to edit a user
434 *
435 * @param string $username Username
436 * @param string $realName Real name of the user
437 * @param int $sysActionUid Id of the sys_action record
438 * @param int $userId Id of the user
439 * @return string html link
440 */
441 protected function action_linkUserName($username, $realName, $sysActionUid, $userId)
442 {
443 if (!empty($realName)) {
444 $username .= ' (' . $realName . ')';
445 }
446 // Link to update the user record
447 $href = $this->moduleUrl . '&SET[function]=sys_action.TYPO3\\CMS\\SysAction\\ActionTask&show=' . (int)$sysActionUid . '&be_users_uid=' . (int)$userId;
448 $link = '<a href="' . htmlspecialchars($href) . '">' . htmlspecialchars($username) . '</a>';
449 // Link to delete the user record
450 $link .= '
451 <a href="' . htmlspecialchars(($href . '&delete=1')) . '" class="t3js-confirm-trigger" data-title="' . $this->getLanguageService()->getLL('lDelete_warning_title', true) . '" data-message="' . $this->getLanguageService()->getLL('lDelete_warning', true) . '">'
452 . $this->iconFactory->getIcon('actions-edit-delete', Icon::SIZE_SMALL)->render() .
453 '</a>';
454 return $link;
455 }
456
457 /**
458 * Save/Update a BE user
459 *
460 * @param array $record Current action record
461 * @param array $vars POST vars
462 * @return int Id of the new/updated user
463 */
464 protected function saveNewBackendUser($record, $vars)
465 {
466 // Check if the db mount is a page the current user is allowed to.);
467 $vars['db_mountpoints'] = $this->fixDbMount($vars['db_mountpoints']);
468 // Check if the usergroup is allowed
469 $vars['usergroup'] = $this->fixUserGroup($vars['usergroup'], $record);
470 $key = $vars['key'];
471 $vars['password'] = trim($vars['password']);
472 // Check if md5 is used as password encryption
473 if ($vars['password'] !== '' && strpos($GLOBALS['TCA']['be_users']['columns']['password']['config']['eval'], 'md5') !== false) {
474 $vars['password'] = md5($vars['password']);
475 }
476 $data = '';
477 $newUserId = 0;
478 if ($key === 'NEW') {
479 $beRec = BackendUtility::getRecord('be_users', (int)$record['t1_copy_of_user']);
480 if (is_array($beRec)) {
481 $data = array();
482 $data['be_users'][$key] = $beRec;
483 $data['be_users'][$key]['username'] = $this->fixUsername($vars['username'], $record['t1_userprefix']);
484 $data['be_users'][$key]['password'] = $vars['password'];
485 $data['be_users'][$key]['realName'] = $vars['realName'];
486 $data['be_users'][$key]['email'] = $vars['email'];
487 $data['be_users'][$key]['disable'] = (int)$vars['disable'];
488 $data['be_users'][$key]['admin'] = 0;
489 $data['be_users'][$key]['usergroup'] = $vars['usergroup'];
490 $data['be_users'][$key]['db_mountpoints'] = $vars['db_mountpoints'];
491 $data['be_users'][$key]['createdByAction'] = $record['uid'];
492 }
493 } else {
494 // Check ownership
495 $beRec = BackendUtility::getRecord('be_users', (int)$key);
496 if (is_array($beRec) && $beRec['cruser_id'] == $this->getBackendUser()->user['uid']) {
497 $data = array();
498 $data['be_users'][$key]['username'] = $this->fixUsername($vars['username'], $record['t1_userprefix']);
499 if ($vars['password'] !== '') {
500 $data['be_users'][$key]['password'] = $vars['password'];
501 }
502 $data['be_users'][$key]['realName'] = $vars['realName'];
503 $data['be_users'][$key]['email'] = $vars['email'];
504 $data['be_users'][$key]['disable'] = (int)$vars['disable'];
505 $data['be_users'][$key]['admin'] = 0;
506 $data['be_users'][$key]['usergroup'] = $vars['usergroup'];
507 $data['be_users'][$key]['db_mountpoints'] = $vars['db_mountpoints'];
508 $newUserId = $key;
509 }
510 }
511 // Save/update user by using TCEmain
512 if (is_array($data)) {
513 $tce = GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
514 $tce->start($data, array(), $this->getBackendUser());
515 $tce->admin = 1;
516 $tce->process_datamap();
517 $newUserId = (int)$tce->substNEWwithIDs['NEW'];
518 if ($newUserId) {
519 // Create
520 $this->action_createDir($newUserId);
521 } else {
522 // Update
523 $newUserId = (int)$key;
524 }
525 unset($tce);
526 }
527 return $newUserId;
528 }
529
530 /**
531 * Create the username based on the given username and the prefix
532 *
533 * @param string $username Username
534 * @param string $prefix Prefix
535 * @return string Combined username
536 */
537 protected function fixUsername($username, $prefix)
538 {
539 $prefix = trim($prefix);
540 if (substr($username, 0, strlen($prefix)) === $prefix) {
541 $username = substr($username, strlen($prefix));
542 }
543 return $prefix . $username;
544 }
545
546 /**
547 * Clean the to be applied usergroups from not allowed ones
548 *
549 * @param array $appliedUsergroups Array of to be applied user groups
550 * @param array $actionRecord The action record
551 * @return array Cleaned array
552 */
553 protected function fixUserGroup($appliedUsergroups, $actionRecord)
554 {
555 if (is_array($appliedUsergroups)) {
556 $cleanGroupList = array();
557 // Create an array from the allowed usergroups using the uid as key
558 $allowedUsergroups = array_flip(explode(',', $actionRecord['t1_allowed_groups']));
559 // Walk through the array and check every uid if it is under the allowed ines
560 foreach ($appliedUsergroups as $group) {
561 if (isset($allowedUsergroups[$group])) {
562 $cleanGroupList[] = $group;
563 }
564 }
565 $appliedUsergroups = $cleanGroupList;
566 }
567 return $appliedUsergroups;
568 }
569
570 /**
571 * Clean the to be applied DB-Mounts from not allowed ones
572 *
573 * @param string $appliedDbMounts List of pages like pages_123,pages456
574 * @return string Cleaned list
575 */
576 protected function fixDbMount($appliedDbMounts)
577 {
578 // Admins can see any page, no need to check there
579 if (!empty($appliedDbMounts) && !$this->getBackendUser()->isAdmin()) {
580 $cleanDbMountList = array();
581 $dbMounts = GeneralUtility::trimExplode(',', $appliedDbMounts, true);
582 // Walk through every wanted DB-Mount and check if it allowed for the current user
583 foreach ($dbMounts as $dbMount) {
584 $uid = (int)substr($dbMount, strrpos($dbMount, '_') + 1);
585 $page = BackendUtility::getRecord('pages', $uid);
586 // Check rootline and access rights
587 if ($this->checkRootline($uid) && $this->getBackendUser()->calcPerms($page)) {
588 $cleanDbMountList[] = 'pages_' . $uid;
589 }
590 }
591 // Build the clean list
592 $appliedDbMounts = implode(',', $cleanDbMountList);
593 }
594 return $appliedDbMounts;
595 }
596
597 /**
598 * Check if a page is inside the rootline the current user can see
599 *
600 * @param int $pageId Id of the the page to be checked
601 * @return bool Access to the page
602 */
603 protected function checkRootline($pageId)
604 {
605 $access = false;
606 $dbMounts = array_flip(explode(',', trim($this->getBackendUser()->dataLists['webmount_list'], ',')));
607 $rootline = BackendUtility::BEgetRootLine($pageId);
608 foreach ($rootline as $page) {
609 if (isset($dbMounts[$page['uid']]) && !$access) {
610 $access = true;
611 }
612 }
613 return $access;
614 }
615
616 /**
617 * Create a user directory if defined
618 *
619 * @param int $uid Id of the user record
620 * @return void
621 */
622 protected function action_createDir($uid)
623 {
624 $path = $this->action_getUserMainDir();
625 if ($path) {
626 GeneralUtility::mkdir($path . $uid);
627 GeneralUtility::mkdir($path . $uid . '/_temp_/');
628 }
629 }
630
631 /**
632 * Get the path to the user home directory which is set in the localconf.php
633 *
634 * @return string Path
635 */
636 protected function action_getUserMainDir()
637 {
638 $path = $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'];
639 // If path is set and a valid directory
640 if ($path && @is_dir($path) && $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] && GeneralUtility::isFirstPartOfStr($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']) && substr($path, -1) == '/') {
641 return $path;
642 }
643 }
644
645 /**
646 * Get all allowed usergroups which can be applied to a user record
647 *
648 * @param array $record sys_action record
649 * @param array $vars Selected be_user record
650 * @return string Rendered user groups
651 */
652 protected function getUsergroups($record, $vars)
653 {
654 $content = '';
655 // Do nothing if no groups are allowed
656 if (empty($record['t1_allowed_groups'])) {
657 return $content;
658 }
659 $content .= '<option value=""></option>';
660 $grList = GeneralUtility::trimExplode(',', $record['t1_allowed_groups'], true);
661 foreach ($grList as $group) {
662 $checkGroup = BackendUtility::getRecord('be_groups', $group);
663 if (is_array($checkGroup)) {
664 $selected = GeneralUtility::inList($vars['usergroup'], $checkGroup['uid']) ? ' selected="selected" ' : '';
665 $content .= '<option ' . $selected . 'value="' . $checkGroup['uid'] . '">' . htmlspecialchars($checkGroup['title']) . '</option>';
666 }
667 }
668 return $content;
669 }
670
671 /**
672 * Action to create a new record
673 *
674 * @param array $record sys_action record
675 * @return void Redirect to form to create a record
676 */
677 protected function viewNewRecord($record)
678 {
679 $link = BackendUtility::getModuleUrl(
680 'record_edit',
681 array(
682 'edit[' . $record['t3_tables'] . '][' . (int)$record['t3_listPid'] . ']' => 'new',
683 'returnUrl' => $this->moduleUrl
684 ),
685 false,
686 true
687 );
688 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($link);
689 }
690
691 /**
692 * Action to edit records
693 *
694 * @param array $record sys_action record
695 * @return string list of records
696 */
697 protected function viewEditRecord($record)
698 {
699 $content = '';
700 $actionList = array();
701 $dbAnalysis = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\RelationHandler::class);
702 $dbAnalysis->setFetchAllFields(true);
703 $dbAnalysis->start($record['t4_recordsToEdit'], '*');
704 $dbAnalysis->getFromDB();
705 // collect the records
706 foreach ($dbAnalysis->itemArray as $el) {
707 $path = BackendUtility::getRecordPath($el['id'], $this->taskObject->perms_clause, $this->getBackendUser()->uc['titleLen']);
708 $record = BackendUtility::getRecord($el['table'], $dbAnalysis->results[$el['table']][$el['id']]);
709 $title = BackendUtility::getRecordTitle($el['table'], $dbAnalysis->results[$el['table']][$el['id']]);
710 $description = $this->getLanguageService()->sL($GLOBALS['TCA'][$el['table']]['ctrl']['title'], true);
711 // @todo: which information could be needful
712 if (isset($record['crdate'])) {
713 $description .= ' - ' . BackendUtility::dateTimeAge($record['crdate']);
714 }
715 $link = BackendUtility::getModuleUrl(
716 'record_edit',
717 array(
718 'edit[' . $el['table'] . '][' . $el['id'] . ']' => 'edit',
719 'returnUrl' => $this->moduleUrl
720 ),
721 false,
722 true
723 );
724 $actionList[$el['id']] = array(
725 'uid' => 'record-' . $el['table'] . '-' . $el['id'],
726 'title' => $title,
727 'description' => BackendUtility::getRecordTitle($el['table'], $dbAnalysis->results[$el['table']][$el['id']]),
728 'descriptionHtml' => $description,
729 'link' => $link,
730 'icon' => '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIconForRecord($el['table'], $dbAnalysis->results[$el['table']][$el['id']], Icon::SIZE_SMALL)->render() . '</span>'
731 );
732 }
733 // Render the record list
734 $content .= $this->taskObject->renderListMenu($actionList);
735 return $content;
736 }
737
738 /**
739 * Action to view the result of a SQL query
740 *
741 * @param array $record sys_action record
742 * @return string Result of the query
743 */
744 protected function viewSqlQuery($record)
745 {
746 $content = '';
747 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('lowlevel')) {
748 $sql_query = unserialize($record['t2_data']);
749 if (!is_array($sql_query) || is_array($sql_query) && strtoupper(substr(trim($sql_query['qSelect']), 0, 6)) === 'SELECT') {
750 $actionContent = '';
751 $fullsearch = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\QueryView::class);
752 $fullsearch->formW = 40;
753 $fullsearch->noDownloadB = 1;
754 $type = $sql_query['qC']['search_query_makeQuery'];
755 if ($sql_query['qC']['labels_noprefix'] === 'on') {
756 $GLOBALS['SOBE']->MOD_SETTINGS['labels_noprefix'] = 'on';
757 }
758 $sqlQuery = $sql_query['qSelect'];
759 $queryIsEmpty = false;
760 if ($sqlQuery) {
761 $res = $this->getDatabaseConnection()->sql_query($sqlQuery);
762 if (!$this->getDatabaseConnection()->sql_error()) {
763 $fullsearch->formW = 48;
764 // Additional configuration
765 $GLOBALS['SOBE']->MOD_SETTINGS['search_result_labels'] = 1;
766 $GLOBALS['SOBE']->MOD_SETTINGS['queryFields'] = $sql_query['qC']['queryFields'];
767 $cP = $fullsearch->getQueryResultCode($type, $res, $sql_query['qC']['queryTable']);
768 $actionContent = $cP['content'];
769 // If the result is rendered as csv or xml, show a download link
770 if ($type === 'csv' || $type === 'xml') {
771 $actionContent .= '<a href="' . GeneralUtility::getIndpEnv('REQUEST_URI') . '&download_file=1"><strong>' . $this->getLanguageService()->getLL('action_download_file') . '</strong></a>';
772 }
773 } else {
774 $actionContent .= $this->getDatabaseConnection()->sql_error();
775 }
776 } else {
777 // Query is empty (not built)
778 $queryIsEmpty = true;
779 $this->addMessage(
780 $this->getLanguageService()->getLL('action_emptyQuery'),
781 $this->getLanguageService()->getLL('action_error'),
782 FlashMessage::ERROR
783 );
784 $content .= $this->renderFlashMessages();
785 }
786 // Admin users are allowed to see and edit the query
787 if ($this->getBackendUser()->isAdmin()) {
788 if (!$queryIsEmpty) {
789 $actionContent .= '<div class="panel panel-default"><div class="panel-body">' . $fullsearch->tableWrap($sql_query['qSelect']) . '</div></div>';
790 }
791 $actionContent .= '<a title="' . $this->getLanguageService()->getLL('action_editQuery') . '" class="btn btn-default" href="'
792 . htmlspecialchars(BackendUtility::getModuleUrl('system_dbint')
793 . '&id=' . '&SET[function]=search' . '&SET[search]=query'
794 . '&storeControl[STORE]=-' . $record['uid'] . '&storeControl[LOAD]=1')
795 . '">'
796 . $this->iconFactory->getIcon('actions-document-info', Icon::SIZE_SMALL)->render() . ' '
797 . $this->getLanguageService()->getLL(($queryIsEmpty ? 'action_createQuery'
798 : 'action_editQuery')) . '</a>';
799 }
800 $content .= '<h2>' . $this->getLanguageService()->getLL('action_t2_result', true) . '</h2>' . $actionContent;
801 } else {
802 // Query is not configured
803 $this->addMessage(
804 $this->getLanguageService()->getLL('action_notReady'),
805 $this->getLanguageService()->getLL('action_error'),
806 FlashMessage::ERROR
807 );
808 $content .= $this->renderFlashMessages();
809 }
810 } else {
811 // Required sysext lowlevel is not installed
812 $this->addMessage(
813 $this->getLanguageService()->getLL('action_lowlevelMissing'),
814 $this->getLanguageService()->getLL('action_error'),
815 FlashMessage::ERROR
816 );
817 $content .= $this->renderFlashMessages();
818 }
819 return $content;
820 }
821
822 /**
823 * Action to create a list of records of a specific table and pid
824 *
825 * @param array $record sys_action record
826 * @return string list of records
827 */
828 protected function viewRecordList($record)
829 {
830 $content = '';
831 $this->id = (int)$record['t3_listPid'];
832 $this->table = $record['t3_tables'];
833 if ($this->id == 0) {
834 $this->addMessage(
835 $this->getLanguageService()->getLL('action_notReady'),
836 $this->getLanguageService()->getLL('action_error'),
837 FlashMessage::ERROR
838 );
839 $content .= $this->renderFlashMessages();
840 return $content;
841 }
842 // Loading current page record and checking access:
843 $this->pageinfo = BackendUtility::readPageAccess($this->id, $this->taskObject->perms_clause);
844 $access = is_array($this->pageinfo) ? 1 : 0;
845 // If there is access to the page, then render the list contents and set up the document template object:
846 if ($access) {
847 // Initialize the dblist object:
848 $dblist = GeneralUtility::makeInstance(\TYPO3\CMS\SysAction\ActionList::class);
849 $dblist->script = GeneralUtility::getIndpEnv('REQUEST_URI');
850 $dblist->calcPerms = $this->getBackendUser()->calcPerms($this->pageinfo);
851 $dblist->thumbs = $this->getBackendUser()->uc['thumbnailsByDefault'];
852 $dblist->returnUrl = $this->taskObject->returnUrl;
853 $dblist->allFields = 1;
854 $dblist->localizationView = 1;
855 $dblist->showClipboard = 0;
856 $dblist->disableSingleTableView = 1;
857 $dblist->pageRow = $this->pageinfo;
858 $dblist->counter++;
859 $dblist->MOD_MENU = array('bigControlPanel' => '', 'clipBoard' => '', 'localization' => '');
860 $dblist->modTSconfig = $this->taskObject->modTSconfig;
861 $dblist->dontShowClipControlPanels = (!$this->taskObject->MOD_SETTINGS['bigControlPanel'] && $dblist->clipObj->current == 'normal' && !$this->modTSconfig['properties']['showClipControlPanelsDespiteOfCMlayers']);
862 // Initialize the listing object, dblist, for rendering the list:
863 $this->pointer = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange(GeneralUtility::_GP('pointer'), 0, 100000);
864 $dblist->start($this->id, $this->table, $this->pointer, $this->taskObject->search_field, $this->taskObject->search_levels, $this->taskObject->showLimit);
865 $dblist->setDispFields();
866 // Render the list of tables:
867 $dblist->generateList();
868 // Add JavaScript functions to the page:
869 $this->taskObject->getModuleTemplate()->addJavaScriptCode(
870 'ActionTaskInlineJavascript',
871 '
872
873 function jumpExt(URL,anchor) {
874 var anc = anchor?anchor:"";
875 window.location.href = URL+(T3_THIS_LOCATION?"&returnUrl="+T3_THIS_LOCATION:"")+anc;
876 return false;
877 }
878 function jumpSelf(URL) {
879 window.location.href = URL+(T3_RETURN_URL?"&returnUrl="+T3_RETURN_URL:"");
880 return false;
881 }
882
883 function setHighlight(id) {
884 top.fsMod.recentIds["web"]=id;
885 top.fsMod.navFrameHighlightedID["web"]="pages"+id+"_"+top.fsMod.currentBank; // For highlighting
886
887 if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav) {
888 top.content.nav_frame.refresh_nav();
889 }
890 }
891
892 ' . $dblist->CBfunctions() . '
893 function editRecords(table,idList,addParams,CBflag) {
894 window.location.href="' . BackendUtility::getModuleUrl('record_edit', array('returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI'))) . '&edit["+table+"]["+idList+"]=edit"+addParams;
895 }
896 function editList(table,idList) {
897 var list="";
898
899 // Checking how many is checked, how many is not
900 var pointer=0;
901 var pos = idList.indexOf(",");
902 while (pos!=-1) {
903 if (cbValue(table+"|"+idList.substr(pointer,pos-pointer))) {
904 list+=idList.substr(pointer,pos-pointer)+",";
905 }
906 pointer=pos+1;
907 pos = idList.indexOf(",",pointer);
908 }
909 if (cbValue(table+"|"+idList.substr(pointer))) {
910 list+=idList.substr(pointer)+",";
911 }
912
913 return list ? list : idList;
914 }
915 T3_THIS_LOCATION = ' . GeneralUtility::quoteJSvalue(rawurlencode(GeneralUtility::getIndpEnv('REQUEST_URI'))) . ';
916
917 if (top.fsMod) top.fsMod.recentIds["web"] = ' . (int)$this->id . ';
918 '
919 );
920 // Setting up the context sensitive menu:
921 $this->taskObject->getModuleTemplate()->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu');
922 // Begin to compile the whole page
923 $content .= '<form action="' . htmlspecialchars($dblist->listURL()) . '" method="post" name="dblistForm">' . $dblist->HTMLcode . '<input type="hidden" name="cmd_table" /><input type="hidden" name="cmd" />
924 </form>';
925 // If a listing was produced, create the page footer with search form etc:
926 // Making field select box (when extended view for a single table is enabled):
927 if ($dblist->HTMLcode && $dblist->table) {
928 $content .= $dblist->fieldSelectBox($dblist->table);
929 }
930 } else {
931 // Not enough rights to access the list view or the page
932 $this->addMessage(
933 $this->getLanguageService()->getLL('action_error-access'),
934 $this->getLanguageService()->getLL('action_error'),
935 FlashMessage::ERROR
936 );
937 $content .= $this->renderFlashMessages();
938 }
939 return $content;
940 }
941
942 /**
943 * @param string $message
944 * @param string $title
945 * @param int $severity
946 *
947 * @throws \TYPO3\CMS\Core\Exception
948 */
949 protected function addMessage($message, $title = '', $severity = FlashMessage::OK)
950 {
951 $flashMessage = GeneralUtility::makeInstance(FlashMessage::class, $message, $title, $severity);
952 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
953 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
954 $defaultFlashMessageQueue->enqueue($flashMessage);
955 }
956
957 /**
958 * Render all currently enqueued FlashMessages
959 *
960 * @return string
961 */
962 protected function renderFlashMessages()
963 {
964 $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
965 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
966 return $defaultFlashMessageQueue->renderFlashMessages();
967 }
968
969 /**
970 * Returns LanguageService
971 *
972 * @return \TYPO3\CMS\Lang\LanguageService
973 */
974 protected function getLanguageService()
975 {
976 return $GLOBALS['LANG'];
977 }
978
979 /**
980 * Returns the current BE user.
981 *
982 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
983 */
984 protected function getBackendUser()
985 {
986 return $GLOBALS['BE_USER'];
987 }
988
989 /**
990 * Returns the database connection
991 *
992 * @return \TYPO3\CMS\Core\Database\DatabaseConnection
993 */
994 protected function getDatabaseConnection()
995 {
996 return $GLOBALS['TYPO3_DB'];
997 }
998 }