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