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