Skip to content

Commit

Permalink
[TASK] Update display and markup of user settings form
Browse files Browse the repository at this point in the history
Previously, the form fields in user settings form were displayed in
their entirety, filling the screen.

With this patch, the width of the input mask is now reduced to create
a uniform appearance with forms of the form engine. Some individual
spacing between the form fields has also been adjusted to match those
of the form engine.

The buttons for adding or removing an avatar image now each contain
speaking labels that were previously only aria-labels.

Additionally, the form fields markup in the user settings has been
updated to fully match the FormEngine form field markup.
With this we create the prerequisite that rollouts of global style
adjustments for form fields also work on this form.
Mid-term, this module should be using FormEngine for rendering anyway,
so this is kind of a pre-patch.

Resolves: #100605
Releases: main
Change-Id: Ia83c9d42bcdb2faa12e1c1918d19e66a43f7ad66
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/78661
Reviewed-by: Stefan Bürk <stefan@buerk.tech>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Stefan Bürk <stefan@buerk.tech>
Tested-by: core-ci <typo3@b13.com>
  • Loading branch information
minapok authored and sbuerk committed Apr 24, 2023
1 parent 2e26e90 commit 8179941
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 105 deletions.
1 change: 1 addition & 0 deletions Build/Sources/Sass/backend.scss
Expand Up @@ -101,5 +101,6 @@
@import "module/page";
@import "module/recycler";
@import "module/scheduler";
@import "module/usersettings";
@import "module/viewpage";
@import "module/workspaces";
12 changes: 12 additions & 0 deletions Build/Sources/Sass/module/_usersettings.scss
@@ -0,0 +1,12 @@
//
// User settings
//
#SetupModuleController .form-wizards-wrap {
margin-bottom: .5rem !important;
}

#SetupModuleController .row > .form-group {
@include media-breakpoint-up('lg') {
width: 50%;
}
}
4 changes: 4 additions & 0 deletions typo3/sysext/backend/Resources/Public/Css/backend.css
Expand Up @@ -4414,6 +4414,10 @@ html{scroll-behavior:smooth}
@media (min-width:992px){
#task_end_col,#task_start_col{width:25%}
}
#SetupModuleController .form-wizards-wrap{margin-bottom:.5rem!important}
@media (min-width:992px){
#SetupModuleController .row>.form-group{width:50%}
}
.typo3-module-viewpage{background-color:#494949}
.typo3-module-viewpage .module{background-color:transparent}
.typo3-module-viewpage .module-body{text-align:center}
Expand Down
Expand Up @@ -134,10 +134,7 @@
<source>Setup not possible</source>
</trans-unit>
<trans-unit id="setup.recoveryCodes.noActiveProviders.message" resname="setup.recoveryCodes.noActiveProviders.message">
<source>
Recovery codes are only meant as a fallback if you lose access to your main multi-factor
authentication credentials. Therefore, please activate a comprehensive MFA provider first.
</source>
<source>Recovery codes are only meant as a fallback if you lose access to your main multi-factorauthentication credentials. Therefore, please activate a comprehensive MFA provider first.</source>
</trans-unit>
<trans-unit id="setup.totpAuthUrlButton.title" resname="setup.totpAuthUrlButton.title">
<source>Show authentication URL</source>
Expand Down
95 changes: 63 additions & 32 deletions typo3/sysext/setup/Classes/Controller/SetupModuleController.php
Expand Up @@ -452,14 +452,13 @@ protected function renderUserSetup(): array

break;
case 'check':
$html = $label . '<div class="form-check form-switch"><input id="field_' . htmlspecialchars($fieldName) . '"
$html = '<input id="field_' . htmlspecialchars($fieldName) . '"
type="checkbox"
class="form-check-input"
name="data' . $dataAdd . '[' . htmlspecialchars($fieldName) . ']"' .
($value ? ' checked="checked"' : '') .
$more .
' /></div>';
$label = '';
' />';
break;
case 'language':
$html = $this->renderLanguageSelect();
Expand Down Expand Up @@ -494,7 +493,7 @@ class="form-check-input"
$buttonAttributes['data-event-name'] = htmlspecialchars($clickData['eventName']);
$buttonAttributes['data-event-payload'] = htmlspecialchars($fieldName);
}
$html = '<br><input '
$html = '<input '
. GeneralUtility::implodeAttributes($buttonAttributes, false) . ' />';
}
if (!empty($config['confirm'])) {
Expand All @@ -513,45 +512,50 @@ class="form-check-input"
$buttonAttributes['data-event-name'] = htmlspecialchars($confirmData['eventName']);
$buttonAttributes['data-event-payload'] = htmlspecialchars($fieldName);
}
$html = '<br><input '
$html = '<input '
. GeneralUtility::implodeAttributes($buttonAttributes, false) . ' />';
}
break;
case 'avatar':
// Get current avatar image
$html = '<br>';
$html = '';
$avatarFileUid = $this->getAvatarFileUid($backendUser->user['uid']);

