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