TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,771 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use TYPO3\CMS\Backend\Authentication\PasswordReset;
|
||||
use TYPO3\CMS\Backend\Module\ModuleData;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder as BackendUriBuilder;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentFactory;
|
||||
use TYPO3\CMS\Backend\Template\Enum\ModuleLayout;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
|
||||
use TYPO3\CMS\Beuser\Domain\Dto\BackendUserGroup;
|
||||
use TYPO3\CMS\Beuser\Domain\Model\BackendUser;
|
||||
use TYPO3\CMS\Beuser\Domain\Model\Demand;
|
||||
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserGroupRepository;
|
||||
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserRepository;
|
||||
use TYPO3\CMS\Beuser\Domain\Repository\BackendUserSessionRepository;
|
||||
use TYPO3\CMS\Beuser\Domain\Repository\FileMountRepository;
|
||||
use TYPO3\CMS\Beuser\Event\AfterBackendGroupFilterListIsAssembledEvent;
|
||||
use TYPO3\CMS\Beuser\Event\AfterFilemountsListIsAssembledEvent;
|
||||
use TYPO3\CMS\Beuser\Service\UserInformationService;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Context\Context;
|
||||
use TYPO3\CMS\Core\Http\AllowedMethodsTrait;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Pagination\ArrayPaginator;
|
||||
use TYPO3\CMS\Core\Pagination\SimplePagination;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Http\ForwardResponse;
|
||||
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
|
||||
use TYPO3\CMS\Extbase\Mvc\Request;
|
||||
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
|
||||
use TYPO3\CMS\Extbase\Pagination\QueryResultPaginator;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
/**
|
||||
* Backend module user and user group administration controller.
|
||||
*
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class BackendUserController extends ActionController
|
||||
{
|
||||
use AllowedMethodsTrait;
|
||||
|
||||
protected ?ModuleData $moduleData = null;
|
||||
protected ModuleTemplate $moduleTemplate;
|
||||
|
||||
public function __construct(
|
||||
protected readonly ComponentFactory $componentFactory,
|
||||
protected readonly BackendUserRepository $backendUserRepository,
|
||||
protected readonly BackendUserGroupRepository $backendUserGroupRepository,
|
||||
protected readonly BackendUserSessionRepository $backendUserSessionRepository,
|
||||
protected readonly UserInformationService $userInformationService,
|
||||
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
|
||||
protected readonly BackendUriBuilder $backendUriBuilder,
|
||||
protected readonly IconFactory $iconFactory,
|
||||
protected readonly PageRenderer $pageRenderer,
|
||||
protected readonly FileMountRepository $fileMountRepository
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Store a valid selected action as defaultAction
|
||||
*/
|
||||
public function processRequest(RequestInterface $request): ResponseInterface
|
||||
{
|
||||
/** @var Request $request */
|
||||
$arguments = $request->getArguments();
|
||||
$moduleData = $request->getAttribute('moduleData');
|
||||
|
||||
if (in_array((string)($arguments['action'] ?? ''), ['list', 'groups', 'online', 'filemounts'], true)
|
||||
&& (string)$moduleData->get('defaultAction') !== (string)$arguments['action']
|
||||
) {
|
||||
$moduleData->set('defaultAction', (string)$arguments['action']);
|
||||
$this->getBackendUser()->pushModuleData($moduleData->getModuleIdentifier(), $moduleData->toArray());
|
||||
}
|
||||
|
||||
return parent::processRequest($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Init module state.
|
||||
* This isn't done within __construct() since the controller
|
||||
* object is only created once in extbase when multiple actions are called in
|
||||
* one call. When those change module state, the second action would see old state.
|
||||
*/
|
||||
public function initializeAction(): void
|
||||
{
|
||||
$this->moduleData = $this->request->getAttribute('moduleData');
|
||||
$this->moduleTemplate = $this->moduleTemplateFactory->create($this->request);
|
||||
$this->moduleTemplate->setTitle(LocalizationUtility::translate('beuser.modules.user_management:title'));
|
||||
$this->moduleTemplate->setLayout(ModuleLayout::NORMAL);
|
||||
$this->moduleTemplate->setFlashMessageQueue($this->getFlashMessageQueue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign default variables to ModuleTemplate view
|
||||
*/
|
||||
protected function initializeView(): void
|
||||
{
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'dateFormat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'],
|
||||
'timeFormat' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
|
||||
]);
|
||||
// Load JavaScript modules
|
||||
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/context-menu.js');
|
||||
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/modal.js');
|
||||
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/element/status-indicator-element.js');
|
||||
$this->pageRenderer->loadJavaScriptModule('@typo3/beuser/backend-user-listing.js');
|
||||
}
|
||||
|
||||
/**
|
||||
* Default action, forwarding the request to either a defined default action or the default entry point "list"
|
||||
*/
|
||||
public function indexAction(): ResponseInterface
|
||||
{
|
||||
$moduleData = $this->request->getAttribute('moduleData');
|
||||
|
||||
if ($moduleData->has('defaultAction')
|
||||
&& in_array((string)$moduleData->get('defaultAction'), ['list', 'groups', 'online', 'filemounts'], true)
|
||||
) {
|
||||
return new ForwardResponse((string)$moduleData->get('defaultAction'));
|
||||
}
|
||||
|
||||
return new ForwardResponse('list');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays all BackendUsers
|
||||
*/
|
||||
public function listAction(?Demand $demand = null, int $currentPage = 1, string $operation = ''): ResponseInterface
|
||||
{
|
||||
$backendUser = $this->getBackendUser();
|
||||
|
||||
if ($operation === 'reset-filters') {
|
||||
// Reset the module data demand object
|
||||
$this->moduleData->set('demand', []);
|
||||
$demand = null;
|
||||
}
|
||||
if ($demand === null) {
|
||||
$demand = Demand::fromUc((array)$this->moduleData->get('demand', []));
|
||||
} else {
|
||||
$this->moduleData->set('demand', $demand->forUc());
|
||||
}
|
||||
$backendUser->pushModuleData($this->moduleData->getModuleIdentifier(), $this->moduleData->toArray());
|
||||
|
||||
$compareUserList = array_keys((array)$this->moduleData->get('compareUserList', []));
|
||||
|
||||
$backendUsers = $this->backendUserRepository->findDemanded($demand);
|
||||
$paginator = new QueryResultPaginator($backendUsers, $currentPage, 50);
|
||||
$pagination = new SimplePagination($paginator);
|
||||
$backendUserGroups = $this->eventDispatcher->dispatch(
|
||||
new AfterBackendGroupFilterListIsAssembledEvent(
|
||||
$this->request,
|
||||
array_merge([''], $this->backendUserGroupRepository->findAll()->toArray())
|
||||
)
|
||||
)->backendGroups;
|
||||
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'onlineBackendUsers' => $this->getOnlineBackendUsers(),
|
||||
'demand' => $demand,
|
||||
'paginator' => $paginator,
|
||||
'pagination' => $pagination,
|
||||
'totalAmountOfBackendUsers' => $backendUsers->count(),
|
||||
'backendUserGroups' => $backendUserGroups,
|
||||
'compareUserUidList' => array_combine($compareUserList, $compareUserList),
|
||||
'currentUserUid' => $backendUser->user['uid'] ?? null,
|
||||
'compareUserList' => !empty($compareUserList) ? $this->backendUserRepository->findByUidList($compareUserList) : '',
|
||||
]);
|
||||
|
||||
$this->addMainMenu('list');
|
||||
$createEditorButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL))
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:btn.editor.create', 'beuser'))
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_users' => [0 => 'new']],
|
||||
'module' => 'backend_user_management',
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($createEditorButton);
|
||||
$createAdminButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL))
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:btn.admin.create', 'beuser'))
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_users' => [0 => 'new']],
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
'defVals' => ['be_users' => ['admin' => 1]],
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($createAdminButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUsers', 'beuser'),
|
||||
['action' => 'list']
|
||||
);
|
||||
|
||||
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/switch-user.js');
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUser/List');
|
||||
}
|
||||
|
||||
/**
|
||||
* Views all currently logged in BackendUsers and their sessions
|
||||
*/
|
||||
public function onlineAction(): ResponseInterface
|
||||
{
|
||||
$onlineUsersAndSessions = [];
|
||||
$onlineUsers = $this->backendUserRepository->findOnline();
|
||||
foreach ($onlineUsers as $onlineUser) {
|
||||
$onlineUsersAndSessions[] = [
|
||||
'backendUser' => $onlineUser,
|
||||
'sessions' => $this->backendUserSessionRepository->findByBackendUser($onlineUser),
|
||||
];
|
||||
}
|
||||
|
||||
$currentSessionId = $this->backendUserSessionRepository->getPersistedSessionIdentifier($this->getBackendUser());
|
||||
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'onlineUsersAndSessions' => $onlineUsersAndSessions,
|
||||
'currentSessionId' => $currentSessionId,
|
||||
]);
|
||||
|
||||
$this->addMainMenu('online');
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:onlineUsers', 'beuser'),
|
||||
['action' => 'online']
|
||||
);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUser/Online');
|
||||
}
|
||||
|
||||
public function showAction(int $uid = 0): ResponseInterface
|
||||
{
|
||||
$data = $this->userInformationService->getUserInformation($uid);
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'data' => $data,
|
||||
'showUid' => $this->getBackendUser()->shallDisplayDebugInformation(),
|
||||
]);
|
||||
|
||||
$this->addMainMenu('show');
|
||||
$this->moduleTemplate->addButtonToButtonBar($this->componentFactory->createBackButton((string)$this->backendUriBuilder->buildUriFromRoute('backend_user_management')));
|
||||
$editButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-open', IconSize::SMALL))
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.edit'))
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_users' => [$uid => 'edit']],
|
||||
'module' => 'backend_user_management',
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($editButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
$addUserButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL))
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:btn.backendUser.create', 'beuser'))
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_users' => [0 => 'new']],
|
||||
'module' => 'backend_user_management',
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($addUserButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
|
||||
$username = empty($data['user']['username']) ? '' : ': ' . $data['user']['username'];
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUser', 'beuser') . $username,
|
||||
['action' => 'show', 'uid' => $uid]
|
||||
);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUser/Show');
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare backend users from demand
|
||||
*/
|
||||
public function compareAction(): ResponseInterface
|
||||
{
|
||||
$compareUserList = array_keys((array)$this->moduleData->get('compareUserList', []));
|
||||
if (empty($compareUserList)) {
|
||||
return $this->redirect('list');
|
||||
}
|
||||
|
||||
$compareData = [];
|
||||
foreach ($compareUserList as $uid) {
|
||||
if ($compareInformation = $this->userInformationService->getUserInformation($uid)) {
|
||||
$compareData[] = $compareInformation;
|
||||
}
|
||||
}
|
||||
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'compareUserList' => $compareData,
|
||||
'onlineBackendUsers' => $this->getOnlineBackendUsers(),
|
||||
'showUid' => $this->getBackendUser()->shallDisplayDebugInformation(),
|
||||
]);
|
||||
|
||||
$this->addMainMenu('compare');
|
||||
$this->moduleTemplate->addButtonToButtonBar($this->componentFactory->createBackButton((string)$this->backendUriBuilder->buildUriFromRoute('backend_user_management')));
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:compareBackendUsers', 'beuser'),
|
||||
['action' => 'compare']
|
||||
);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUser/Compare');
|
||||
}
|
||||
|
||||
protected function initializeInitiatePasswordResetAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the password reset process for a selected user.
|
||||
*/
|
||||
public function initiatePasswordResetAction(int $user): ResponseInterface
|
||||
{
|
||||
$context = GeneralUtility::makeInstance(Context::class);
|
||||
/** @var BackendUser|null $userObject */
|
||||
$userObject = $this->backendUserRepository->findByUid($user);
|
||||
if (!$userObject || !$userObject->isPasswordResetEnabled() || !$context->getAspect('backend.user')->isAdmin()) {
|
||||
// Add an error message
|
||||
$this->addFlashMessage(
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.error.text', 'beuser') ?? '',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.error.title', 'beuser') ?? '',
|
||||
ContextualFeedbackSeverity::ERROR
|
||||
);
|
||||
} else {
|
||||
GeneralUtility::makeInstance(PasswordReset::class)->initiateReset(
|
||||
$this->request,
|
||||
$context,
|
||||
$userObject->getEmail()
|
||||
);
|
||||
$this->addFlashMessage(
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.success.text', 'beuser', [$userObject->getEmail()]) ?? '',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:flashMessage.resetPassword.success.title', 'beuser') ?? ''
|
||||
);
|
||||
}
|
||||
return $this->redirect('list');
|
||||
}
|
||||
|
||||
protected function initializeAddToCompareListAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches one backend user to the compare list
|
||||
*/
|
||||
public function addToCompareListAction(int $uid): ResponseInterface
|
||||
{
|
||||
$this->addToCompareList('compareUserList', $uid);
|
||||
return $this->redirect('list');
|
||||
}
|
||||
|
||||
protected function initializeRemoveFromCompareListAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes given backend user to the compare list
|
||||
*/
|
||||
public function removeFromCompareListAction(int $uid, int $redirectToCompare = 0): ResponseInterface
|
||||
{
|
||||
$this->removeFromCompareList('compareUserList', $uid);
|
||||
if ($redirectToCompare) {
|
||||
return $this->redirect('compare');
|
||||
}
|
||||
return $this->redirect('list');
|
||||
}
|
||||
|
||||
protected function initializeRemoveAllFromCompareListAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all backend users from the compare list
|
||||
*/
|
||||
public function removeAllFromCompareListAction(): ResponseInterface
|
||||
{
|
||||
$this->cleanCompareList('compareUserList');
|
||||
return $this->redirect('list');
|
||||
}
|
||||
|
||||
protected function initializeTerminateBackendUserSessionAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate BackendUser session and logout corresponding client
|
||||
* Redirects to onlineAction with message
|
||||
*/
|
||||
protected function terminateBackendUserSessionAction(string $sessionId): ResponseInterface
|
||||
{
|
||||
// terminating value of persisted session ID
|
||||
$success = $this->backendUserSessionRepository->terminateSessionByIdentifier($sessionId);
|
||||
if ($success) {
|
||||
$this->addFlashMessage(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUser.online.flashMessage.terminateSessionSuccess', 'beuser') ?? '');
|
||||
}
|
||||
return $this->redirect('online');
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays all BackendUserGroups
|
||||
*/
|
||||
public function groupsAction(?BackendUserGroup $userGroupDto = null, int $currentPage = 1, string $operation = ''): ResponseInterface
|
||||
{
|
||||
$backendUser = $this->getBackendUser();
|
||||
|
||||
if ($operation === 'reset-filters') {
|
||||
$this->moduleData->set('userGroupDto', []);
|
||||
$userGroupDto = null;
|
||||
}
|
||||
|
||||
if ($userGroupDto === null) {
|
||||
$userGroupDto = BackendUserGroup::fromUc((array)$this->moduleData->get('userGroupDto', []));
|
||||
} else {
|
||||
$this->moduleData->set('userGroupDto', $userGroupDto->forUc());
|
||||
}
|
||||
|
||||
$backendUser->pushModuleData($this->moduleData->getModuleIdentifier(), $this->moduleData->toArray());
|
||||
|
||||
$backendUserGroups = $this->backendUserGroupRepository->findByFilter($userGroupDto);
|
||||
$paginator = new QueryResultPaginator($backendUserGroups, $currentPage, 50);
|
||||
$pagination = new SimplePagination($paginator);
|
||||
$compareGroupUidList = array_keys((array)$this->moduleData->get('compareGroupUidList', []));
|
||||
$this->moduleTemplate->assignMultiple(
|
||||
[
|
||||
'paginator' => $paginator,
|
||||
'pagination' => $pagination,
|
||||
'totalAmountOfBackendUserGroups' => $backendUserGroups->count(),
|
||||
'compareGroupUidList' => array_map(static function (int $value): int { // uid as key and force value to 1
|
||||
return 1;
|
||||
}, array_flip($compareGroupUidList)),
|
||||
'compareGroupList' => !empty($compareGroupUidList) ? $this->backendUserGroupRepository->findByUidList($compareGroupUidList) : [],
|
||||
'userGroupDto' => $userGroupDto,
|
||||
]
|
||||
);
|
||||
|
||||
$this->addMainMenu('groups');
|
||||
$addGroupButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL))
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:btn.backendUserGroup.create', 'beuser'))
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_groups' => [0 => 'new']],
|
||||
'module' => 'backend_user_management',
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($addGroupButton);
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUserGroupsMenu', 'beuser'),
|
||||
['action' => 'groups']
|
||||
);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUserGroup/List');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a single backend user group.
|
||||
*/
|
||||
public function showGroupAction(int $uid = 0): ResponseInterface
|
||||
{
|
||||
$data = $this->userInformationService->getGroupInformation($uid);
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'data' => $data,
|
||||
'showUid' => $this->getBackendUser()->shallDisplayDebugInformation(),
|
||||
]);
|
||||
|
||||
$this->addMainMenu('showGroup');
|
||||
$this->moduleTemplate->addButtonToButtonBar($this->componentFactory->createBackButton((string)$this->backendUriBuilder->buildUriFromRoute('backend_user_management', ['action' => 'groups'])));
|
||||
$editButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-open', IconSize::SMALL))
|
||||
->setTitle(
|
||||
LocalizationUtility::translate(
|
||||
'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.edit'
|
||||
)
|
||||
)
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_groups' => [$uid => 'edit']],
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($editButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
$addUserButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL))
|
||||
->setTitle(
|
||||
LocalizationUtility::translate(
|
||||
'LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:btn.backendUserGroup.create',
|
||||
'beuser'
|
||||
)
|
||||
)
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['be_groups' => [0 => 'new']],
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($addUserButton, ButtonBar::BUTTON_POSITION_LEFT, 3);
|
||||
$backendGroupTitle = empty($data['group']['title']) ? '' : ': ' . $data['group']['title'];
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate(
|
||||
'LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUserGroup',
|
||||
'beuser'
|
||||
) . $backendGroupTitle,
|
||||
['action' => 'showGroup', 'uid' => $uid]
|
||||
);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUserGroup/Show');
|
||||
}
|
||||
|
||||
public function compareGroupsAction(): ResponseInterface
|
||||
{
|
||||
$compareGroupUidList = array_keys((array)$this->moduleData->get('compareGroupUidList', []));
|
||||
|
||||
$compareData = [];
|
||||
foreach ($compareGroupUidList as $uid) {
|
||||
if ($compareInformation = $this->userInformationService->getGroupInformation($uid)) {
|
||||
$compareData[] = $compareInformation;
|
||||
}
|
||||
}
|
||||
if (empty($compareData)) {
|
||||
return $this->redirect('groups');
|
||||
}
|
||||
|
||||
$this->moduleTemplate->assignMultiple([
|
||||
'compareGroupList' => $compareData,
|
||||
'showUid' => $this->getBackendUser()->shallDisplayDebugInformation(),
|
||||
]);
|
||||
|
||||
$this->addMainMenu('compareGroups');
|
||||
$this->moduleTemplate->addButtonToButtonBar($this->componentFactory->createBackButton($this->uriBuilder->uriFor('groups')));
|
||||
$this->moduleTemplate->getDocHeaderComponent()->setShortcutContext(
|
||||
'backend_user_management',
|
||||
LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:compareBackendUsersGroups', 'beuser'),
|
||||
['action' => 'compareGroups']
|
||||
);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('BackendUserGroup/Compare');
|
||||
}
|
||||
|
||||
protected function initializeAddGroupToCompareListAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches one backend user group to the compare list
|
||||
*/
|
||||
public function addGroupToCompareListAction(int $uid): ResponseInterface
|
||||
{
|
||||
$this->addToCompareList('compareGroupUidList', $uid);
|
||||
return $this->redirect('groups');
|
||||
}
|
||||
|
||||
protected function initializeRemoveGroupFromCompareListAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes given backend user group to the compare list
|
||||
*/
|
||||
public function removeGroupFromCompareListAction(int $uid, int $redirectToCompare = 0): ResponseInterface
|
||||
{
|
||||
$this->removeFromCompareList('compareGroupUidList', $uid);
|
||||
if ($redirectToCompare) {
|
||||
return $this->redirect('compareGroups');
|
||||
}
|
||||
return $this->redirect('groups');
|
||||
}
|
||||
|
||||
protected function initializeRemoveAllGroupsFromCompareListAction(): void
|
||||
{
|
||||
$this->assertAllowedHttpMethod($this->request, 'POST');
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all backend user groups from the compare list
|
||||
*/
|
||||
public function removeAllGroupsFromCompareListAction(): ResponseInterface
|
||||
{
|
||||
$this->cleanCompareList('compareGroupUidList');
|
||||
return $this->redirect('groups');
|
||||
}
|
||||
|
||||
protected function filemountsAction(int $currentPage = 1): ResponseInterface
|
||||
{
|
||||
$filemounts = $this->eventDispatcher->dispatch(
|
||||
new AfterFilemountsListIsAssembledEvent(
|
||||
$this->request,
|
||||
$this->fileMountRepository->findAll()->toArray()
|
||||
)
|
||||
)->filemounts;
|
||||
|
||||
$this->addMainMenu('filemounts');
|
||||
|
||||
$paginator = new ArrayPaginator($filemounts, $currentPage, 50);
|
||||
$pagination = new SimplePagination($paginator);
|
||||
$this->moduleTemplate->assignMultiple(
|
||||
[
|
||||
'paginator' => $paginator,
|
||||
'pagination' => $pagination,
|
||||
'totalAmountOfFilemounts' => count($filemounts),
|
||||
]
|
||||
);
|
||||
|
||||
$addFilemountButton = $this->componentFactory->createLinkButton()
|
||||
->setIcon($this->iconFactory->getIcon('actions-plus', IconSize::SMALL))
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:filemount.create', 'beuser'))
|
||||
->setShowLabelText(true)
|
||||
->setHref((string)$this->backendUriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit' => ['sys_filemounts' => [0 => 'new']],
|
||||
'module' => 'backend_user_management',
|
||||
'returnUrl' => $this->request->getAttribute('normalizedParams')->getRequestUri(),
|
||||
]));
|
||||
$this->moduleTemplate->addButtonToButtonBar($addFilemountButton);
|
||||
|
||||
return $this->moduleTemplate->renderResponse('Filemount/List');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array with the uids of online users as the keys
|
||||
* [
|
||||
* 1 => true,
|
||||
* 5 => true
|
||||
* ]
|
||||
*/
|
||||
protected function getOnlineBackendUsers(): array
|
||||
{
|
||||
$onlineUsers = $this->backendUserSessionRepository->findAllActive();
|
||||
$onlineBackendUsers = [];
|
||||
foreach ($onlineUsers as $onlineUser) {
|
||||
$onlineBackendUsers[$onlineUser['ses_userid']] = true;
|
||||
}
|
||||
return $onlineBackendUsers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Doc header main drop down
|
||||
*/
|
||||
protected function addMainMenu(string $currentAction): void
|
||||
{
|
||||
$this->uriBuilder->setRequest($this->request);
|
||||
$menu = $this->componentFactory->createMenu();
|
||||
$menu->setIdentifier('BackendUserModuleMenu');
|
||||
$menu->setLabel(
|
||||
LocalizationUtility::translate(
|
||||
'LLL:EXT:backend/Resources/Private/Language/locallang.xlf:modulemenu.label',
|
||||
'backend',
|
||||
)
|
||||
);
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUsers', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('list'))
|
||||
->setActive($currentAction === 'list')
|
||||
);
|
||||
if ($currentAction === 'show') {
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUserDetails', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('show'))
|
||||
->setActive(true)
|
||||
);
|
||||
}
|
||||
if ($currentAction === 'compare') {
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:compareBackendUsers', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('list'))
|
||||
->setActive(true)
|
||||
);
|
||||
}
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUserGroupsMenu', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('groups'))
|
||||
->setActive($currentAction === 'groups')
|
||||
);
|
||||
if ($currentAction === 'showGroup') {
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:backendUserGroupDetails', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('showGroup'))
|
||||
->setActive(true)
|
||||
);
|
||||
}
|
||||
if ($currentAction === 'compareGroups') {
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:compareBackendUsersGroups', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('compareGroups'))
|
||||
->setActive(true)
|
||||
);
|
||||
}
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:onlineUsers', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('online'))
|
||||
->setActive($currentAction === 'online')
|
||||
);
|
||||
$menu->addMenuItem(
|
||||
$this->componentFactory->createMenuItem()
|
||||
->setTitle(LocalizationUtility::translate('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:filemounts', 'beuser'))
|
||||
->setHref($this->uriBuilder->uriFor('filemounts'))
|
||||
->setActive($currentAction === 'filemounts')
|
||||
);
|
||||
$this->moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the given uid to the requested compare list
|
||||
*/
|
||||
protected function addToCompareList(string $listIdentifier, int $uid): void
|
||||
{
|
||||
$compareList = (array)$this->moduleData->get($listIdentifier, []);
|
||||
$compareList[$uid] = true;
|
||||
$this->moduleData->set($listIdentifier, $compareList);
|
||||
$this->getBackendUser()->pushModuleData($this->moduleData->getModuleIdentifier(), $this->moduleData->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given uid from the requested compare list
|
||||
*/
|
||||
protected function removeFromCompareList(string $listIdentifier, int $uid): void
|
||||
{
|
||||
$compareList = (array)$this->moduleData->get($listIdentifier, []);
|
||||
unset($compareList[$uid]);
|
||||
$this->moduleData->set($listIdentifier, $compareList);
|
||||
$this->getBackendUser()->pushModuleData($this->moduleData->getModuleIdentifier(), $this->moduleData->toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items from the requested compare list
|
||||
*/
|
||||
protected function cleanCompareList(string $listIdentifier): void
|
||||
{
|
||||
$this->moduleData->set($listIdentifier, []);
|
||||
$this->getBackendUser()->pushModuleData($this->moduleData->getModuleIdentifier(), $this->moduleData->toArray());
|
||||
}
|
||||
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Controller;
|
||||
|
||||
use Psr\Http\Message\ResponseFactoryInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use TYPO3\CMS\Backend\Attribute\AsController;
|
||||
use TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
|
||||
use TYPO3\CMS\Backend\Template\Components\ComponentFactory;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplate;
|
||||
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
|
||||
use TYPO3\CMS\Backend\Tree\View\PageTreeView;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Backend\View\BackendViewFactory;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\DataHandling\DataHandler;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageService;
|
||||
use TYPO3\CMS\Core\Page\PageRenderer;
|
||||
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Backend module page permissions. This is the "Access" module in main module.
|
||||
* Also includes the ajax endpoint for convenient methods for editing
|
||||
* of page permissions (including page ownership (user and group)).
|
||||
*
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
#[AsController]
|
||||
class PermissionController
|
||||
{
|
||||
private const string SESSION_PREFIX = 'tx_Beuser_';
|
||||
private const array DEPTH_LEVELS = [1, 2, 3, 4, 10];
|
||||
private const int RECURSIVE_LEVELS = 10;
|
||||
|
||||
protected int $id = 0;
|
||||
protected string $returnUrl = '';
|
||||
protected int $depth;
|
||||
protected array $pageInfo = [];
|
||||
|
||||
public function __construct(
|
||||
protected readonly ComponentFactory $componentFactory,
|
||||
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
|
||||
protected readonly PageRenderer $pageRenderer,
|
||||
protected readonly IconFactory $iconFactory,
|
||||
protected readonly UriBuilder $uriBuilder,
|
||||
protected readonly ResponseFactoryInterface $responseFactory,
|
||||
protected readonly BackendViewFactory $backendViewFactory,
|
||||
protected readonly FlashMessageService $flashMessageService,
|
||||
) {}
|
||||
|
||||
public function handleRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$queryParams = $request->getQueryParams();
|
||||
$backendUser = $this->getBackendUser();
|
||||
$lang = $this->getLanguageService();
|
||||
|
||||
// determine depth parameter
|
||||
$this->depth = (int)($parsedBody['depth'] ?? $queryParams['depth'] ?? 0);
|
||||
if (!$this->depth) {
|
||||
$this->depth = (int)$backendUser->getSessionData(self::SESSION_PREFIX . 'depth');
|
||||
} else {
|
||||
$backendUser->setAndSaveSessionData(self::SESSION_PREFIX . 'depth', $this->depth);
|
||||
}
|
||||
|
||||
// determine id parameter
|
||||
$this->id = (int)($parsedBody['id'] ?? $queryParams['id'] ?? 0);
|
||||
$pageRecord = BackendUtility::getRecord('pages', $this->id);
|
||||
// Check if a page with the given id exists, otherwise fall back
|
||||
if ($pageRecord === null) {
|
||||
$this->id = 0;
|
||||
}
|
||||
|
||||
$this->returnUrl = GeneralUtility::sanitizeLocalUrl((string)($parsedBody['returnUrl'] ?? $queryParams['returnUrl'] ?? ''), $request);
|
||||
$this->pageInfo = BackendUtility::readPageAccess($this->id, ' 1=1') ?: [
|
||||
'title' => $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
|
||||
'uid' => 0,
|
||||
'pid' => 0,
|
||||
];
|
||||
|
||||
$action = (string)($parsedBody['action'] ?? $queryParams['action'] ?? 'index');
|
||||
|
||||
if ($action === 'update') {
|
||||
// Update returns a redirect. No further fiddling with view here, return directly.
|
||||
return $this->updateAction($request);
|
||||
}
|
||||
|
||||
$view = $this->moduleTemplateFactory->create($request);
|
||||
if ($backendUser->workspace !== 0) {
|
||||
$this->addFlashMessage(
|
||||
$lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:WorkspaceWarningText'),
|
||||
$lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:WorkspaceWarning'),
|
||||
ContextualFeedbackSeverity::WARNING
|
||||
);
|
||||
}
|
||||
$this->registerDocHeaderButtons($view, $action);
|
||||
$view->setTitle(
|
||||
$this->getLanguageService()->translate('title', 'beuser.modules.permissions'),
|
||||
$this->id !== 0 && !empty($this->pageInfo['title']) ? $this->pageInfo['title'] : ''
|
||||
);
|
||||
$view->getDocHeaderComponent()->setPageBreadcrumb($this->pageInfo);
|
||||
|
||||
if ($action === 'edit') {
|
||||
return $this->editAction($view);
|
||||
}
|
||||
return $this->indexAction($view, $request);
|
||||
}
|
||||
|
||||
public function handleAjaxRequest(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$parsedBody = $request->getParsedBody();
|
||||
$conf = [
|
||||
'page' => $parsedBody['page'] ?? null,
|
||||
'who' => $parsedBody['who'] ?? null,
|
||||
'mode' => $parsedBody['mode'] ?? null,
|
||||
'bits' => (int)($parsedBody['bits'] ?? 0),
|
||||
'permissions' => (int)($parsedBody['permissions'] ?? 0),
|
||||
'action' => $parsedBody['action'] ?? null,
|
||||
'ownerUid' => (int)($parsedBody['ownerUid'] ?? 0),
|
||||
'username' => $parsedBody['username'] ?? null,
|
||||
'groupUid' => (int)($parsedBody['groupUid'] ?? 0),
|
||||
'groupname' => $parsedBody['groupname'] ?? '',
|
||||
'editLockState' => (int)($parsedBody['editLockState'] ?? 0),
|
||||
'new_owner_uid' => (int)($parsedBody['newOwnerUid'] ?? 0),
|
||||
'new_group_uid' => (int)($parsedBody['newGroupUid'] ?? 0),
|
||||
];
|
||||
|
||||
// Basic test for required value
|
||||
if ($conf['page'] <= 0) {
|
||||
return $this->htmlResponse('This script cannot be called directly', 500);
|
||||
}
|
||||
|
||||
// Initialize view and always assign current page id
|
||||
$view = $this->backendViewFactory->create($request);
|
||||
$view->assign('pageId', $conf['page']);
|
||||
|
||||
// Initialize TCE for execution of updates
|
||||
$tce = GeneralUtility::makeInstance(DataHandler::class);
|
||||
// Determine the action to execute
|
||||
switch ($conf['action'] ?? '') {
|
||||
case 'show_change_owner_selector':
|
||||
$template = 'Permission/ChangeOwnerSelector';
|
||||
$users = BackendUtility::getUserNames();
|
||||
$view->assignMultiple([
|
||||
'elementId' => 'o_' . $conf['page'],
|
||||
'ownerUid' => $conf['ownerUid'],
|
||||
'username' => $conf['username'],
|
||||
'users' => $users,
|
||||
'addCurrentUser' => !isset($users[$conf['ownerUid']]),
|
||||
]);
|
||||
break;
|
||||
case 'show_change_group_selector':
|
||||
$template = 'Permission/ChangeGroupSelector';
|
||||
$groups = BackendUtility::getGroupNames();
|
||||
$view->assignMultiple([
|
||||
'elementId' => 'g_' . $conf['page'],
|
||||
'groupUid' => $conf['groupUid'],
|
||||
'groupname' => $conf['groupname'],
|
||||
'groups' => $groups,
|
||||
'addCurrentGroup' => !isset($groups[$conf['groupUid']]),
|
||||
]);
|
||||
break;
|
||||
case 'toggle_edit_lock':
|
||||
// Initialize requested lock state
|
||||
$editLockState = $conf['editLockState'] ? 0 : 1;
|
||||
|
||||
// Execute TCE Update
|
||||
$tce->start([
|
||||
'pages' => [
|
||||
$conf['page'] => [
|
||||
'editlock' => $editLockState,
|
||||
],
|
||||
],
|
||||
], []);
|
||||
$tce->process_datamap();
|
||||
|
||||
// Setup view
|
||||
$template = 'Permission/ToggleEditLock';
|
||||
$view->assignMultiple([
|
||||
'elementId' => 'el_' . $conf['page'],
|
||||
'editLockState' => $editLockState,
|
||||
]);
|
||||
break;
|
||||
case 'change_owner':
|
||||
// Check if new owner uid is given (also accept 0 => [not set])
|
||||
if ($conf['new_owner_uid'] < 0) {
|
||||
return $this->htmlResponse('An error occurred: No page owner uid specified', 500);
|
||||
}
|
||||
|
||||
// Execute TCE Update
|
||||
$tce->start([
|
||||
'pages' => [
|
||||
$conf['page'] => [
|
||||
'perms_userid' => $conf['new_owner_uid'],
|
||||
],
|
||||
],
|
||||
], []);
|
||||
$tce->process_datamap();
|
||||
|
||||
// Setup and render view
|
||||
$template = 'Permission/ChangeOwner';
|
||||
$view->assignMultiple([
|
||||
'userId' => $conf['new_owner_uid'],
|
||||
'username' => BackendUtility::getUserNames(
|
||||
'username',
|
||||
' AND uid = ' . $conf['new_owner_uid']
|
||||
)[$conf['new_owner_uid']]['username'] ?? '',
|
||||
]);
|
||||
break;
|
||||
case 'change_group':
|
||||
// Check if new group uid is given (also accept 0 => [not set])
|
||||
if ($conf['new_group_uid'] < 0) {
|
||||
return $this->htmlResponse('An error occurred: No page group uid specified', 500);
|
||||
}
|
||||
|
||||
// Execute TCE Update
|
||||
$tce->start([
|
||||
'pages' => [
|
||||
$conf['page'] => [
|
||||
'perms_groupid' => $conf['new_group_uid'],
|
||||
],
|
||||
],
|
||||
], []);
|
||||
$tce->process_datamap();
|
||||
|
||||
// Setup and render view
|
||||
$template = 'Permission/ChangeGroup';
|
||||
$view->assignMultiple([
|
||||
'groupId' => $conf['new_group_uid'],
|
||||
'groupname' => BackendUtility::getGroupNames(
|
||||
'title',
|
||||
' AND uid = ' . $conf['new_group_uid']
|
||||
)[$conf['new_group_uid']]['title'] ?? '',
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
// Initialize permissions state
|
||||
if ($conf['mode'] === 'delete') {
|
||||
$conf['permissions'] -= $conf['bits'];
|
||||
} else {
|
||||
$conf['permissions'] += $conf['bits'];
|
||||
}
|
||||
|
||||
// Execute TCE Update
|
||||
$tce->start([
|
||||
'pages' => [
|
||||
$conf['page'] => [
|
||||
'perms_' . $conf['who'] => $conf['permissions'],
|
||||
],
|
||||
],
|
||||
], []);
|
||||
$tce->process_datamap();
|
||||
|
||||
// Setup and render view
|
||||
$template = 'Permission/ChangePermission';
|
||||
$view->assignMultiple([
|
||||
'permission' => $conf['permissions'],
|
||||
'scope' => $conf['who'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $this->htmlResponse($view->render($template));
|
||||
}
|
||||
|
||||
public function indexAction(ModuleTemplate $view, ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$view->assignMultiple([
|
||||
'currentId' => $this->id,
|
||||
'viewTree' => $this->getTree(),
|
||||
'beUsers' => BackendUtility::getUserNames(),
|
||||
'beGroups' => BackendUtility::getGroupNames(),
|
||||
'depth' => $this->depth,
|
||||
'depthBaseUrl' => $this->uriBuilder->buildUriFromRoute('permissions_pages', [
|
||||
'id' => $this->id,
|
||||
'depth' => '${value}',
|
||||
'action' => 'index',
|
||||
]),
|
||||
'returnUrl' => (string)$this->uriBuilder->buildUriFromRoute('permissions_pages', [
|
||||
'id' => $this->id,
|
||||
'depth' => $this->depth,
|
||||
'action' => 'index',
|
||||
]),
|
||||
]);
|
||||
|
||||
return $view->renderResponse('Permission/Index');
|
||||
}
|
||||
|
||||
public function editAction(ModuleTemplate $view): ResponseInterface
|
||||
{
|
||||
$lang = $this->getLanguageService();
|
||||
$selectNone = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:selectNone');
|
||||
$selectUnchanged = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:selectUnchanged');
|
||||
|
||||
// Owner selector
|
||||
$beUserDataArray = [0 => $selectNone];
|
||||
foreach (BackendUtility::getUserNames() as $uid => $row) {
|
||||
$beUserDataArray[$uid] = $row['username'] ?? '';
|
||||
}
|
||||
$beUserDataArray[-1] = $selectUnchanged;
|
||||
|
||||
// Group selector
|
||||
$beGroupDataArray = [0 => $selectNone];
|
||||
foreach (BackendUtility::getGroupNames() as $uid => $row) {
|
||||
$beGroupDataArray[$uid] = $row['title'] ?? '';
|
||||
}
|
||||
$beGroupDataArray[-1] = $selectUnchanged;
|
||||
|
||||
$view->assignMultiple([
|
||||
'id' => $this->id,
|
||||
'depth' => $this->depth,
|
||||
'currentBeUser' => $this->pageInfo['perms_userid'] ?? 0,
|
||||
'beUserData' => $beUserDataArray,
|
||||
'currentBeGroup' => $this->pageInfo['perms_groupid'] ?? 0,
|
||||
'beGroupData' => $beGroupDataArray,
|
||||
'pageInfo' => $this->pageInfo,
|
||||
'returnUrl' => $this->returnUrl,
|
||||
'recursiveSelectOptions' => $this->getRecursiveSelectOptions(),
|
||||
'formAction' => (string)$this->uriBuilder->buildUriFromRoute('permissions_pages', [
|
||||
'action' => 'update',
|
||||
'id' => $this->id,
|
||||
'depth' => $this->depth,
|
||||
'returnUrl' => $this->returnUrl,
|
||||
]),
|
||||
]);
|
||||
|
||||
return $view->renderResponse('Permission/Edit');
|
||||
}
|
||||
|
||||
protected function updateAction(ServerRequestInterface $request): ResponseInterface
|
||||
{
|
||||
$data = (array)($request->getParsedBody()['data'] ?? []);
|
||||
$mirror = (array)($request->getParsedBody()['mirror'] ?? []);
|
||||
|
||||
$dataHandlerInput = [];
|
||||
// Prepare the input data for data handler
|
||||
$dataPages = $data['pages'] ?? null;
|
||||
if (is_array($dataPages) && $dataPages !== []) {
|
||||
foreach ($dataPages as $pageUid => $properties) {
|
||||
// if the owner and group field shouldn't be touched, unset the option
|
||||
if ((int)($properties['perms_userid'] ?? 0) === -1) {
|
||||
unset($properties['perms_userid']);
|
||||
}
|
||||
if ((int)($properties['perms_groupid'] ?? 0) === -1) {
|
||||
unset($properties['perms_groupid']);
|
||||
}
|
||||
$dataHandlerInput[$pageUid] = $properties;
|
||||
if (!empty($mirror['pages'][$pageUid])) {
|
||||
$mirrorPages = GeneralUtility::intExplode(',', (string)$mirror['pages'][$pageUid]);
|
||||
foreach ($mirrorPages as $mirrorPageUid) {
|
||||
$dataHandlerInput[$mirrorPageUid] = $properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
|
||||
$dataHandler->start(
|
||||
[
|
||||
'pages' => $dataHandlerInput,
|
||||
],
|
||||
[]
|
||||
);
|
||||
$dataHandler->process_datamap();
|
||||
|
||||
return $this->responseFactory->createResponse(303)
|
||||
->withHeader('location', $this->returnUrl);
|
||||
}
|
||||
|
||||
protected function registerDocHeaderButtons(ModuleTemplate $view, string $action): void
|
||||
{
|
||||
$lang = $this->getLanguageService();
|
||||
|
||||
if ($action === 'edit') {
|
||||
// CLOSE button:
|
||||
if ($this->returnUrl !== '') {
|
||||
$view->addButtonToButtonBar($this->componentFactory->createCloseButton($this->returnUrl));
|
||||
}
|
||||
|
||||
// SAVE button:
|
||||
$saveButton = $this->componentFactory
|
||||
->createSaveButton('PermissionControllerEdit')
|
||||
->setName('_save')
|
||||
->setTitle($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:rm.saveCloseDoc'));
|
||||
$view->addButtonToButtonBar($saveButton, ButtonBar::BUTTON_POSITION_LEFT, 2);
|
||||
}
|
||||
|
||||
if ($action === 'index' && count($this->getDepthOptions()) > 0) {
|
||||
$viewModeItems = [];
|
||||
$viewModeItems[] = $this->componentFactory->createDropDownHeader()
|
||||
->setLabel($lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:Depth'));
|
||||
foreach ($this->getDepthOptions() as $value => $label) {
|
||||
$viewModeItems[] = $this->componentFactory->createDropDownRadio()
|
||||
->setActive($this->depth === $value)
|
||||
->setLabel($label)
|
||||
->setHref((string)$this->uriBuilder->buildUriFromRoute('permissions_pages', [
|
||||
'id' => $this->id,
|
||||
'depth' => $value,
|
||||
]));
|
||||
}
|
||||
$viewModeButton = $this->componentFactory->createDropDownButton()
|
||||
->setLabel($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.view'))
|
||||
->setIcon($this->iconFactory->getIcon('actions-cog'))
|
||||
->setShowLabelText(true);
|
||||
foreach ($viewModeItems as $viewModeItem) {
|
||||
$viewModeButton->addItem($viewModeItem);
|
||||
}
|
||||
$view->addButtonToButtonBar($viewModeButton, ButtonBar::BUTTON_POSITION_RIGHT, 2);
|
||||
}
|
||||
|
||||
$view->getDocHeaderComponent()->setShortcutContext(
|
||||
'permissions_pages',
|
||||
$this->getShortcutTitle(),
|
||||
['id' => $this->id, 'action' => $action]
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTree(): array
|
||||
{
|
||||
$tree = GeneralUtility::makeInstance(PageTreeView::class);
|
||||
$tree->init();
|
||||
|
||||
// Create the tree from $this->id
|
||||
if ($this->id) {
|
||||
$icon = $this->iconFactory->getIconForRecord('pages', $this->pageInfo, IconSize::SMALL);
|
||||
} else {
|
||||
$icon = $this->iconFactory->getIcon('apps-pagetree-root', IconSize::SMALL);
|
||||
}
|
||||
$iconMarkup = '<span title="' . BackendUtility::getRecordIconAltText($this->pageInfo, 'pages') . '">' . $icon->render() . '</span>';
|
||||
$tree->tree[] = ['row' => $this->pageInfo, 'HTML' => '', 'icon' => $iconMarkup];
|
||||
$tree->getTree($this->id, $this->depth);
|
||||
|
||||
return $tree->tree;
|
||||
}
|
||||
|
||||
protected function getDepthOptions(): array
|
||||
{
|
||||
$depthOptions = [];
|
||||
foreach (self::DEPTH_LEVELS as $depthLevel) {
|
||||
$levelLabel = $depthLevel === 1 ? 'level' : 'levels';
|
||||
$depthOptions[$depthLevel] = $depthLevel . ' ' . $this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:' . $levelLabel);
|
||||
}
|
||||
return $depthOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finding tree and offer setting of values recursively.
|
||||
*/
|
||||
protected function getRecursiveSelectOptions(): array
|
||||
{
|
||||
$lang = $this->getLanguageService();
|
||||
// Initialize tree object:
|
||||
$tree = GeneralUtility::makeInstance(PageTreeView::class);
|
||||
$tree->init();
|
||||
$tree->makeHTML = false;
|
||||
// Make tree:
|
||||
$tree->getTree($this->id, self::RECURSIVE_LEVELS);
|
||||
$options = [
|
||||
'' => $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:selectNone'),
|
||||
];
|
||||
// If there are a hierarchy of page ids, then...
|
||||
if (!empty($tree->orig_ids_hierarchy) && ($this->getBackendUser()->user['uid'] ?? false)) {
|
||||
// Init:
|
||||
$labelRecursive = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:recursive');
|
||||
$labelLevel = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:level');
|
||||
$labelLevels = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:levels');
|
||||
$labelPageAffected = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:page_affected');
|
||||
$labelPagesAffected = $lang->sL('LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:pages_affected');
|
||||
$theIdListArr = [];
|
||||
// Traverse the number of levels we want to allow recursive
|
||||
// setting of permissions for:
|
||||
for ($a = self::RECURSIVE_LEVELS; $a > 0; $a--) {
|
||||
if (is_array($tree->orig_ids_hierarchy[$a] ?? false)) {
|
||||
foreach ($tree->orig_ids_hierarchy[$a] as $theId) {
|
||||
$theIdListArr[] = $theId;
|
||||
}
|
||||
$lKey = self::RECURSIVE_LEVELS - $a + 1;
|
||||
$pagesCount = count($theIdListArr);
|
||||
$options[implode(',', $theIdListArr)] = $labelRecursive . ' ' . $lKey . ' ' . ($lKey === 1 ? $labelLevel : $labelLevels)
|
||||
. ' (' . $pagesCount . ' ' . ($pagesCount === 1 ? $labelPageAffected : $labelPagesAffected) . ')';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a flash message to the default flash message queue
|
||||
*/
|
||||
protected function addFlashMessage(string $message, string $title = '', ContextualFeedbackSeverity $severity = ContextualFeedbackSeverity::INFO): void
|
||||
{
|
||||
$flashMessage = new FlashMessage($message, $title, $severity, true);
|
||||
$defaultFlashMessageQueue = $this->flashMessageService->getMessageQueueByIdentifier();
|
||||
$defaultFlashMessageQueue->enqueue($flashMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the shortcut title for the current page
|
||||
*/
|
||||
protected function getShortcutTitle(): string
|
||||
{
|
||||
return sprintf(
|
||||
'%s: %s [%d]',
|
||||
$this->getLanguageService()->translate('title', 'beuser.modules.permissions'),
|
||||
BackendUtility::getRecordTitle('pages', $this->pageInfo),
|
||||
$this->id
|
||||
);
|
||||
}
|
||||
|
||||
protected function htmlResponse(string $html, int $code = 200): ResponseInterface
|
||||
{
|
||||
$response = $this->responseFactory->createResponse($code)
|
||||
->withHeader('Content-Type', 'text/html; charset=utf-8');
|
||||
$response->getBody()->write($html);
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
protected function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Dto;
|
||||
|
||||
/**
|
||||
* @internal not part of the TYPO3 Core API.
|
||||
*/
|
||||
class BackendUserGroup
|
||||
{
|
||||
public function __construct(protected string $title = '') {}
|
||||
|
||||
public static function fromUc(array $uc): self
|
||||
{
|
||||
$demand = new self();
|
||||
$demand->title = (string)($uc['title'] ?? '');
|
||||
return $demand;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function forUc(): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->title,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Backend\Authentication\PasswordReset;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Attribute as Extbase;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
|
||||
/**
|
||||
* Model for backend user
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class BackendUser extends AbstractEntity
|
||||
{
|
||||
#[Extbase\Validate(validator: 'NotEmpty')]
|
||||
protected string $userName = '';
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<BackendUserGroup>
|
||||
*/
|
||||
protected ObjectStorage $backendUserGroups;
|
||||
|
||||
/**
|
||||
* Comma separated list of uids in multi-select
|
||||
* Might retrieve the labels from TCA/DataMapper
|
||||
*/
|
||||
protected string $allowedLanguages = '';
|
||||
|
||||
protected string $dbMountPoints = '';
|
||||
protected string $description = '';
|
||||
protected string $fileMountPoints = '';
|
||||
protected bool $isAdministrator = false;
|
||||
protected bool $isDisabled = false;
|
||||
protected ?\DateTime $startDateAndTime = null;
|
||||
protected ?\DateTime $endDateAndTime = null;
|
||||
protected string $email = '';
|
||||
protected string $realName = '';
|
||||
protected ?\DateTime $lastLoginDateAndTime = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initializeObject();
|
||||
}
|
||||
|
||||
public function initializeObject(): void
|
||||
{
|
||||
$this->backendUserGroups = new ObjectStorage();
|
||||
}
|
||||
|
||||
public function setAllowedLanguages(string $allowedLanguages): void
|
||||
{
|
||||
$this->allowedLanguages = $allowedLanguages;
|
||||
}
|
||||
|
||||
public function getAllowedLanguages(): string
|
||||
{
|
||||
return $this->allowedLanguages;
|
||||
}
|
||||
|
||||
public function setDbMountPoints(string $dbMountPoints): void
|
||||
{
|
||||
$this->dbMountPoints = $dbMountPoints;
|
||||
}
|
||||
|
||||
public function getDbMountPoints(): string
|
||||
{
|
||||
return $this->dbMountPoints;
|
||||
}
|
||||
|
||||
public function setFileMountPoints(string $fileMountPoints): void
|
||||
{
|
||||
$this->fileMountPoints = $fileMountPoints;
|
||||
}
|
||||
|
||||
public function getFileMountPoints(): string
|
||||
{
|
||||
return $this->fileMountPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is active, not disabled
|
||||
*/
|
||||
public function isActive(): bool
|
||||
{
|
||||
if ($this->getIsDisabled()) {
|
||||
return false;
|
||||
}
|
||||
$now = new \DateTime('now');
|
||||
return (!$this->getStartDateAndTime() && !$this->getEndDateAndTime()) || ($this->getStartDateAndTime() <= $now && (!$this->getEndDateAndTime() || $this->getEndDateAndTime() > $now));
|
||||
}
|
||||
|
||||
public function setBackendUserGroups(ObjectStorage $backendUserGroups): void
|
||||
{
|
||||
$this->backendUserGroups = $backendUserGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ObjectStorage<BackendUserGroup>
|
||||
*/
|
||||
public function getBackendUserGroups(): ObjectStorage
|
||||
{
|
||||
return $this->backendUserGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is currently logged in
|
||||
*/
|
||||
public function isCurrentlyLoggedIn(): bool
|
||||
{
|
||||
return $this->getUid() === (int)($this->getBackendUser()->user['uid'] ?? 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is allowed to trigger a password reset
|
||||
*
|
||||
* Requirements:
|
||||
* 1. The user for which the password reset should be triggered is not the currently logged in user
|
||||
* 2. Password reset is enabled for the user (Email+Password are set)
|
||||
* 3. The currently logged in user is allowed to reset passwords in the backend (Enabled in user TSconfig)
|
||||
*/
|
||||
public function isPasswordResetEnabled(): bool
|
||||
{
|
||||
return !$this->isCurrentlyLoggedIn()
|
||||
&& GeneralUtility::makeInstance(PasswordReset::class)->isEnabledForUser((int)$this->getUid())
|
||||
&& ($this->getBackendUser()->getTSConfig()['options.']['passwordReset'] ?? true);
|
||||
}
|
||||
|
||||
public function getUserName(): string
|
||||
{
|
||||
return $this->userName;
|
||||
}
|
||||
|
||||
public function setUserName(string $userName): void
|
||||
{
|
||||
$this->userName = $userName;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function getIsAdministrator(): bool
|
||||
{
|
||||
return $this->isAdministrator;
|
||||
}
|
||||
|
||||
public function setIsAdministrator(bool $isAdministrator): void
|
||||
{
|
||||
$this->isAdministrator = $isAdministrator;
|
||||
}
|
||||
|
||||
public function getIsDisabled(): bool
|
||||
{
|
||||
return $this->isDisabled;
|
||||
}
|
||||
|
||||
public function setIsDisabled(bool $isDisabled): void
|
||||
{
|
||||
$this->isDisabled = $isDisabled;
|
||||
}
|
||||
|
||||
public function getStartDateAndTime(): ?\DateTime
|
||||
{
|
||||
return $this->startDateAndTime;
|
||||
}
|
||||
|
||||
public function setStartDateAndTime(?\DateTime $dateAndTime = null): void
|
||||
{
|
||||
$this->startDateAndTime = $dateAndTime;
|
||||
}
|
||||
|
||||
public function getEndDateAndTime(): ?\DateTime
|
||||
{
|
||||
return $this->endDateAndTime;
|
||||
}
|
||||
|
||||
public function setEndDateAndTime(?\DateTime $dateAndTime = null): void
|
||||
{
|
||||
$this->endDateAndTime = $dateAndTime;
|
||||
}
|
||||
|
||||
public function getEmail(): string
|
||||
{
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
public function setEmail(string $email): void
|
||||
{
|
||||
$this->email = $email;
|
||||
}
|
||||
|
||||
public function getRealName(): string
|
||||
{
|
||||
return $this->realName;
|
||||
}
|
||||
|
||||
public function setRealName(string $name): void
|
||||
{
|
||||
$this->realName = $name;
|
||||
}
|
||||
|
||||
public function getLastLoginDateAndTime(): ?\DateTime
|
||||
{
|
||||
return $this->lastLoginDateAndTime;
|
||||
}
|
||||
|
||||
public function setLastLoginDateAndTime(?\DateTime $dateAndTime = null): void
|
||||
{
|
||||
$this->lastLoginDateAndTime = $dateAndTime;
|
||||
}
|
||||
|
||||
public function getBackendUser(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Extbase\Attribute as Extbase;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
|
||||
|
||||
/**
|
||||
* Model for backend user group
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class BackendUserGroup extends AbstractEntity
|
||||
{
|
||||
protected string $title = '';
|
||||
protected string $description = '';
|
||||
protected bool $hidden = false;
|
||||
|
||||
/**
|
||||
* @var ObjectStorage<BackendUserGroup>
|
||||
*/
|
||||
#[Extbase\ORM\Lazy]
|
||||
protected ObjectStorage $subGroups;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->initializeObject();
|
||||
}
|
||||
|
||||
public function initializeObject(): void
|
||||
{
|
||||
$this->subGroups = new ObjectStorage();
|
||||
}
|
||||
|
||||
public function setTitle(string $title): void
|
||||
{
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
public function setHidden(bool $hidden): void
|
||||
{
|
||||
$this->hidden = $hidden;
|
||||
}
|
||||
|
||||
public function getHidden(): bool
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
public function setSubGroups(ObjectStorage $subGroups): void
|
||||
{
|
||||
$this->subGroups = $subGroups;
|
||||
}
|
||||
|
||||
public function getSubGroups(): ObjectStorage
|
||||
{
|
||||
return $this->subGroups;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Model;
|
||||
|
||||
/**
|
||||
* Demand filter for listings
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class Demand
|
||||
{
|
||||
public const ALL = 0;
|
||||
|
||||
public const USERTYPE_ADMINONLY = 1;
|
||||
public const USERTYPE_USERONLY = 2;
|
||||
|
||||
public const STATUS_ACTIVE = 1;
|
||||
public const STATUS_INACTIVE = 2;
|
||||
|
||||
public const LOGIN_SOME = 1;
|
||||
public const LOGIN_NONE = 2;
|
||||
public const LOGIN_CURRENT = 3;
|
||||
|
||||
protected string $userName = '';
|
||||
protected int $userType = self::ALL;
|
||||
protected int $status = self::ALL;
|
||||
protected int $logins = 0;
|
||||
protected int $backendUserGroup = 0;
|
||||
|
||||
public static function fromUc(array $uc): self
|
||||
{
|
||||
$demand = new self();
|
||||
$demand->userName = (string)($uc['userName'] ?? '');
|
||||
$demand->userType = (int)($uc['userType'] ?? 0);
|
||||
$demand->status = (int)($uc['status'] ?? 0);
|
||||
$demand->logins = (int)($uc['logins'] ?? 0);
|
||||
$demand->backendUserGroup = (int)($uc['backendUserGroup'] ?? 0);
|
||||
return $demand;
|
||||
}
|
||||
|
||||
public function forUc(): array
|
||||
{
|
||||
return [
|
||||
'userName' => $this->getUserName(),
|
||||
'userType' => $this->getUserType(),
|
||||
'status' => $this->getStatus(),
|
||||
'logins' => $this->getLogins(),
|
||||
'backendUserGroup' => $this->getBackendUserGroup(),
|
||||
];
|
||||
}
|
||||
|
||||
public function setUserName(string $userName): void
|
||||
{
|
||||
$this->userName = $userName;
|
||||
}
|
||||
|
||||
public function getUserName(): string
|
||||
{
|
||||
return $this->userName;
|
||||
}
|
||||
|
||||
public function setUserType(int $userType): void
|
||||
{
|
||||
$this->userType = $userType;
|
||||
}
|
||||
|
||||
public function getUserType(): int
|
||||
{
|
||||
return $this->userType;
|
||||
}
|
||||
|
||||
public function setStatus(int $status): void
|
||||
{
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
public function getStatus(): int
|
||||
{
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
public function setLogins(int $logins): void
|
||||
{
|
||||
$this->logins = $logins;
|
||||
}
|
||||
|
||||
public function getLogins(): int
|
||||
{
|
||||
return $this->logins;
|
||||
}
|
||||
|
||||
public function setBackendUserGroup(int $backendUserGroup): void
|
||||
{
|
||||
$this->backendUserGroup = $backendUserGroup;
|
||||
}
|
||||
|
||||
public function getBackendUserGroup(): int
|
||||
{
|
||||
return $this->backendUserGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Model;
|
||||
|
||||
use TYPO3\CMS\Core\Resource\ResourceStorage;
|
||||
use TYPO3\CMS\Core\Resource\StorageRepository;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Attribute as Extbase;
|
||||
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
|
||||
|
||||
/**
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class FileMount extends AbstractEntity
|
||||
{
|
||||
/**
|
||||
* Title of the file mount.
|
||||
*/
|
||||
#[Extbase\Validate(validator: 'NotEmpty')]
|
||||
protected string $title = '';
|
||||
|
||||
/**
|
||||
* Description of the file mount.
|
||||
*/
|
||||
protected string $description = '';
|
||||
|
||||
/**
|
||||
* Identifier of the file mount
|
||||
*/
|
||||
protected string $identifier = '';
|
||||
|
||||
/**
|
||||
* Status of the file mount
|
||||
*/
|
||||
protected bool $hidden = false;
|
||||
|
||||
/**
|
||||
* Determines whether this file mount should be read only.
|
||||
*/
|
||||
protected bool $readOnly = false;
|
||||
|
||||
/**
|
||||
* Getter for the title of the file mount.
|
||||
*/
|
||||
public function getTitle(): string
|
||||
{
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the title of the file mount.
|
||||
*/
|
||||
public function setTitle(string $value): void
|
||||
{
|
||||
$this->title = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the description of the file mount.
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the description of the file mount.
|
||||
*/
|
||||
public function setDescription(string $description): void
|
||||
{
|
||||
$this->description = $description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the readOnly property of the file mount.
|
||||
*/
|
||||
public function setReadOnly(bool $readOnly): void
|
||||
{
|
||||
$this->readOnly = $readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the readOnly property of the file mount.
|
||||
*/
|
||||
public function isReadOnly(): bool
|
||||
{
|
||||
return $this->readOnly;
|
||||
}
|
||||
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
public function setIdentifier(string $identifier): void
|
||||
{
|
||||
$this->identifier = $identifier;
|
||||
}
|
||||
|
||||
public function isHidden(): bool
|
||||
{
|
||||
return $this->hidden;
|
||||
}
|
||||
|
||||
public function setHidden(bool $hidden): void
|
||||
{
|
||||
$this->hidden = $hidden;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the path segment of the file mount (without the storage id)
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
return explode(':/', $this->identifier)[1] ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo This should be part of the ORM not the model class
|
||||
*/
|
||||
public function getStorage(): ?ResourceStorage
|
||||
{
|
||||
return GeneralUtility::makeInstance(StorageRepository::class)->findByCombinedIdentifier($this->identifier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Dto\BackendUserGroup;
|
||||
use TYPO3\CMS\Beuser\Event\AfterBackendGroupListConstraintsAssembledFromDemandEvent;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* Repository for \TYPO3\CMS\Beuser\Domain\Model\BackendUserGroup
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
* @extends Repository<\TYPO3\CMS\Beuser\Domain\Model\BackendUserGroup>
|
||||
*/
|
||||
class BackendUserGroupRepository extends Repository
|
||||
{
|
||||
public const TABLE_NAME = 'be_groups';
|
||||
|
||||
protected $defaultOrderings = [
|
||||
'title' => QueryInterface::ORDER_ASCENDING,
|
||||
];
|
||||
|
||||
/**
|
||||
* Overwrite createQuery to don't respect enable fields
|
||||
*/
|
||||
public function createQuery(): QueryInterface
|
||||
{
|
||||
$query = parent::createQuery();
|
||||
$query->getQuerySettings()->setIgnoreEnableFields(true);
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get QueryBuilder without restrictions for table be_groups
|
||||
*/
|
||||
public function getQueryBuilder(bool $removeRestrictions = true): QueryBuilder
|
||||
{
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
|
||||
if ($removeRestrictions === true) {
|
||||
$queryBuilder->getRestrictions()->removeAll();
|
||||
}
|
||||
|
||||
return $queryBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds Backend Usergroups on a given list of uids
|
||||
*/
|
||||
public function findByUidList(array $uidList): array
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
// being explicit here, albeit `Typo3DbQueryParser::parseDynamicOperand` uses prepared parameters
|
||||
$uidList = array_map(intval(...), $uidList);
|
||||
$query->matching($query->in('uid', $uidList));
|
||||
return $query->execute(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preforms a query on be_groups, matching the field title with like
|
||||
* @throws InvalidQueryException
|
||||
*/
|
||||
public function findByFilter(BackendUserGroup $backendUserGroupDto): QueryResult
|
||||
{
|
||||
$constraints = [];
|
||||
$query = $this->createQuery();
|
||||
$query->setOrderings(['title' => QueryInterface::ORDER_ASCENDING]);
|
||||
if ($backendUserGroupDto->getTitle() !== '') {
|
||||
$searchConstraints = [];
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(self::TABLE_NAME);
|
||||
$searchConstraints[] = $query->like(
|
||||
'title',
|
||||
'%' . $queryBuilder->escapeLikeWildcards($backendUserGroupDto->getTitle()) . '%'
|
||||
);
|
||||
|
||||
if (MathUtility::canBeInterpretedAsInteger($backendUserGroupDto->getTitle())) {
|
||||
$searchConstraints[] = $query->equals('uid', (int)$backendUserGroupDto->getTitle());
|
||||
}
|
||||
|
||||
if (count($searchConstraints) >= 2) {
|
||||
$constraints[] = $query->logicalOr(...$searchConstraints);
|
||||
} else {
|
||||
$constraints = $searchConstraints;
|
||||
}
|
||||
}
|
||||
$constraints = $this->eventDispatcher->dispatch(
|
||||
new AfterBackendGroupListConstraintsAssembledFromDemandEvent(
|
||||
$backendUserGroupDto,
|
||||
$query,
|
||||
$constraints
|
||||
)
|
||||
)->constraints;
|
||||
$query->matching($query->logicalAnd(...$constraints));
|
||||
/** @var QueryResult $result */
|
||||
$result = $query->execute();
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Model\BackendUser;
|
||||
use TYPO3\CMS\Beuser\Domain\Model\Demand;
|
||||
use TYPO3\CMS\Beuser\Event\AfterBackendUserListConstraintsAssembledFromDemandEvent;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Session\Backend\SessionBackendInterface;
|
||||
use TYPO3\CMS\Core\Session\SessionManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\MathUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* Repository for \TYPO3\CMS\Beuser\Domain\Model\BackendUser
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
* @extends Repository<BackendUser>
|
||||
*/
|
||||
class BackendUserRepository extends Repository
|
||||
{
|
||||
/**
|
||||
* Finds Backend Users on a given list of uids
|
||||
*/
|
||||
public function findByUidList(array $uidList): QueryResult
|
||||
{
|
||||
$query = $this->createQuery();
|
||||
$query->matching($query->in('uid', array_map(intval(...), $uidList)));
|
||||
/** @var QueryResult $result */
|
||||
$result = $query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Backend Users matching to Demand object properties
|
||||
*/
|
||||
public function findDemanded(Demand $demand): QueryResult
|
||||
{
|
||||
$constraints = [];
|
||||
$query = $this->createQuery();
|
||||
$query->setOrderings(['userName' => QueryInterface::ORDER_ASCENDING]);
|
||||
// Username
|
||||
if ($demand->getUserName() !== '') {
|
||||
$searchConstraints = [];
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
|
||||
foreach (['userName', 'realName'] as $field) {
|
||||
$searchConstraints[] = $query->like(
|
||||
$field,
|
||||
'%' . $queryBuilder->escapeLikeWildcards($demand->getUserName()) . '%'
|
||||
);
|
||||
}
|
||||
if (MathUtility::canBeInterpretedAsInteger($demand->getUserName())) {
|
||||
$searchConstraints[] = $query->equals('uid', (int)$demand->getUserName());
|
||||
}
|
||||
if (count($searchConstraints) === 1) {
|
||||
$constraints[] = reset($searchConstraints);
|
||||
} else {
|
||||
$constraints[] = $query->logicalOr(...$searchConstraints);
|
||||
}
|
||||
}
|
||||
|
||||
switch ($demand->getUserType()) {
|
||||
case Demand::USERTYPE_ADMINONLY:
|
||||
// Only display admin users
|
||||
$constraints[] = $query->equals('admin', 1);
|
||||
break;
|
||||
case Demand::USERTYPE_USERONLY:
|
||||
// Only display non-admin users
|
||||
$constraints[] = $query->equals('admin', 0);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($demand->getStatus()) {
|
||||
case Demand::STATUS_ACTIVE:
|
||||
// Only display active users
|
||||
$constraints[] = $query->equals('disable', 0);
|
||||
break;
|
||||
case Demand::STATUS_INACTIVE:
|
||||
// Only display in-active users
|
||||
$constraints[] = $query->equals('disable', 1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch ($demand->getLogins()) {
|
||||
case Demand::LOGIN_NONE:
|
||||
// Not logged in before
|
||||
$constraints[] = $query->equals('lastlogin', 0);
|
||||
break;
|
||||
case Demand::LOGIN_SOME:
|
||||
// At least one login
|
||||
$constraints[] = $query->logicalNot($query->equals('lastlogin', 0));
|
||||
break;
|
||||
|
||||
case Demand::LOGIN_CURRENT:
|
||||
// Currently logged-in users
|
||||
$sessionTimeout = (int)($GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] ?? 28800);
|
||||
$constraints[] = $query->greaterThanOrEqual('lastlogin', time() - $sessionTimeout);
|
||||
}
|
||||
|
||||
// In backend user group
|
||||
if ($demand->getBackendUserGroup()) {
|
||||
$constraints[] = $query->logicalOr(
|
||||
$query->equals('usergroup', $demand->getBackendUserGroup()),
|
||||
$query->like('usergroup', $demand->getBackendUserGroup() . ',%'),
|
||||
$query->like('usergroup', '%,' . $demand->getBackendUserGroup()),
|
||||
$query->like('usergroup', '%,' . $demand->getBackendUserGroup() . ',%'),
|
||||
);
|
||||
}
|
||||
$constraints = $this->eventDispatcher->dispatch(
|
||||
new AfterBackendUserListConstraintsAssembledFromDemandEvent(
|
||||
$demand,
|
||||
$query,
|
||||
$constraints
|
||||
)
|
||||
)->constraints;
|
||||
$query->matching($query->logicalAnd(...$constraints));
|
||||
|
||||
/** @var QueryResult $result */
|
||||
$result = $query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Backend Users currently online
|
||||
*/
|
||||
public function findOnline(): QueryResult
|
||||
{
|
||||
$uids = [];
|
||||
foreach ($this->getSessionBackend()->getAll() as $sessionRecord) {
|
||||
if (isset($sessionRecord['ses_userid']) && !in_array($sessionRecord['ses_userid'], $uids, true)) {
|
||||
$uids[] = $sessionRecord['ses_userid'];
|
||||
}
|
||||
}
|
||||
|
||||
$query = $this->createQuery();
|
||||
$query->matching($query->in('uid', $uids));
|
||||
/** @var QueryResult $result */
|
||||
$result = $query->execute();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overwrite createQuery to don't respect enable fields
|
||||
*/
|
||||
public function createQuery(): QueryInterface
|
||||
{
|
||||
$query = parent::createQuery();
|
||||
$query->getQuerySettings()->setIgnoreEnableFields(true);
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getSessionBackend(): SessionBackendInterface
|
||||
{
|
||||
return GeneralUtility::makeInstance(SessionManager::class)->getSessionBackend('BE');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Model\BackendUser;
|
||||
use TYPO3\CMS\Core\Authentication\AbstractUserAuthentication;
|
||||
use TYPO3\CMS\Core\Session\Backend\HashableSessionBackendInterface;
|
||||
use TYPO3\CMS\Core\Session\Backend\SessionBackendInterface;
|
||||
use TYPO3\CMS\Core\Session\SessionManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class BackendUserSessionRepository
|
||||
{
|
||||
protected SessionBackendInterface $sessionBackend;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->sessionBackend = GeneralUtility::makeInstance(SessionManager::class)->getSessionBackend('BE');
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all active sessions for all backend users
|
||||
*/
|
||||
public function findAllActive(): array
|
||||
{
|
||||
$allSessions = $this->sessionBackend->getAll();
|
||||
|
||||
// Map array to correct keys
|
||||
$allSessions = array_map(
|
||||
static function (array $session): array {
|
||||
return [
|
||||
'id' => $session['ses_id'], // this is the hashed sessionId
|
||||
'ip' => $session['ses_iplock'],
|
||||
'timestamp' => $session['ses_tstamp'],
|
||||
'ses_userid' => $session['ses_userid'],
|
||||
];
|
||||
},
|
||||
$allSessions
|
||||
);
|
||||
|
||||
// Sort by timestamp
|
||||
usort($allSessions, static function ($session1, $session2) {
|
||||
return $session1['timestamp'] <=> $session2['timestamp'];
|
||||
});
|
||||
|
||||
return $allSessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find Sessions for specific BackendUser
|
||||
*/
|
||||
public function findByBackendUser(BackendUser $backendUser): array
|
||||
{
|
||||
$allActive = $this->findAllActive();
|
||||
|
||||
return array_filter(
|
||||
$allActive,
|
||||
static function (array $session) use ($backendUser): bool {
|
||||
return (int)$session['ses_userid'] === $backendUser->getUid();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public function getPersistedSessionIdentifier(AbstractUserAuthentication $userObject): string
|
||||
{
|
||||
$currentSessionId = $userObject->getSession()->getIdentifier();
|
||||
if ($this->sessionBackend instanceof HashableSessionBackendInterface) {
|
||||
$currentSessionId = $this->sessionBackend->hash($currentSessionId);
|
||||
}
|
||||
return $currentSessionId;
|
||||
}
|
||||
|
||||
public function terminateSessionByIdentifier(string $sessionIdentifier): bool
|
||||
{
|
||||
return $this->sessionBackend->remove($sessionIdentifier);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Domain\Repository;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Model\FileMount;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Extbase\Persistence\Generic\Typo3QuerySettings;
|
||||
use TYPO3\CMS\Extbase\Persistence\Repository;
|
||||
|
||||
/**
|
||||
* Repository for \TYPO3\CMS\Extbase\Domain\Model\FileMount.
|
||||
* @extends Repository<FileMount>
|
||||
*/
|
||||
class FileMountRepository extends Repository
|
||||
{
|
||||
public function initializeObject(): void
|
||||
{
|
||||
/** @var Typo3QuerySettings $querySettings */
|
||||
$querySettings = GeneralUtility::makeInstance(Typo3QuerySettings::class);
|
||||
$querySettings->setRespectStoragePage(false);
|
||||
$querySettings->setIgnoreEnableFields(true);
|
||||
$this->setDefaultQuerySettings($querySettings);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Event;
|
||||
|
||||
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
|
||||
|
||||
/**
|
||||
* Listeners are able to manipulating the backend groups list, used for the filter selector in the backend module
|
||||
*/
|
||||
final class AfterBackendGroupFilterListIsAssembledEvent
|
||||
{
|
||||
public function __construct(
|
||||
public readonly RequestInterface $request,
|
||||
public array $backendGroups
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Event;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Dto\BackendUserGroup;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
|
||||
/**
|
||||
* Listeners are able to manipulating the constraint, used to find demanded user groups for the backend module
|
||||
*/
|
||||
final class AfterBackendGroupListConstraintsAssembledFromDemandEvent
|
||||
{
|
||||
public function __construct(
|
||||
public readonly BackendUserGroup $backendUserGroupDto,
|
||||
public readonly QueryInterface $query,
|
||||
public array $constraints
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Event;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Model\Demand;
|
||||
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
|
||||
|
||||
/**
|
||||
* Listeners are able to manipulating the constraint, used to find demanded users for the backend module
|
||||
*/
|
||||
final class AfterBackendUserListConstraintsAssembledFromDemandEvent
|
||||
{
|
||||
public function __construct(
|
||||
public readonly Demand $demand,
|
||||
public readonly QueryInterface $query,
|
||||
public array $constraints
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Event;
|
||||
|
||||
use TYPO3\CMS\Extbase\Mvc\RequestInterface;
|
||||
|
||||
/**
|
||||
* Listeners are able to manipulating the list of file mounts shown in the backend module
|
||||
*/
|
||||
final class AfterFilemountsListIsAssembledEvent
|
||||
{
|
||||
public function __construct(
|
||||
public readonly RequestInterface $request,
|
||||
public array $filemounts,
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser;
|
||||
|
||||
use TYPO3\CMS\Core\Exception as CoreException;
|
||||
|
||||
/**
|
||||
* Generic Beuser exception
|
||||
* @internal
|
||||
*/
|
||||
class Exception extends CoreException {}
|
||||
@@ -0,0 +1,293 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\Service;
|
||||
|
||||
use TYPO3\CMS\Backend\Module\ModuleProvider;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\DataHandling\PageDoktypeRegistry;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Schema\Field\StaticSelectFieldType;
|
||||
use TYPO3\CMS\Core\Schema\SchemaLabelResolver;
|
||||
use TYPO3\CMS\Core\Schema\TcaSchemaFactory;
|
||||
use TYPO3\CMS\Core\Site\SiteFinder;
|
||||
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Transform information of user and groups into better format
|
||||
* @internal
|
||||
*/
|
||||
final readonly class UserInformationService
|
||||
{
|
||||
public function __construct(
|
||||
private IconFactory $iconFactory,
|
||||
private ModuleProvider $moduleProvider,
|
||||
private TcaSchemaFactory $tcaSchemaFactory,
|
||||
private PageDoktypeRegistry $pageDoktypeRegistry,
|
||||
private SchemaLabelResolver $schemaLabelResolver,
|
||||
private SiteFinder $siteFinder,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get all relevant information for a backend usergroup
|
||||
*/
|
||||
public function getGroupInformation(int $groupId): array
|
||||
{
|
||||
$usergroupRecord = BackendUtility::getRecord('be_groups', $groupId);
|
||||
if (!$usergroupRecord) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$user = GeneralUtility::makeInstance(BackendUserAuthentication::class);
|
||||
$user->enablecolumns = [
|
||||
'deleted' => true,
|
||||
];
|
||||
|
||||
// Setup dummy user to allow fetching all group data
|
||||
// @see \TYPO3\CMS\Core\Authentication\BackendUserAuthentication::fetchGroups
|
||||
$user->user = [
|
||||
'uid' => PHP_INT_MAX,
|
||||
'options' => 3,
|
||||
// The below admin flag is required to prevent workspace access checks,
|
||||
// triggered by workspaceInit() in fetchGroupData(). Those would fail
|
||||
// due to insufficient permissions of the dummy user and therefore might
|
||||
// result in generating superfluous log entries.
|
||||
'admin' => 1,
|
||||
'workspace_id' => 0,
|
||||
'realName' => 'fakeUser',
|
||||
'email' => 'fake.user@typo3.org',
|
||||
'TSconfig' => '',
|
||||
'category_perms' => '',
|
||||
$user->usergroup_column => $groupId,
|
||||
];
|
||||
$user->fetchGroupData();
|
||||
|
||||
$data = $this->convert($user);
|
||||
$data['group'] = $usergroupRecord;
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all relevant information of the user
|
||||
*/
|
||||
public function getUserInformation(int $userId): array
|
||||
{
|
||||
$user = GeneralUtility::makeInstance(BackendUserAuthentication::class);
|
||||
$user->enablecolumns = [
|
||||
'deleted' => true,
|
||||
];
|
||||
$user->setBeUserByUid($userId);
|
||||
if (!$user->user) {
|
||||
return [];
|
||||
}
|
||||
$user->fetchGroupData();
|
||||
|
||||
return $this->convert($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert hard readable user & group information into structured
|
||||
* data which can be rendered later
|
||||
*/
|
||||
private function convert(BackendUserAuthentication $user): array
|
||||
{
|
||||
// usergroups
|
||||
$data = [
|
||||
'user' => $user->user ?? [],
|
||||
'groups' => [
|
||||
'inherit' => $user->userGroupsUID,
|
||||
'direct' => GeneralUtility::trimExplode(',', (string)($user->user['usergroup'] ?? ''), true),
|
||||
],
|
||||
'modules' => [],
|
||||
];
|
||||
$data['groups']['diff'] = array_diff($data['groups']['inherit'], $data['groups']['direct']);
|
||||
foreach ($data['groups'] as $type => $groups) {
|
||||
foreach ($groups as $id) {
|
||||
$record = BackendUtility::getRecord('be_groups', (int)$id);
|
||||
if (isset($record['uid'])) {
|
||||
$recordId = $record['uid'];
|
||||
$data['groups']['all'][$recordId]['row'] = $record;
|
||||
$data['groups']['all'][$recordId][$type] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// languages
|
||||
$siteLanguages = $this->getAllSiteLanguages();
|
||||
$userLanguages = GeneralUtility::trimExplode(',', $user->groupData['allowed_languages'] ?? '', true);
|
||||
asort($userLanguages);
|
||||
foreach ($userLanguages as $languageId) {
|
||||
$languageId = (int)$languageId;
|
||||
$record = $siteLanguages[$languageId] ?? null;
|
||||
if ($record) {
|
||||
$data['languages'][$languageId] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
// table permissions
|
||||
$data['tables']['tables_select'] = [];
|
||||
$data['tables']['tables_modify'] = [];
|
||||
foreach (['tables_select', 'tables_modify'] as $tableField) {
|
||||
$temp = GeneralUtility::trimExplode(',', $user->groupData[$tableField] ?? '', true);
|
||||
foreach ($temp as $tableName) {
|
||||
if ($this->tcaSchemaFactory->has($tableName)) {
|
||||
$data['tables'][$tableField][$tableName] = $this->tcaSchemaFactory->get($tableName)->getTitle() ?: $tableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['tables']['all'] = array_replace($data['tables']['tables_select'], $data['tables']['tables_modify']);
|
||||
|
||||
// Page Tree Entry Points / dbMounts
|
||||
$dbMounts = GeneralUtility::trimExplode(',', $user->groupData['webmounts'] ?? '', true);
|
||||
asort($dbMounts);
|
||||
foreach ($dbMounts as $mount) {
|
||||
$record = BackendUtility::getRecord('pages', (int)$mount);
|
||||
if ($record) {
|
||||
$data['dbMounts'][] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
// File mounts
|
||||
$fileMounts = GeneralUtility::trimExplode(',', $user->groupData['filemounts'] ?? '', true);
|
||||
asort($fileMounts);
|
||||
foreach ($fileMounts as $mount) {
|
||||
$record = BackendUtility::getRecord('sys_filemounts', (int)$mount);
|
||||
if ($record) {
|
||||
$data['fileMounts'][] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
// Modules
|
||||
$modules = GeneralUtility::trimExplode(',', $user->groupData['modules'] ?? '', true);
|
||||
foreach ($modules as $moduleIdentifier) {
|
||||
if ($this->moduleProvider->isModuleRegistered($moduleIdentifier)) {
|
||||
$data['modules'][] = $this->moduleProvider->getModule($moduleIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
// Categories
|
||||
$categories = $user->getCategoryMountPoints();
|
||||
foreach ($categories as $category) {
|
||||
$record = BackendUtility::getRecord('sys_category', $category);
|
||||
if ($record) {
|
||||
$data['categories'][$category] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
// workspaces
|
||||
if (ExtensionManagementUtility::isLoaded('workspaces')) {
|
||||
$data['workspaces'] = [
|
||||
'loaded' => true,
|
||||
'record' => $user->workspaceRec,
|
||||
];
|
||||
}
|
||||
|
||||
// file & folder permissions
|
||||
$filePermissions = $user->groupData['file_permissions'] ?? '';
|
||||
if ($filePermissions) {
|
||||
$items = GeneralUtility::trimExplode(',', $filePermissions, true);
|
||||
/** @var StaticSelectFieldType $fieldType */
|
||||
$fieldType = $this->tcaSchemaFactory->get('be_groups')->getField('file_permissions');
|
||||
foreach ($fieldType->getConfiguration()['items'] ?? [] as $availableItem) {
|
||||
if (in_array($availableItem['value'], $items, true)) {
|
||||
$data['fileFolderPermissions'][] = $availableItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tsconfig
|
||||
$data['tsconfig'] = $user->getTSConfig();
|
||||
|
||||
// non_exclude_fields
|
||||
$fieldListTmp = GeneralUtility::trimExplode(',', $user->groupData['non_exclude_fields'] ?? '', true);
|
||||
$fieldList = [];
|
||||
foreach ($fieldListTmp as $item) {
|
||||
$itemParts = explode(':', $item);
|
||||
$itemTable = $itemParts[0];
|
||||
$itemField = $itemParts[1] ?? '';
|
||||
if (!empty($itemField) && $this->tcaSchemaFactory->has($itemTable)) {
|
||||
$schema = $this->tcaSchemaFactory->get($itemTable);
|
||||
$fieldList[$itemTable]['label'] = $schema->getTitle();
|
||||
if ($schema->hasField($itemField)) {
|
||||
$fieldList[$itemTable]['fields'][$itemField] = $schema->getField($itemField)->getLabel();
|
||||
}
|
||||
}
|
||||
}
|
||||
$data['non_exclude_fields'] = $fieldList;
|
||||
|
||||
// page types
|
||||
foreach ($this->pageDoktypeRegistry->getAllDoktypes() as $specialItem) {
|
||||
if (!GeneralUtility::inList($user->groupData['pagetypes_select'] ?? '', $specialItem->getValue())) {
|
||||
continue;
|
||||
}
|
||||
$label = $specialItem->getLabel();
|
||||
$icon = $specialItem->getIcon() ?? 'apps-pagetree-page-default';
|
||||
$data['pageTypes'][] = ['label' => $label, 'value' => $specialItem->getValue(), 'icon' => $icon];
|
||||
}
|
||||
|
||||
// page content types
|
||||
$pageContentTypes = GeneralUtility::trimExplode(',', $user->groupData['explicit_allowdeny'] ?? '', true);
|
||||
foreach ($pageContentTypes as $item) {
|
||||
$split = explode(':', $item);
|
||||
if (count($split) !== 3) {
|
||||
continue;
|
||||
}
|
||||
[$table, $recordType, $recordTypeValue] = $split;
|
||||
$label = $this->schemaLabelResolver->getLabelForFieldValue(...$split);
|
||||
$data['pageContentTypes'][] = [
|
||||
// If label is empty => the record type value does not exist so we use "empty-empty" as icon instead of falling back to the default record type icon
|
||||
'icon' => $label ? $this->iconFactory->getIconForRecord($table, [$recordType => $recordTypeValue], IconSize::SMALL)->getIdentifier() : 'install-check-extables',
|
||||
'label' => $label,
|
||||
'shortType' => $recordTypeValue,
|
||||
'longType' => $item,
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
private function getAllSiteLanguages(): array
|
||||
{
|
||||
$siteLanguages = [];
|
||||
foreach ($this->siteFinder->getAllSites() as $site) {
|
||||
foreach ($site->getAllLanguages() as $languageId => $language) {
|
||||
if (isset($siteLanguages[$languageId])) {
|
||||
// Language already provided by another site, check if values differ
|
||||
if (!str_contains($siteLanguages[$languageId]['title'], $language->getTitle())) {
|
||||
// Language already provided by another site, but with a different title
|
||||
$siteLanguages[$languageId]['title'] .= ', ' . $language->getTitle();
|
||||
}
|
||||
if ($siteLanguages[$languageId]['flagIconIdentifier'] !== $language->getFlagIdentifier()) {
|
||||
// Language already provided by another site, but with a different flag icon identifier
|
||||
$siteLanguages[$languageId]['flagIconIdentifier'] = 'flags-multiple';
|
||||
}
|
||||
} else {
|
||||
$siteLanguages[$languageId] = [
|
||||
'title' => $language->getTitle(),
|
||||
'flagIconIdentifier' => $language->getFlagIdentifier(),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $siteLanguages;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Beuser\Exception;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to get a value from an array by given key.
|
||||
*
|
||||
* ```
|
||||
* <f:render
|
||||
* partial="Permission/Ownername"
|
||||
* arguments="{
|
||||
* username: '{beuser:arrayElement(array:beUsers, key:data.row.perms_userid, subKey:\'username\')}'
|
||||
* }"
|
||||
* />
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ArrayElementViewHelper extends AbstractViewHelper
|
||||
{
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('array', 'array', 'Array to search in', true);
|
||||
$this->registerArgument('key', 'string', 'Key to return its value', true);
|
||||
$this->registerArgument('subKey', 'string', 'If result of key access is an array, subkey can be used to fetch an element from this again', false, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return array element by key. Accessed values must be scalar (string, int, float or double)
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
$array = $this->arguments['array'];
|
||||
$key = $this->arguments['key'];
|
||||
$subKey = $this->arguments['subKey'];
|
||||
$result = '';
|
||||
if (isset($array[$key])) {
|
||||
$result = $array[$key];
|
||||
if (is_array($result) && $subKey && isset($result[$subKey])) {
|
||||
$result = $result[$subKey];
|
||||
}
|
||||
}
|
||||
if (!is_scalar($result)) {
|
||||
throw new Exception('Only scalar return values (string, int, float or double) are supported.', 1382284105);
|
||||
}
|
||||
return (string)$result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\ViewHelpers;
|
||||
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to checks whether the given value is an array.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class IsArrayViewHelper extends AbstractViewHelper
|
||||
{
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('value', 'mixed', 'The variable being checked', true);
|
||||
}
|
||||
|
||||
public function render(): bool
|
||||
{
|
||||
return is_array($this->arguments['value']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to render MFA status information.
|
||||
*
|
||||
* ```
|
||||
* <beuser:mfaStatus userUid="{backendUser.uid}" />
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MfaStatusViewHelper extends AbstractTagBasedViewHelper
|
||||
{
|
||||
protected $tagName = 'span';
|
||||
|
||||
public function __construct(
|
||||
private readonly MfaProviderRegistry $mfaProviderRegistry
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('userUid', 'int', 'The uid of the user to check', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$userUid = (int)($this->arguments['userUid'] ?? 0);
|
||||
if (!$userUid) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class);
|
||||
$backendUser->enablecolumns = ['deleted' => true];
|
||||
$backendUser->setBeUserByUid($userUid);
|
||||
|
||||
// Check if user has active providers
|
||||
if (!$this->mfaProviderRegistry->hasActiveProviders($backendUser)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// Check locked providers
|
||||
if ($this->mfaProviderRegistry->hasLockedProviders($backendUser)) {
|
||||
$this->tag->addAttribute('class', 'badge badge-warning');
|
||||
$this->tag->setContent(htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:lockedMfaProviders')));
|
||||
return $this->tag->render();
|
||||
}
|
||||
|
||||
// Add mfa enabled label since we have active providers and non of them are locked
|
||||
$this->tag->addAttribute('class', 'badge badge-info');
|
||||
$this->tag->setContent(htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:mfaEnabled')));
|
||||
return $this->tag->render();
|
||||
}
|
||||
|
||||
private function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\ViewHelpers;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to render a permission icon group (user / group / others) of the "Access" module,
|
||||
* using native PHP instead of Fluid for performance reasons.
|
||||
*
|
||||
* ```
|
||||
* <beuser:permissions permission="{data.row.perms_user}" scope="user" pageId="{data.row.uid}" />
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class PermissionsViewHelper extends AbstractViewHelper
|
||||
{
|
||||
private const array MASKS = [1, 16, 2, 4, 8];
|
||||
|
||||
/**
|
||||
* As this ViewHelper renders HTML, the output must not be escaped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly IconFactory $iconFactory,
|
||||
#[Autowire(service: 'cache.runtime')]
|
||||
private readonly FrontendInterface $cachePermissionLabels
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
$this->registerArgument('permission', 'int', 'Current permission', true);
|
||||
$this->registerArgument('scope', 'string', '"user" / "group" / "everybody"', true);
|
||||
$this->registerArgument('pageId', 'int', 'Page ID to evaluate permission for', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$icon = '';
|
||||
foreach (self::MASKS as $mask) {
|
||||
if ($this->arguments['permission'] & $mask) {
|
||||
$iconIdentifier = 'actions-check';
|
||||
$iconClass = 'text-success';
|
||||
$mode = 'delete';
|
||||
} else {
|
||||
$iconIdentifier = 'actions-close';
|
||||
$iconClass = 'text-danger';
|
||||
$mode = 'add';
|
||||
}
|
||||
|
||||
$label = $this->resolvePermissionLabel($mask);
|
||||
$icon .= '<button'
|
||||
. ' aria-label="' . htmlspecialchars($label) . ', ' . htmlspecialchars($mode) . ', ' . htmlspecialchars($this->arguments['scope']) . '"'
|
||||
. ' title="' . htmlspecialchars($label) . '"'
|
||||
. ' data-page="' . htmlspecialchars((string)$this->arguments['pageId']) . '"'
|
||||
. ' data-permissions="' . htmlspecialchars((string)$this->arguments['permission']) . '"'
|
||||
. ' data-who="' . htmlspecialchars($this->arguments['scope']) . '"'
|
||||
. ' data-bits="' . htmlspecialchars((string)$mask) . '"'
|
||||
. ' data-mode="' . htmlspecialchars($mode) . '"'
|
||||
. ' class="btn btn-default btn-icon btn-borderless change-permission ' . htmlspecialchars($iconClass) . '">'
|
||||
. $this->iconFactory->getIcon($iconIdentifier, IconSize::SMALL)->render(SvgIconProvider::MARKUP_IDENTIFIER_INLINE)
|
||||
. '</button>';
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
private function resolvePermissionLabel(int $mask): string
|
||||
{
|
||||
$cacheIdentifier = 'beuser-viewhelper-permission_' . $mask;
|
||||
if (!$this->cachePermissionLabels->has($cacheIdentifier)) {
|
||||
$this->cachePermissionLabels->set($cacheIdentifier, htmlspecialchars($this->getLanguageService()->sL(
|
||||
'LLL:EXT:beuser/Resources/Private/Language/locallang_mod_permission.xlf:' . $mask,
|
||||
)));
|
||||
}
|
||||
return $this->cachePermissionLabels->get($cacheIdentifier);
|
||||
}
|
||||
|
||||
private function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to display a sprite icon for a record (object).
|
||||
*
|
||||
* ```
|
||||
* <beuser:spriteIconForRecord table="be_groups" object="{backendUserGroup}" />
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class SpriteIconForRecordViewHelper extends AbstractViewHelper
|
||||
{
|
||||
/**
|
||||
* As this ViewHelper renders HTML, the output must not be escaped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $escapeOutput = false;
|
||||
|
||||
public function __construct(
|
||||
private readonly IconFactory $iconFactory
|
||||
) {}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('table', 'string', '', true);
|
||||
$this->registerArgument('object', 'object', '', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays spriteIcon for database table and object
|
||||
*/
|
||||
public function render(): string
|
||||
{
|
||||
$object = $this->arguments['object'];
|
||||
$table = $this->arguments['table'];
|
||||
|
||||
if (!method_exists($object, 'getUid')) {
|
||||
return '';
|
||||
}
|
||||
$row = [
|
||||
'uid' => $object->getUid(),
|
||||
'startTime' => false,
|
||||
'endTime' => false,
|
||||
];
|
||||
if (method_exists($object, 'getIsDisabled')) {
|
||||
$row['disable'] = $object->getIsDisabled();
|
||||
}
|
||||
if (method_exists($object, 'getHidden')) {
|
||||
$row['hidden'] = $object->getHidden();
|
||||
}
|
||||
if (method_exists($object, 'getStartDateAndTime')) {
|
||||
$row['startTime'] = $object->getStartDateAndTime();
|
||||
}
|
||||
if (method_exists($object, 'getEndDateAndTime')) {
|
||||
$row['endTime'] = $object->getEndDateAndTime();
|
||||
}
|
||||
return $this->iconFactory->getIconForRecord($table, $row, IconSize::SMALL)->render();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Beuser\ViewHelpers;
|
||||
|
||||
use TYPO3\CMS\Beuser\Domain\Model\BackendUser;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Imaging\IconSize;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
|
||||
|
||||
/**
|
||||
* ViewHelper to displays a 'SwitchUser' button to change the current backend user to the target backend user.
|
||||
*
|
||||
* ```
|
||||
* <beuser:SwitchUser class="btn btn-default" backendUser="{backendUser}" />
|
||||
* ```
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class SwitchUserViewHelper extends AbstractTagBasedViewHelper
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $tagName = 'typo3-backend-switch-user';
|
||||
|
||||
public function __construct(
|
||||
private readonly IconFactory $iconFactory
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function initializeArguments(): void
|
||||
{
|
||||
parent::initializeArguments();
|
||||
$this->registerArgument('backendUser', BackendUser::class, 'Target backendUser to switch active session to', true);
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
/** @var BackendUser $targetUser */
|
||||
$targetUser = $this->arguments['backendUser'];
|
||||
$currentUser = self::getBackendUserAuthentication();
|
||||
|
||||
if ((int)$targetUser->getUid() === (int)$currentUser->getUserId()
|
||||
|| !$targetUser->isActive()
|
||||
|| !$currentUser->isAdmin()
|
||||
|| $currentUser->getOriginalUserIdWhenInSwitchUserMode() !== null
|
||||
) {
|
||||
$this->tag->setTagName('span');
|
||||
$this->tag->addAttribute('class', $this->tag->getAttribute('class') . ' disabled');
|
||||
$this->tag->addAttribute('disabled', 'disabled');
|
||||
$this->tag->setContent($this->iconFactory->getIcon('empty-empty', IconSize::SMALL)->render());
|
||||
} else {
|
||||
$this->tag->addAttribute('title', self::getLanguageService()->sL('LLL:EXT:beuser/Resources/Private/Language/locallang.xlf:switchBackMode'));
|
||||
$this->tag->addAttribute('targetUser', (string)$targetUser->getUid());
|
||||
$this->tag->setContent($this->iconFactory->getIcon('actions-system-backend-user-switch', IconSize::SMALL)->render());
|
||||
}
|
||||
|
||||
$this->tag->forceClosingTag(true);
|
||||
return $this->tag->render();
|
||||
}
|
||||
|
||||
private static function getBackendUserAuthentication(): BackendUserAuthentication
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
private static function getLanguageService(): LanguageService
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user