‪TYPO3CMS  ‪main
PasswordElement.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
26 
31 {
37  protected ‪$defaultFieldInformation = [
38  'tcaDescription' => [
39  'renderType' => 'tcaDescription',
40  ],
41  ];
42 
48  public function ‪render(): array
49  {
50  $table = $this->data['tableName'];
51  $fieldName = $this->data['fieldName'];
52  $parameterArray = $this->data['parameterArray'];
53  $resultArray = $this->‪initializeResultArray();
54  $config = $parameterArray['fieldConf']['config'];
55  $passwordPolicyValidator = null;
56 
57  $itemValue = $parameterArray['itemFormElValue'];
58  $width = $this->‪formMaxWidth(
59  ‪MathUtility::forceIntegerInRange($config['size'] ?? $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth)
60  );
61  $fieldId = ‪StringUtility::getUniqueId('formengine-input-');
62  $renderedLabel = $this->‪renderLabel($fieldId);
63 
64  $passwordPolicy = $config['passwordPolicy'] ?? null;
65  if ($passwordPolicy) {
66  // We always use PasswordPolicyAction::NEW_USER_PASSWORD here, since the password is not set by the user,
67  // but either by an admin or an editor
68  $passwordPolicyValidator = GeneralUtility::makeInstance(
69  PasswordPolicyValidator::class,
71  is_string($passwordPolicy) ? $passwordPolicy : ''
72  );
73  }
74 
75  $fieldInformationResult = $this->‪renderFieldInformation();
76  $fieldInformationHtml = $fieldInformationResult['html'];
77  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
78 
79  if ($config['readOnly'] ?? false) {
80  $html = [];
81  $html[] = $renderedLabel;
82  $html[] = '<div class="formengine-field-item t3js-formengine-field-item">';
83  $html[] = $fieldInformationHtml;
84  $html[] = '<div class="form-wizards-wrap">';
85  $html[] = '<div class="form-wizards-element">';
86  $html[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">';
87  $html[] = '<input class="form-control" id="' . htmlspecialchars($fieldId) . '" value="' . htmlspecialchars($this->‪getObfuscatedSecretValue($itemValue)) . '" type="text" disabled>';
88  $html[] = '</div>';
89  $html[] = '</div>';
90  $html[] = '</div>';
91  $html[] = '</div>';
92  $resultArray['html'] = implode(LF, $html);
93  return $resultArray;
94  }
95 
96  $languageService = $this->‪getLanguageService();
97  $itemName = (string)$parameterArray['itemFormElName'];
98 
99  // Always add "trim" and "password" (required for JS validation)
100  $evalList = ['trim', 'password'];
101  if ($config['nullable'] ?? false) {
102  $evalList[] = 'null';
103  }
104 
105  $attributes = [
106  'value' => '',
107  'id' => $fieldId,
108  'spellcheck' => 'false',
109  'class' => implode(' ', [
110  'form-control',
111  'form-control-clearable',
112  't3js-clearable',
113  'hasDefaultValue',
114  ]),
115  'data-formengine-validation-rules' => $this->‪getValidationDataAsJsonString($config),
116  'data-formengine-input-params' => (string)json_encode([
117  'field' => $itemName,
118  'evalList' => implode(',', $evalList),
119  ], JSON_THROW_ON_ERROR),
120  'data-formengine-input-name' => $itemName,
121  ];
122 
123  if (!empty($config['placeholder'])) {
124  $attributes['placeholder'] = trim($config['placeholder']);
125  }
126 
127  $attributes['autocomplete'] = ($config['autocomplete'] ?? false) ? 'current-password' : 'new-password';
128 
129  $fieldControlResult = $this->‪renderFieldControl();
130  $fieldControlHtml = $fieldControlResult['html'];
131  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
132 
133  $fieldWizardResult = $this->‪renderFieldWizard();
134  $fieldWizardHtml = $fieldWizardResult['html'];
135  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
136 
137  $mainFieldHtml = [];
138  $mainFieldHtml[] = '<div class="form-control-wrap" style="max-width: ' . $width . 'px">';
139  $mainFieldHtml[] = '<div class="form-wizards-wrap">';
140  $mainFieldHtml[] = '<div class="form-wizards-element">';
141  $mainFieldHtml[] = '<input type="password" ' . GeneralUtility::implodeAttributes($attributes, true) . ' />';
142  $mainFieldHtml[] = '<input type="hidden" disabled data-enable-on-modification="true" name="' . $itemName . '" value="' . htmlspecialchars($this->‪getObfuscatedSecretValue($itemValue)) . '" />';
143  $mainFieldHtml[] = '</div>';
144  if (!empty($fieldControlHtml)) {
145  $mainFieldHtml[] = '<div class="form-wizards-items-aside form-wizards-items-aside--field-control">';
146  $mainFieldHtml[] = '<div class="btn-group">';
147  $mainFieldHtml[] = $fieldControlHtml;
148  $mainFieldHtml[] = '</div>';
149  $mainFieldHtml[] = '</div>';
150  }
151  if (!empty($fieldWizardHtml)) {
152  $mainFieldHtml[] = '<div class="form-wizards-items-bottom">';
153  $mainFieldHtml[] = $fieldWizardHtml;
154  $mainFieldHtml[] = '</div>';
155  }
156  $mainFieldHtml[] = '</div>';
157  $mainFieldHtml[] = '</div>';
158  $mainFieldHtml = implode(LF, $mainFieldHtml);
159 
160  $nullControlNameEscaped = htmlspecialchars('control[active][' . $table . '][' . $this->data['databaseRow']['uid'] . '][' . $fieldName . ']');
161 
162  $fullElement = $mainFieldHtml;
163  if ($this->‪hasNullCheckboxButNoPlaceholder()) {
164  $checked = $itemValue !== null ? ' checked="checked"' : '';
165  $fullElement = [];
166  $fullElement[] = '<div class="t3-form-field-disable"></div>';
167  $fullElement[] = '<div class="form-check t3-form-field-eval-null-checkbox">';
168  $fullElement[] = '<input type="hidden" name="' . $nullControlNameEscaped . '" value="0" />';
169  $fullElement[] = '<input type="checkbox" class="form-check-input" name="' . $nullControlNameEscaped . '" id="' . $nullControlNameEscaped . '" value="1"' . $checked . ' />';
170  $fullElement[] = '<label class="form-check-label" for="' . $nullControlNameEscaped . '">';
171  $fullElement[] = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.nullCheckbox');
172  $fullElement[] = '</label>';
173  $fullElement[] = '</div>';
174  $fullElement[] = $mainFieldHtml;
175  $fullElement = implode(LF, $fullElement);
176  } elseif ($this->‪hasNullCheckboxWithPlaceholder()) {
177  $checked = $itemValue !== null ? ' checked="checked"' : '';
178  $placeholder = $shortenedPlaceholder = trim($config['placeholder'] ?? '');
179  if ($placeholder !== '') {
180  $shortenedPlaceholder = ‪GeneralUtility::fixed_lgd_cs($placeholder, 20);
181  if ($placeholder !== $shortenedPlaceholder) {
182  $overrideLabel = sprintf(
183  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'),
184  '<span title="' . htmlspecialchars($placeholder) . '">' . htmlspecialchars($shortenedPlaceholder) . '</span>'
185  );
186  } else {
187  $overrideLabel = sprintf(
188  $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override'),
189  htmlspecialchars($placeholder)
190  );
191  }
192  } else {
193  $overrideLabel = $languageService->sL(
194  'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.placeholder.override_not_available'
195  );
196  }
197  $fullElement = [];
198  $fullElement[] = '<div class="form-check t3js-form-field-eval-null-placeholder-checkbox">';
199  $fullElement[] = '<input type="hidden" name="' . $nullControlNameEscaped . '" value="0" />';
200  $fullElement[] = '<input type="checkbox" class="form-check-input" name="' . $nullControlNameEscaped . '" id="' . $nullControlNameEscaped . '" value="1"' . $checked . ' />';
201  $fullElement[] = '<label class="form-check-label" for="' . $nullControlNameEscaped . '">';
202  $fullElement[] = $overrideLabel;
203  $fullElement[] = '</label>';
204  $fullElement[] = '</div>';
205  $fullElement[] = '<div class="t3js-formengine-placeholder-placeholder">';
206  $fullElement[] = '<div class="form-control-wrap" style="max-width:' . $width . 'px">';
207  $fullElement[] = '<input type="text" class="form-control" disabled="disabled" value="' . htmlspecialchars($shortenedPlaceholder) . '" />';
208  $fullElement[] = '</div>';
209  $fullElement[] = '</div>';
210  $fullElement[] = '<div class="t3js-formengine-placeholder-formfield">';
211  $fullElement[] = $mainFieldHtml;
212  $fullElement[] = '</div>';
213  $fullElement = implode(LF, $fullElement);
214  }
215 
216  $passwordPolicyInfo = '';
217  if ($passwordPolicy) {
218  $passwordPolicyInfo = $this->‪renderPasswordPolicyRequirements($passwordPolicyValidator, $fieldId);
219  }
220 
221  $passwordElementAttributes['class'] = 'formengine-field-item t3js-formengine-field-item';
222  $passwordElementAttributes['recordFieldId'] = $fieldId;
223  $passwordElementAttributes['passwordPolicy'] = $passwordPolicy;
224 
225  $resultArray['html'] = $renderedLabel . '
226  <typo3-formengine-element-password ' . GeneralUtility::implodeAttributes($passwordElementAttributes, true) . '>
227  ' . $fieldInformationHtml . '
228  ' . $fullElement . '
229  ' . $passwordPolicyInfo . '
230  </typo3-formengine-element-password>';
231 
232  $resultArray['javaScriptModules'][] = ‪JavaScriptModuleInstruction::create(
233  '@typo3/backend/form-engine/element/password-element.js'
234  );
235 
236  return $resultArray;
237  }
238 
239  private function ‪renderPasswordPolicyRequirements(
240  ‪PasswordPolicyValidator $passwordPolicyValidator,
241  string $fieldId
242  ): string {
243  if (empty($passwordPolicyValidator->‪getRequirements())) {
244  return '';
245  }
246 
247  $passwordPolicyElement = [];
248  $requirements = [];
249 
250  foreach ($passwordPolicyValidator->‪getRequirements() as $id => $requirement) {
251  $requirements[] = '<li data-id="' . htmlspecialchars($fieldId . '-' . $id) . '">' . $requirement . '</li>';
252  }
253 
254  $calloutTitle = $this->‪getLanguageService()->sL(
255  'LLL:EXT:core/Resources/Private/Language/locallang_password_policy.xlf:passwordRequirements.description'
256  );
257 
258  $passwordPolicyElement[] = '<div id="password-policy-info-' . htmlspecialchars($fieldId) . '" class="mt-2 callout callout-secondary hidden">';
259  $passwordPolicyElement[] = ' <h5 class="callout-title">' . htmlspecialchars($calloutTitle) . '</h5>';
260  $passwordPolicyElement[] = ' <div class="callout-body">';
261  $passwordPolicyElement[] = ' <ul>';
262  $passwordPolicyElement[] = implode(LF, $requirements);
263  $passwordPolicyElement[] = ' </ul>';
264  $passwordPolicyElement[] = ' </div>';
265  $passwordPolicyElement[] = '</div>';
266 
267  return implode(LF, $passwordPolicyElement);
268  }
269 
277  protected function ‪getObfuscatedSecretValue(?string $value): string
278  {
279  if ($value === null || $value === '') {
280  return '';
281  }
282  return '*********';
283  }
284 }
‪TYPO3\CMS\Backend\Form\Element\PasswordElement\renderPasswordPolicyRequirements
‪renderPasswordPolicyRequirements(PasswordPolicyValidator $passwordPolicyValidator, string $fieldId)
Definition: PasswordElement.php:238
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:73
‪TYPO3\CMS\Core\Utility\GeneralUtility\fixed_lgd_cs
‪static string fixed_lgd_cs(string $string, int $chars, string $appendString='...')
Definition: GeneralUtility.php:92
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:104
‪TYPO3\CMS\Backend\Form\Element\PasswordElement\render
‪array render()
Definition: PasswordElement.php:47
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\create
‪static create(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:47
‪TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyValidator\getRequirements
‪getRequirements()
Definition: PasswordPolicyValidator.php:77
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:37
‪TYPO3\CMS\Backend\Form\Element\PasswordElement
Definition: PasswordElement.php:31
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldControl
‪array renderFieldControl()
Definition: AbstractFormElement.php:89
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\getLanguageService
‪getLanguageService()
Definition: AbstractFormElement.php:456
‪TYPO3\CMS\Core\PasswordPolicy\NEW_USER_PASSWORD
‪@ NEW_USER_PASSWORD
Definition: PasswordPolicyAction.php:27
‪TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyValidator
Definition: PasswordPolicyValidator.php:27
‪TYPO3\CMS\Backend\Form\Element\PasswordElement\getObfuscatedSecretValue
‪getObfuscatedSecretValue(?string $value)
Definition: PasswordElement.php:276
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\formMaxWidth
‪int formMaxWidth($size=48)
Definition: AbstractFormElement.php:332
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:133
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderLabel
‪renderLabel(string $for)
Definition: AbstractFormElement.php:119
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\PasswordPolicy\PasswordPolicyAction
‪PasswordPolicyAction
Definition: PasswordPolicyAction.php:24
‪TYPO3\CMS\Core\Utility\MathUtility\forceIntegerInRange
‪static int forceIntegerInRange(mixed $theInt, int $min, int $max=2000000000, int $defaultValue=0)
Definition: MathUtility.php:34
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Backend\Form\Element\PasswordElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: PasswordElement.php:36
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:105
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\hasNullCheckboxWithPlaceholder
‪hasNullCheckboxWithPlaceholder()
Definition: AbstractFormElement.php:195
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\hasNullCheckboxButNoPlaceholder
‪hasNullCheckboxButNoPlaceholder()
Definition: AbstractFormElement.php:163
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪initializeResultArray()
Definition: AbstractNode.php:77