if ($avatarFileUid) {
$defaultAvatarProvider = GeneralUtility::makeInstance(DefaultAvatarProvider::class);
$avatarImage = $defaultAvatarProvider->getImage($backendUser->user, 32);
if ($avatarImage) {
$icon = '<span class="avatar avatar-size-medium"><span class="avatar-image">' .
$icon = '<span class="avatar avatar-size-medium mb-2"><span class="avatar-image">' .
'<img alt="" src="' . htmlspecialchars($avatarImage->getUrl()) . '"' .
' width="' . (int)$avatarImage->getWidth() . '"' .
' height="' . (int)$avatarImage->getHeight() . '" />' .
'</span></span>';
$html .= '<span class="float-start" style="padding-right: 10px" id="image_' . htmlspecialchars($fieldName) . '">' . $icon . ' </span>';
$html .= '<span id="image_' . htmlspecialchars($fieldName) . '">' . $icon . ' </span>';
}
}
$html .= '<input id="field_' . htmlspecialchars($fieldName) . '" type="hidden" ' .
'name="data' . $dataAdd . '[' . htmlspecialchars($fieldName) . ']"' . $more .
' value="' . $avatarFileUid . '" data-setup-avatar-field="' . htmlspecialchars($fieldName) . '" />';

$html .= '<div class="btn-group">';
$html .= '<typo3-formengine-container-files><div class="form-group"><div class="form-group"><div class="form-control-wrap">';
$html .= '<button type="button" id="add_button_' . htmlspecialchars($fieldName)
. '" class="btn btn-default"'
. ' title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:avatar.openFileBrowser')) . '"'
. ' data-setup-avatar-url="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('wizard_element_browser', ['mode' => 'file', 'bparams' => '||||-0-be_users-avatar-avatar'])) . '"'
. '>' . $this->iconFactory->getIcon('actions-insert-record', Icon::SIZE_SMALL)
. htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:avatar.openFileBrowser'))
. '</button>';
if ($avatarFileUid) {
$html .=
'<button type="button" id="clear_button_' . htmlspecialchars($fieldName) . '" aria-label="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:avatar.clear')) . '" '
. ' class="btn btn-default">'
. $this->iconFactory->getIcon('actions-delete', Icon::SIZE_SMALL)
. '</button>';
// Keep space between both buttons with a whitespace (like for other buttons)
$html .= ' ';
$html .= '<button type="button" id="clear_button_' . htmlspecialchars($fieldName)
. '" class="btn btn-default"'
. ' title="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:avatar.clear')) . '" '
. '>' . $this->iconFactory->getIcon('actions-delete', Icon::SIZE_SMALL)
. htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:avatar.clear'))
. '</button>';
}
$html .=
'<button type="button" id="add_button_' . htmlspecialchars($fieldName) . '" class="btn btn-default btn-add-avatar"'
. ' aria-label="' . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:avatar.openFileBrowser')) . '"'
. ' data-setup-avatar-url="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('wizard_element_browser', ['mode' => 'file', 'bparams' => '||||-0-be_users-avatar-avatar'])) . '"'
. '>' . $this->iconFactory->getIcon('actions-insert-record', Icon::SIZE_SMALL)
. '</button></div>';
$html .= '</div></div></div></typo3-formengine-container-files>';
break;
case 'mfa':
$label = $this->getLabel($config['label'] ?? '');
Expand All @@ -565,24 +569,51 @@ class="form-check-input"
$html .= ' <span class="badge badge-success">' . htmlspecialchars($lang->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:mfaProviders.enabled')) . '</span>';
}
}
$html .= '<p class="text-body-secondary">' . nl2br(htmlspecialchars($lang->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:mfaProviders.description'))) . '</p>';
$html .= '<div class="formengine-field-item t3js-formengine-field-item">';
$html .= '<div class="form-description">' . nl2br(htmlspecialchars($lang->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:mfaProviders.description'))) . '</div>';
if (!$this->mfaProviderRegistry->hasProviders()) {
$html .= '<span class="badge badge-danger">' . htmlspecialchars($lang->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:mfaProviders.notAvailable')) . '</span>';
break;
}
$html .= '<a href="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('mfa')) . '" class="btn btn-' . ($hasActiveProviders ? 'default' : 'success') . '">';
$html .= $this->iconFactory->getIcon($hasActiveProviders ? 'actions-cog' : 'actions-plus', Icon::SIZE_SMALL);
$html .= ' <span>' . htmlspecialchars($lang->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:mfaProviders.' . ($hasActiveProviders ? 'manageLinkTitle' : 'setupLinkTitle'))) . '</span>';
$html .= '<div class="form-group"><div class="form-group"><div class="form-control-wrap t3js-file-controls">';
$html .= '<a href="' . htmlspecialchars((string)$this->uriBuilder->buildUriFromRoute('mfa')) . '" class="btn btn-default">';
$html .= htmlspecialchars($lang->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:mfaProviders.' . ($hasActiveProviders ? 'manageLinkTitle' : 'setupLinkTitle')));
$html .= '</a>';
$html .= '</div></div></div></div>';
break;
default:
$html = '';
}

$code[] = '<div class="form-section"><div class="row"><div class="form-group t3js-formengine-field-item col-md-12">' .
$label .
$html .
'</div></div></div>';
$htmlPrepended = '';
$htmlAppended = '';
if ($type === 'button') {
$htmlPrepended = '<div class="formengine-field-item t3js-formengine-field-item"><div class="form-group">'
. '<div class="form-group"><div class="form-control-wrap t3js-file-controls">';
$htmlAppended = '</div></div></div></div>';
}
if ($type === 'check') {
$htmlPrepended = '<div class="formengine-field-item t3js-formengine-field-item"><div class="form-wizards-wrap">'
. '<div class="form-wizards-element"><div class="form-check form-switch">';
$htmlAppended = '</div></div></div></div>';
}
if ($type === 'select' || $type === 'language') {
$htmlPrepended = '<div class="formengine-field-item t3js-formengine-field-item"><div class="form-control-wrap">'
. '<div class="form-wizards-wrap"><div class="form-wizards-element"><div class="input-group">';
$htmlAppended = '</div></div></div></div></div>';
}
if ($type === 'text' || $type === 'number' || $type === 'email' || $type === 'password') {
$htmlPrepended = '<div class="formengine-field-item t3js-formengine-field-item"><div class="form-control-wrap">'
. '<div class="form-wizards-wrap"><div class="form-wizards-element"><div class="form-control-clearable">';
$htmlAppended = '</div></div></div></div></div>';
}

$code[] = '<fieldset class="form-section"><div class="row"><div class="form-group col-md-12">'
. $label
. $htmlPrepended
. $html
. $htmlAppended
. '</div></div></fieldset>';
}

$result[] = [
Expand Down Expand Up @@ -626,8 +657,8 @@ protected function renderLanguageSelect()
}
$content = '<select id="field_lang" name="data[be_users][lang]" class="form-select">' . $content . '</select>';
if ($currentSelectedLanguage !== 'default' && !@is_dir(Environment::getLabelsPath() . '/' . $currentSelectedLanguage)) {
$languageUnavailableWarning = htmlspecialchars(sprintf($languageService->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:languageUnavailable'), $languageService->sL($officialLanguages->getLabelIdentifier($currentSelectedLanguage)))) . '&nbsp;&nbsp;<br />&nbsp;&nbsp;' . htmlspecialchars($languageService->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:languageUnavailable.' . ($backendUser->isAdmin() ? 'admin' : 'user')));
$content = '<br /><span class="badge badge-danger">' . $languageUnavailableWarning . '</span><br /><br />' . $content;
$languageUnavailableWarning = htmlspecialchars(sprintf($languageService->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:languageUnavailable'), $languageService->sL($officialLanguages->getLabelIdentifier($currentSelectedLanguage)))) . '&nbsp;&nbsp;<br>&nbsp;&nbsp;' . htmlspecialchars($languageService->sL('LLL:EXT:setup/Resources/Private/Language/locallang.xlf:languageUnavailable.' . ($backendUser->isAdmin() ? 'admin' : 'user')));
$content = '<br><span class="badge badge-danger">' . $languageUnavailableWarning . '</span><br><br>' . $content;
}
return $content;
}
Expand Down Expand Up @@ -703,9 +734,9 @@ protected function getLabel($str, $key = '', $addLabelTag = true)
}
if ($addLabelTag) {
if ($key !== '') {
$out = '<label for="field_' . htmlspecialchars($key) . '">' . $out . '</label>';
$out = '<label class="form-label t3js-formengine-label" for="field_' . htmlspecialchars($key) . '">' . $out . '</label>';
} else {
$out = '<label>' . $out . '</label>';
$out = '<label class="form-label t3js-formengine-label">' . $out . '</label>';
}
}
return $out;
Expand Down
50 changes: 10 additions & 40 deletions typo3/sysext/setup/Resources/Private/Language/locallang.xlf
Expand Up @@ -22,7 +22,7 @@
<source>Simulate backend user</source>
</trans-unit>
<trans-unit id="opening" resname="opening">
<source>Startup</source>
<source>Backend appearance</source>
</trans-unit>
<trans-unit id="personal_data" resname="personal_data">
<source>Personal data</source>
Expand All @@ -37,7 +37,7 @@
<source>Show help text when applicable</source>
</trans-unit>
<trans-unit id="maxTitleLen" resname="maxTitleLen">
<source>Max. Title Length</source>
<source>Max. title length</source>
</trans-unit>
<trans-unit id="edit_docModuleUpload" resname="edit_docModuleUpload">
<source>File upload directly in Doc-module</source>
Expand All @@ -46,19 +46,16 @@
<source>Show hidden files and folders in the filelist</source>
</trans-unit>
<trans-unit id="editFunctionsTab" resname="editFunctionsTab">
<source>Edit and Advanced functions</source>
</trans-unit>
<trans-unit id="edit_functions" resname="edit_functions">
<source>Edit</source>
<source>Edit and advanced functions</source>
</trans-unit>
<trans-unit id="setToStandard" resname="setToStandard">
<source>Reset all Values to default</source>
<source>Reset all values to default</source>
</trans-unit>
<trans-unit id="resetSectionHeader" resname="resetSectionHeader">
<source>Reset Settings</source>
<trans-unit id="resetTab" resname="resetTab">
<source>Reset configuration</source>
</trans-unit>
<trans-unit id="resetConfiguration" resname="resetConfiguration">
<source>Reset Configuration and Clear Temporary Data</source>
<source>Reset configuration and clear temporary data</source>
</trans-unit>
<trans-unit id="resetConfigurationButton" resname="resetConfigurationButton">
<source>Reset user settings to default state</source>
Expand All @@ -70,7 +67,7 @@
<source>Advanced functions</source>
</trans-unit>
<trans-unit id="copyLevels" resname="copyLevels">
<source>Recursive Copy: Enter the number of page sublevels to include, when a page is copied</source>
<source>Recursive copy: Enter the number of page sublevels to include, when a page is copied</source>
</trans-unit>
<trans-unit id="activateChanges" resname="activateChanges">
<source>Notice! In order to activate most of these changes, please reload the backend (eg. logout and login again).</source>
Expand All @@ -88,13 +85,13 @@
<source>Your email address</source>
</trans-unit>
<trans-unit id="save" resname="save">
<source>Save Configuration</source>
<source>Save configuration</source>
</trans-unit>
<trans-unit id="newPassword" resname="newPassword">
<source>New password</source>
</trans-unit>
<trans-unit id="newPasswordAgain" resname="newPasswordAgain">
<source>New password, again</source>
<source>New password (repetition)</source>
</trans-unit>
<trans-unit id="newPassword_ok" resname="newPassword_ok">
<source>Password was updated.</source>
Expand Down Expand Up @@ -138,33 +135,6 @@
<trans-unit id="oldPassword_failed" resname="oldPassword_failed">
<source>Password was NOT updated because you didn't enter the correct current password.</source>
</trans-unit>
<trans-unit id="enableInstallTool.label" resname="enableInstallTool.label">
<source>Install Tool Access</source>
</trans-unit>
<trans-unit id="enableInstallTool.createFile" resname="enableInstallTool.createFile">
<source>Create Install Tool Enable File</source>
</trans-unit>
<trans-unit id="enableInstallTool.file" resname="enableInstallTool.file">
<source>Install Tool Enable file</source>
</trans-unit>
<trans-unit id="enableInstallTool.fileCreate_ok" resname="enableInstallTool.fileCreate_ok">
<source>The Install Tool Enable file has been created.</source>
</trans-unit>
<trans-unit id="enableInstallTool.fileCreate_failed" resname="enableInstallTool.fileCreate_failed">
<source>The Install Tool Enable file could not be created.</source>
</trans-unit>
<trans-unit id="enableInstallTool.deleteFile" resname="enableInstallTool.deleteFile">
<source>Delete Install Tool Enable File</source>
</trans-unit>
<trans-unit id="enableInstallTool.fileDelete_ok" resname="enableInstallTool.fileDelete_ok">
<source>The Install Tool Enable file has been deleted.</source>
</trans-unit>
<trans-unit id="enableInstallTool.fileDelete_failed" resname="enableInstallTool.fileDelete_failed">
<source>The Install Tool Enable file could not be deleted!</source>
</trans-unit>
<trans-unit id="enableInstallTool.fileHasKeep" resname="enableInstallTool.fileHasKeep">
<source>The Install Tool file has the content "KEEP_FILE" and can not be deleted!</source>
</trans-unit>
<trans-unit id="settingsAreReset" resname="settingsAreReset">
<source>The user settings have been reset to default values and temporary data has been cleared.</source>
</trans-unit>
Expand Down

0 comments on commit 8179941

Please sign in to comment.