Files
cms-backend/Classes/Controller/SetupModuleController.php

705 lines
33 KiB
PHP

<?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\Backend\Controller;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Attribute\AsController;
use TYPO3\CMS\Backend\Event\AddUserSettingsJavaScriptModulesEvent;
use TYPO3\CMS\Backend\Form\FormDataCompiler;
use TYPO3\CMS\Backend\Form\FormDataGroup\UserSettingsDataGroup;
use TYPO3\CMS\Backend\Form\FormResultFactory;
use TYPO3\CMS\Backend\Form\FormResultHandler;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Backend\Module\ModuleProvider;
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\Enum\ModuleLayout;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Backend\Template\ModuleTemplateFactory;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Authentication\Mfa\MfaProviderRegistry;
use TYPO3\CMS\Core\Authentication\UserSettingsSchema;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\Information\Typo3Information;
use TYPO3\CMS\Core\Localization\DateFormatter;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
use TYPO3\CMS\Core\Localization\Locales;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\PasswordPolicy\Event\EnrichPasswordValidationContextDataEvent;
use TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyAction;
use TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyValidator;
use TYPO3\CMS\Core\PasswordPolicy\Validator\Dto\ContextData;
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Schema\TcaSchemaBuilder;
use TYPO3\CMS\Core\SysLog\Action\Setting as SystemLogSettingAction;
use TYPO3\CMS\Core\SysLog\Error as SystemLogErrorClassification;
use TYPO3\CMS\Core\SysLog\Type as SystemLogType;
use TYPO3\CMS\Core\Type\ContextualFeedbackSeverity;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Script class for the User Settings module
*
* @internal This is a specific Backend Controller implementation and is not considered part of the Public TYPO3 API.
*/
#[AsController]
class SetupModuleController
{
protected const DISALLOWED_FIELD_NAMES = ['password', 'password2', 'email', 'realName', 'admin', 'avatar'];
protected const PASSWORD_NOT_UPDATED = 0;
protected const PASSWORD_UPDATED = 1;
protected const PASSWORD_NOT_THE_SAME = 2;
// @todo: Can this constant be removed?
protected const PASSWORD_OLD_WRONG = 3;
protected const PASSWORD_POLICY_FAILED = 4;
protected array $overrideConf = [];
protected bool $languageUpdate = false;
protected array $persistentUpdate = [];
protected bool $pagetreeNeedsRefresh = false;
protected bool $colorSchemeChanged = false;
protected bool $themeChanged = false;
protected bool $backendTitleFormatChanged = false;
protected bool $dateTimeFirstDayOfWeekChanged = false;
protected array $tsFieldConf = [];
protected int $passwordIsUpdated = self::PASSWORD_NOT_UPDATED;
protected bool $passwordIsSubmitted = false;
protected bool $setupIsUpdated = false;
protected bool $settingsAreResetToDefault = false;
protected PasswordPolicyValidator $passwordPolicyValidator;
public function __construct(
protected readonly Typo3Information $typo3Information,
protected readonly EventDispatcherInterface $eventDispatcher,
protected readonly MfaProviderRegistry $mfaProviderRegistry,
protected readonly IconFactory $iconFactory,
protected readonly PageRenderer $pageRenderer,
protected readonly ModuleTemplateFactory $moduleTemplateFactory,
protected readonly LanguageServiceFactory $languageServiceFactory,
protected readonly ModuleProvider $moduleProvider,
protected readonly UriBuilder $uriBuilder,
protected readonly FormProtectionFactory $formProtectionFactory,
protected readonly Locales $locales,
protected readonly ComponentFactory $componentFactory,
protected readonly DateFormatter $dateFormatter,
protected readonly FormDataCompiler $formDataCompiler,
protected readonly NodeFactory $nodeFactory,
protected readonly FormResultFactory $formResultFactory,
protected readonly FormResultHandler $formResultHandler,
protected readonly UserSettingsSchema $userSettingsSchema,
protected readonly TcaSchemaBuilder $tcaSchemaBuilder,
protected readonly ResourceFactory $resourceFactory,
) {
$passwordPolicy = $GLOBALS['TYPO3_CONF_VARS']['BE']['passwordPolicy'] ?? 'default';
$action = PasswordPolicyAction::UPDATE_USER_PASSWORD;
if ($this->getBackendUser()->getOriginalUserIdWhenInSwitchUserMode()) {
$action = PasswordPolicyAction::UPDATE_USER_PASSWORD_SWITCH_USER_MODE;
}
$this->passwordPolicyValidator = GeneralUtility::makeInstance(
PasswordPolicyValidator::class,
$action,
is_string($passwordPolicy) ? $passwordPolicy : ''
);
}
/**
* Injects the request object, checks if data should be saved, and prepares a HTML page
*/
public function mainAction(ServerRequestInterface $request): ResponseInterface
{
$view = $this->initialize($request);
$this->storeIncomingData($request);
if ($this->pagetreeNeedsRefresh || $this->settingsAreResetToDefault) {
BackendUtility::setUpdateSignal('updatePageTree');
}
if ($this->colorSchemeChanged || $this->settingsAreResetToDefault) {
BackendUtility::setUpdateSignal('updateColorScheme', $this->getBackendUser()->uc['colorScheme'] ?? 'auto');
}
if ($this->themeChanged || $this->settingsAreResetToDefault) {
BackendUtility::setUpdateSignal('updateTheme', $this->getBackendUser()->uc['theme'] ?? 'modern');
}
if ($this->backendTitleFormatChanged || $this->settingsAreResetToDefault) {
BackendUtility::setUpdateSignal('updateTitleFormat', $this->getBackendUser()->uc['backendTitleFormat'] ?? 'titleFirst');
}
if ($this->dateTimeFirstDayOfWeekChanged || $this->settingsAreResetToDefault) {
BackendUtility::setUpdateSignal('updateDateTimeFirstDayOfWeek', $this->getBackendUser()->uc['dateTimeFirstDayOfWeek'] ?? '');
}
if ($this->languageUpdate) {
$this->getLanguageService()->init($this->getBackendUser()->user['lang'] ?? 'en');
$locale = $this->getLanguageService()->getLocale();
if ($locale !== null) {
$parameters = [
'language' => $locale->getLanguageCode(),
];
BackendUtility::setUpdateSignal('updateBackendLanguage', $parameters);
}
}
if ($this->persistentUpdate !== []) {
foreach ($this->persistentUpdate as $params) {
BackendUtility::setUpdateSignal('updatePersistent', $params);
}
}
// Use FormEngine to render the user settings form
$formData = $this->compileFormData($request, $this->userSettingsSchema->getTca());
$formData['renderType'] = 'fullRecordContainer';
$formResultArray = $this->nodeFactory->create($formData)->render();
$formResult = $this->formResultFactory->create($formResultArray);
$this->formResultHandler->addAssets($formResult);
$formProtection = $this->formProtectionFactory->createFromRequest($request);
$this->addFlashMessages($view);
$view->addButtonToButtonBar($this->componentFactory->createSaveButton('SetupModuleController')->setName('data[save]'));
$this->registerResetButtonToButtonBar($view);
// Set shortcut context - reload button is added automatically
$view->getDocHeaderComponent()->setShortcutContext(
'user_setup',
$this->getLanguageService()->translate('short_description', 'backend.modules.user_settings')
);
$view->assignMultiple([
'typo3Info' => $this->typo3Information,
'isLanguageUpdate' => $this->languageUpdate,
'formEngineHtml' => $formResult->html,
'formToken' => $formProtection->generateToken('BE user setup', 'edit'),
]);
return $view->renderResponse('Setup/Main');
}
/**
* Compile form data for FormEngine rendering.
*/
protected function compileFormData(ServerRequestInterface $request, array $userSettingsTca): array
{
$backendUser = $this->getBackendUser();
$formDataCompilerInput = [
'request' => $request,
'tableName' => 'be_users_settings',
'vanillaUid' => (int)$backendUser->user['uid'],
'command' => 'edit',
'returnUrl' => '',
'tcaSchemata' => $this->tcaSchemaBuilder->buildFromStructure($userSettingsTca),
'fullTca' => $userSettingsTca,
];
return $this->formDataCompiler->compile(
$formDataCompilerInput,
GeneralUtility::makeInstance(UserSettingsDataGroup::class)
);
}
/**
* Initializes the module for display of the settings form.
*/
protected function initialize(ServerRequestInterface $request): ModuleTemplate
{
$languageService = $this->getLanguageService();
$backendUser = $this->getBackendUser();
$view = $this->moduleTemplateFactory->create($request);
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/modal.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/form-engine.js');
$this->pageRenderer->loadJavaScriptModule('@typo3/backend/setup-module.js');
$this->processAdditionalJavaScriptModules($request);
$this->pageRenderer->addInlineSetting('FormEngine', 'formName', 'editform');
$this->pageRenderer->addInlineLanguageLabelArray([
'FormEngine.remainingCharacters' => $languageService->translate('labels.remainingCharacters', 'core.core'),
]);
$view->setTitle($languageService->translate('user_settings', 'backend.user_profile'));
$view->setLayout(ModuleLayout::NORMAL);
// Getting the 'override' values as set might be set in user TSconfig
$this->overrideConf = $backendUser->getTSConfig()['setup.']['override.'] ?? [];
// Getting the disabled fields might be set in user TSconfig (eg setup.fields.password.disabled=1)
$this->tsFieldConf = $backendUser->getTSConfig()['setup.']['fields.'] ?? [];
// if password is disabled, disable repeat of password too (password2)
if ($this->tsFieldConf['password.']['disabled'] ?? false) {
$this->tsFieldConf['password2.']['disabled'] = 1;
}
return $view;
}
protected function processAdditionalJavaScriptModules(ServerRequestInterface $request): void
{
$event = new AddUserSettingsJavaScriptModulesEvent($request);
$event = $this->eventDispatcher->dispatch($event);
foreach ($event->getJavaScriptModules() as $specifier) {
$this->pageRenderer->loadJavaScriptModule($specifier);
}
}
/**
* If settings are submitted via POST, store them
*/
protected function storeIncomingData(ServerRequestInterface $request): void
{
$postData = $request->getParsedBody();
if (!is_array($postData) || empty($postData)) {
return;
}
$formProtection = $this->formProtectionFactory->createFromRequest($request);
// Separate submitted data into corresponding partitions
$backendUserId = (int)$this->getBackendUser()->user['uid'];
$beUsersSubmission = $this->extractPartitionData($postData['data']['be_users_settings'][$backendUserId] ?? [], 'be_users');
$userSettingsSubmission = $this->extractPartitionData($postData['data']['be_users_settings'][$backendUserId] ?? [], 'user_settings');
$columns = $this->userSettingsSchema->getColumns();
$backendUser = $this->getBackendUser();
$beUserId = (int)$backendUser->user['uid'];
$storeRec = [];
$doSaveData = false;
$fieldList = $this->getFieldsFromShowItem();
if ($beUsersSubmission !== []
&& $userSettingsSubmission !== []
&& $formProtection->validateToken((string)($postData['formToken'] ?? ''), 'BE user setup', 'edit')
) {
// UC hashed before applying changes
$save_before = md5(serialize($backendUser->uc));
// PUT SETTINGS into the ->uc array:
// Reload left frame when switching BE language
if (isset($beUsersSubmission['lang']) && $beUsersSubmission['lang'] !== $backendUser->user['lang']) {
$this->languageUpdate = true;
}
// Reload pagetree if the title length is changed
if (isset($userSettingsSubmission['titleLen']) && $userSettingsSubmission['titleLen'] !== $backendUser->uc['titleLen']) {
$this->pagetreeNeedsRefresh = true;
}
if (isset($userSettingsSubmission['colorScheme']) && $userSettingsSubmission['colorScheme'] !== ($backendUser->uc['colorScheme'] ?? null)) {
$this->colorSchemeChanged = true;
}
if (isset($userSettingsSubmission['theme']) && $userSettingsSubmission['theme'] !== ($backendUser->uc['theme'] ?? null)) {
$this->themeChanged = true;
}
if (isset($userSettingsSubmission['backendTitleFormat']) && $userSettingsSubmission['backendTitleFormat'] !== ($backendUser->uc['backendTitleFormat'] ?? null)) {
$this->backendTitleFormatChanged = true;
}
if (isset($userSettingsSubmission['dateTimeFirstDayOfWeek']) && $userSettingsSubmission['dateTimeFirstDayOfWeek'] !== ($backendUser->uc['dateTimeFirstDayOfWeek'] ?? null)) {
$this->dateTimeFirstDayOfWeekChanged = true;
$this->persistentUpdate[] = [
'fieldName' => 'dateTimeFirstDayOfWeek',
'value' => $userSettingsSubmission['dateTimeFirstDayOfWeek'],
];
}
// Options which should trigger direct JS persistent update, because
// their new state needs to be available in JS components right away.
foreach ($this->userSettingsSchema->getPersistentUpdateFieldNames() as $fieldName) {
$fieldValue = ((int)($userSettingsSubmission[$fieldName] ?? 0)) ? 1 : 0;
if ($fieldValue !== ($backendUser->uc[$fieldName] ?? null)) {
$this->persistentUpdate[] = [
'fieldName' => $fieldName,
'value' => $fieldValue ? '1' : '0',
];
}
}
if ($postData['data']['setValuesToDefault'] ?? false) {
// If every value should be default
$backendUser->resetUC();
$this->settingsAreResetToDefault = true;
} elseif ($postData['data']['save'] ?? false) {
foreach ($columns as $field => $config) {
if (!in_array($field, $fieldList, true)) {
continue;
}
// Skip any disallowed field name, not matter if it's in be_users or user_settings partition
if (in_array($field, self::DISALLOWED_FIELD_NAMES, true)) {
continue;
}
$isBeUsersField = ($config['table'] ?? '') === 'be_users';
$fieldType = $config['type'] ?? 'text';
if ($isBeUsersField) {
$submittedValue = $beUsersSubmission[$field] ?? null;
if (!isset($config['access']) || ($this->checkAccess($config) && ($backendUser->user[$field] !== $submittedValue))) {
if ($fieldType === 'check') {
$fieldValue = (int)($submittedValue ?? 0);
} else {
$fieldValue = $submittedValue;
}
$storeRec['be_users'][$beUserId][$field] = $fieldValue;
$backendUser->user[$field] = $fieldValue;
}
} else {
if ($fieldType === 'check') {
$backendUser->uc[$field] = (int)($userSettingsSubmission[$field] ?? 0);
} else {
$backendUser->uc[$field] = htmlspecialchars($userSettingsSubmission[$field] ?? '');
}
}
}
// Personal data for the users be_user-record (email, name, password...)
// If email and name is changed, set it in the users record:
$be_user_data = $beUsersSubmission;
// Temporarily hold `password2` for the hook to be able to adjust the password
$be_user_data['password2'] = $userSettingsSubmission['password2'] ?? '';
// Possibility to modify the transmitted values. Useful to do transformations, like RSA password decryption
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/setup/mod/index.php']['modifyUserDataBeforeSave'] ?? [] as $function) {
$params = ['be_user_data' => &$be_user_data];
GeneralUtility::callUserFunction($function, $params, $this);
}
$this->passwordIsSubmitted = (string)($be_user_data['password'] ?? '') !== '';
$passwordIsConfirmed = $this->passwordIsSubmitted && $be_user_data['password'] === $be_user_data['password2'];
unset($be_user_data['password2']);
// Validate password against password policy
$contextData = new ContextData(
loginMode: 'BE',
currentPasswordHash: $this->getBackendUser()->user['password'],
newUserFullName: $be_user_data['realName'] ?? $this->getBackendUser()->user['realName']
);
$contextData->setData('currentUsername', $this->getBackendUser()->user['username']);
$event = $this->eventDispatcher->dispatch(
new EnrichPasswordValidationContextDataEvent(
$contextData,
$be_user_data,
self::class
)
);
$contextData = $event->getContextData();
$passwordValid = true;
if ($passwordIsConfirmed
&& !$this->passwordPolicyValidator->isValidPassword($be_user_data['password'], $contextData)
) {
$passwordValid = false;
$this->passwordIsUpdated = self::PASSWORD_POLICY_FAILED;
}
// Update the real name:
if (isset($be_user_data['realName']) && $be_user_data['realName'] !== $backendUser->user['realName']) {
$backendUser->user['realName'] = ($storeRec['be_users'][$beUserId]['realName'] = substr($be_user_data['realName'], 0, 80));
}
// Update the email address:
if (isset($be_user_data['email']) && $be_user_data['email'] !== $backendUser->user['email']) {
$backendUser->user['email'] = ($storeRec['be_users'][$beUserId]['email'] = substr($be_user_data['email'], 0, 255));
}
// Update the password:
if ($this->passwordIsSubmitted) {
if ($passwordIsConfirmed && $passwordValid) {
$this->passwordIsUpdated = self::PASSWORD_UPDATED;
$storeRec['be_users'][$beUserId]['password'] = $be_user_data['password'];
} elseif ($passwordIsConfirmed) {
$this->passwordIsUpdated = self::PASSWORD_POLICY_FAILED;
} else {
$this->passwordIsUpdated = self::PASSWORD_NOT_THE_SAME;
}
}
$this->setAvatarFileUid($beUserId, $be_user_data['avatar'] ?? null, $storeRec);
$doSaveData = true;
}
// Explicitly unset disallowed field names
foreach (self::DISALLOWED_FIELD_NAMES as $disallowedFieldName) {
unset($backendUser->uc[$disallowedFieldName]);
}
// Inserts the overriding values.
$backendUser->overrideUC();
$save_after = md5(serialize($backendUser->uc));
// If something in the uc-array of the user has changed, we save the array...
if ($save_before != $save_after) {
$backendUser->writeUC();
$backendUser->writelog(SystemLogType::SETTING, SystemLogSettingAction::CHANGE, SystemLogErrorClassification::MESSAGE, null, 'Personal settings changed', []);
$this->setupIsUpdated = true;
}
// Persist data if something has changed:
if (!empty($storeRec) && $doSaveData) {
// Set user to admin to circumvent DataHandler restrictions.
// Not using isAdmin() to fetch the original value, just in case it has been boolean casted.
$savedUserAdminState = $backendUser->user['admin'];
$backendUser->user['admin'] = true;
// Make dedicated instance of TCE for storing the changes.
$dataHandler = GeneralUtility::makeInstance(DataHandler::class);
$dataHandler->start($storeRec, [], $backendUser);
$dataHandler->process_datamap();
$dataHandler->printLogErrorMessages();
// reset the user record admin flag to previous value, just in case it gets used any further.
$backendUser->user['admin'] = $savedUserAdminState;
if ($this->passwordIsUpdated === self::PASSWORD_NOT_UPDATED || count($storeRec['be_users'][$beUserId]) > 1) {
$this->setupIsUpdated = true;
}
BackendUtility::setUpdateSignal('updateTopbar');
}
}
}
/**
* Returns access check (currently only "admin" is supported)
*
* @param array $config Configuration of the field, access mode is defined in key 'access'
* @return bool Whether it is allowed to modify the given field
*/
protected function checkAccess(array $config)
{
$access = $config['access'];
if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['setup']['accessLevelCheck'][$access])) {
if (class_exists($access)) {
$accessObject = GeneralUtility::makeInstance($access);
if (method_exists($accessObject, 'accessLevelCheck')) {
// Initialize vars. If method fails, $set will be set to FALSE
return $accessObject->accessLevelCheck($config);
}
}
} elseif ($access === 'admin') {
return $this->getBackendUser()->isAdmin();
}
return false;
}
/**
* Returns array with fields defined in TCA user settings showitem.
* Remove fields which are disabled by user TSconfig
*
* @return list<string> Array with field names visible in form
*/
protected function getFieldsFromShowItem(): array
{
// just keep field names, filter out control sequences
$tcaFieldNames = array_keys($this->userSettingsSchema->getColumns());
$allowedFields = GeneralUtility::trimExplode(',', $this->userSettingsSchema->getRawShowitem(), true);
$allowedFields = array_map($this->extractShowitemFieldName(...), $allowedFields);
$allowedFields = array_filter($allowedFields, static fn(string $field): bool => in_array($field, $tcaFieldNames, true));
$backendUser = $this->getBackendUser();
if ($backendUser->getOriginalUserIdWhenInSwitchUserMode() && $backendUser->isSystemMaintainer(true)) {
// DataHandler denies changing the password of system maintainer users in switch user mode.
// Do not show the password fields is this case.
$key = array_search('password', $allowedFields);
if ($key !== false) {
unset($allowedFields[$key]);
}
$key = array_search('password2', $allowedFields);
if ($key !== false) {
unset($allowedFields[$key]);
}
}
foreach ($this->tsFieldConf as $fieldName => $userTsFieldConfig) {
if (!empty($userTsFieldConfig['disabled'])) {
$fieldName = rtrim($fieldName, '.');
$key = array_search($fieldName, $allowedFields);
if ($key !== false) {
unset($allowedFields[$key]);
}
}
}
return $allowedFields;
}
/**
* Extracts `field_name` from a showitem field `field_name;field_label`.
*/
protected function extractShowitemFieldName(string $fieldName): string
{
$offset = strpos($fieldName, ';');
return $offset !== false ? substr($fieldName, 0, $offset) : $fieldName;
}
/**
* Get Avatar fileUid
*/
protected function getAvatarFileUid(int $beUserId): int
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference');
$file = $queryBuilder
->select('uid_local')
->from('sys_file_reference')
->where(
$queryBuilder->expr()->eq(
'tablenames',
$queryBuilder->createNamedParameter('be_users')
),
$queryBuilder->expr()->eq(
'fieldname',
$queryBuilder->createNamedParameter('avatar')
),
$queryBuilder->expr()->eq(
'uid_foreign',
$queryBuilder->createNamedParameter($beUserId, Connection::PARAM_INT)
)
)
->executeQuery()
->fetchOne();
return (int)$file;
}
/**
* Set avatar fileUid for backend user
*
* @param numeric-string|''|'delete'|null $fileUid either null, a file UID, an empty string, or `delete`
*/
protected function setAvatarFileUid(int $beUserId, ?string $fileUid, array &$storeRec): void
{
// Update is only needed when new fileUid is set
if ((int)$fileUid === $this->getAvatarFileUid($beUserId)) {
return;
}
// If user is not allowed to modify avatar $fileUid is empty - so don't overwrite existing avatar
if (empty($fileUid)) {
return;
}
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_file_reference');
$queryBuilder->getRestrictions()->removeAll();
$queryBuilder
->delete('sys_file_reference')
->where(
$queryBuilder->expr()->eq(
'tablenames',
$queryBuilder->createNamedParameter('be_users')
),
$queryBuilder->expr()->eq(
'fieldname',
$queryBuilder->createNamedParameter('avatar')
),
$queryBuilder->expr()->eq(
'uid_foreign',
$queryBuilder->createNamedParameter($beUserId, Connection::PARAM_INT)
)
)
->executeStatement();
// If Avatar is marked for delete => set it to empty string so it will be updated properly
if ($fileUid === 'delete') {
$fileUid = '';
}
// Create new reference
if ((int)$fileUid > 0) {
// Get file object
try {
$file = $this->resourceFactory->getFileObject((int)$fileUid);
} catch (FileDoesNotExistException $e) {
$file = false;
}
// Check if user is allowed to use the image (only when not in simulation mode)
if ($file && !$file->getStorage()->checkFileActionPermission('read', $file)) {
$file = false;
}
// Check if extension is allowed
if ($file && $file->isImage()) {
// Create new file reference
$storeRec['sys_file_reference']['NEW1234'] = [
'uid_local' => (int)$fileUid,
'uid_foreign' => (int)$beUserId,
'tablenames' => 'be_users',
'fieldname' => 'avatar',
'pid' => 0,
];
$storeRec['be_users'][(int)$beUserId]['avatar'] = 'NEW1234';
}
}
}
/**
* Register the reset configuration button to the button bar.
*/
protected function registerResetButtonToButtonBar(ModuleTemplate $view): void
{
$languageService = $this->getLanguageService();
$resetButton = $this->componentFactory->createGenericButton()
->setTag('button')
->setLabel($languageService->translate('reset_configuration_button', 'backend.user_profile'))
->setTitle($languageService->translate('reset_configuration', 'backend.user_profile'))
->setIcon($this->iconFactory->getIcon('actions-undo', IconSize::SMALL))
->setShowLabelText(true)
->setClasses('t3js-modal-trigger')
->setAttributes([
'type' => 'button',
'data-severity' => 'warning',
'data-title' => $languageService->translate('reset_configuration', 'backend.user_profile'),
'data-content' => $languageService->translate('set_to_standard_question', 'backend.user_profile'),
'data-event' => 'confirm',
'data-event-name' => 'setup:confirmation:response',
'data-event-payload' => 'resetConfiguration',
]);
$view->addButtonToButtonBar($resetButton, ButtonBar::BUTTON_POSITION_RIGHT);
}
/**
* Add FlashMessages for various actions
*/
protected function addFlashMessages(ModuleTemplate $view): void
{
$languageService = $this->getLanguageService();
if ($this->setupIsUpdated && !$this->settingsAreResetToDefault) {
$view->addFlashMessage($languageService->translate('setup_was_updated', 'backend.user_profile'), $languageService->translate('user_settings', 'backend.user_profile'));
}
if ($this->settingsAreResetToDefault) {
$view->addFlashMessage($languageService->translate('settings_are_reset', 'backend.user_profile'), $languageService->translate('reset_configuration', 'backend.user_profile'));
}
if ($this->passwordIsSubmitted) {
switch ($this->passwordIsUpdated) {
case self::PASSWORD_NOT_THE_SAME:
$view->addFlashMessage($languageService->translate('new_password_failed', 'backend.user_profile'), $languageService->translate('new_password', 'backend.user_profile'), ContextualFeedbackSeverity::ERROR);
break;
case self::PASSWORD_UPDATED:
$view->addFlashMessage($languageService->translate('new_password_ok', 'backend.user_profile'), $languageService->translate('new_password', 'backend.user_profile'));
break;
case self::PASSWORD_POLICY_FAILED:
$view->addFlashMessage($languageService->translate('password_policy_failed', 'backend.user_profile'), $languageService->translate('new_password', 'backend.user_profile'), ContextualFeedbackSeverity::ERROR);
break;
}
}
}
/**
* @param array<string, scalar> $data
* @return array<string, scalar>
*/
protected function extractPartitionData(array $data, string $partition): array
{
$partitionData = [];
$prefix = $partition . '__';
$length = strlen($prefix);
foreach ($data as $key => $value) {
if (!str_starts_with($key, $prefix)) {
continue;
}
$normalizedKey = substr($key, $length);
$partitionData[$normalizedKey] = $value;
}
return $partitionData;
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}