1 <?php
2 3 4 5 6 7 8 9
10 class ConfirmedPasswordField extends FormField {
11
12 13 14 15 16
17 public $minLength = null;
18
19 20 21 22 23
24 public $maxLength = null;
25
26 27 28 29 30 31
32 public $requireStrongPassword = false;
33
34 35 36 37 38
39 public $canBeEmpty = false;
40
41 42 43 44 45 46 47 48 49 50 51
52 protected $showOnClick = false;
53
54 55 56 57 58 59
60 public $showOnClickTitle;
61
62 63 64 65 66 67 68 69
70 function __construct($name, $title = null, $value = "", $form = null, $showOnClick = false, $titleConfirmField = null) {
71
72 $this->children = new FieldSet(
73 $pf = new PasswordField(
74 "{$name}[_Password]",
75 (isset($title)) ? $title : _t('Member.PASSWORD')),
76 $cpf = new PasswordField(
77 "{$name}[_ConfirmPassword]",
78 (isset($titleConfirmField)) ? $titleConfirmField : _t('Member.CONFIRMPASSWORD', 'Confirm Password')
79 )
80 );
81
82 $cpf->isConfirmedPasswordField = true;
83
84
85 if($showOnClick) {
86 $this->children->push(new HiddenField("{$name}[_PasswordFieldVisible]"));
87 }
88 $this->showOnClick = $showOnClick;
89
90
91 $title = false;
92
93 parent::__construct($name, $title, null, $form);
94 $this->setValue($value);
95 }
96
97 function Field() {
98 return $this->renderWith($this->fieldTemplates());
99 }
100
101 102 103 104 105
106 function setCanBeEmpty($value) {
107 $this->canBeEmpty = (bool)$value;
108 }
109
110 111 112 113 114 115 116
117 public function setShowOnClickTitle($title) {
118 $this->showOnClickTitle = $title;
119 }
120
121 122 123
124 public function getShowOnClickTitle() {
125 return $this->showOnClickTitle;
126 }
127
128 function setRightTitle($title) {
129 foreach($this->children as $field) {
130 $field->setRightTitle($title);
131 }
132 }
133
134 135 136
137 function setChildrenTitles($titles) {
138 if(is_array($titles)&&count($titles)==2){
139 foreach($this->children as $field) {
140 if(isset($titles[0])){
141 $field->setTitle($titles[0]);
142 array_shift($titles);
143 }
144 }
145 }
146 }
147
148 149 150
151 function setValue($value) {
152 if(is_array($value)) {
153 if($value['_Password'] || (!$value['_Password'] && !$this->canBeEmpty)) {
154 $this->value = $value['_Password'];
155 }
156 if(isset($value['_PasswordFieldVisible'])){
157 $this->children->fieldByName($this->Name() . '[_PasswordFieldVisible]')->setValue($value['_PasswordFieldVisible']);
158 }
159 } else {
160 if($value || (!$value && !$this->canBeEmpty)) {
161 $this->value = $value;
162 }
163 }
164 $this->children->fieldByName($this->Name() . '[_Password]')->setValue($this->value);
165 $this->children->fieldByName($this->Name() . '[_ConfirmPassword]')->setValue($this->value);
166 }
167
168 function jsValidation() {
169 $formID = $this->form->FormName();
170 $jsTests = '';
171
172 $jsTests .= "
173 // if fields are hidden, reset values and don't validate
174 var containers = $$('.showOnClickContainer', $('#'+fieldName));
175 if(containers.length && !Element.visible(containers[0])) {
176 passEl.value = null;
177 confEl.value = null;
178 return true;
179 }
180 ";
181
182 $error1 = _t('ConfirmedPasswordField.HAVETOMATCH', 'Passwords have to match.');
183 $jsTests .= "
184 if(passEl.value != confEl.value) {
185 validationError(confEl, \"$error1\", \"error\");
186 return false;
187 }
188 ";
189
190 $error2 = _t('ConfirmedPasswordField.NOEMPTY', 'Passwords can\'t be empty.');
191 if(!$this->canBeEmpty) {
192 $jsTests .= "
193 if(!passEl.value || !confEl.value) {
194 validationError(confEl, \"$error2\", \"error\");
195 return false;
196 }
197 ";
198 }
199
200 if(($this->minLength || $this->maxLength)) {
201 if($this->minLength && $this->maxLength) {
202 $limit = "{{$this->minLength},{$this->maxLength}}";
203 $errorMsg = sprintf(_t('ConfirmedPasswordField.BETWEEN', 'Passwords must be %s to %s characters long.'), $this->minLength, $this->maxLength);
204 } elseif($this->minLength) {
205 $limit = "{{$this->minLength}}.*";
206 $errorMsg = sprintf(_t('ConfirmedPasswordField.ATLEAST', 'Passwords must be at least %s characters long.'), $this->minLength);
207 } elseif($this->maxLength) {
208 $limit = "{0,{$this->maxLength}}";
209 $errorMsg = sprintf(_t('ConfirmedPasswordField.MAXIMUM', 'Passwords must be at most %s characters long.'), $this->maxLength);
210 }
211 $limitRegex = '/^.' . $limit . '$/';
212 $jsTests .= "
213 if(passEl.value && !passEl.value.match({$limitRegex})) {
214 validationError(confEl, \"{$errorMsg}\", \"error\");
215 return false;
216 }
217 ";
218 }
219
220 $error3 = _t('ConfirmedPasswordField.LEASTONE', 'Passwords must have at least one digit and one alphanumeric character.');
221 if($this->requireStrongPassword) {
222 $jsTests .= "
223 if(!passEl.value.match(/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/)) {
224 validationError(
225 confEl,
226 \"$error3\",
227 \"error\"
228 );
229 return false;
230 }
231 ";
232 }
233
234 $jsFunc =<<<JS
235 Behaviour.register({
236 "#$formID": {
237 validateConfirmedPassword: function(fieldName) {
238 var passEl = _CURRENT_FORM.elements['Password[_Password]'];
239 var confEl = _CURRENT_FORM.elements['Password[_ConfirmPassword]'];
240 $jsTests
241 return true;
242 }
243 }
244 });
245 JS;
246 Requirements :: customScript($jsFunc, 'func_validateConfirmedPassword_' . $formID);
247
248
249 return <<<JS
250 if(typeof fromAnOnBlur != 'undefined'){
251 if(fromAnOnBlur.name == '$this->name')
252 $('$formID').validateConfirmedPassword('$this->name');
253 }else{
254 $('$formID').validateConfirmedPassword('$this->name');
255 }
256 JS;
257 }
258
259 260 261 262 263 264 265
266 function isSaveable() {
267 $isVisible = $this->children->fieldByName($this->Name() . '[_PasswordFieldVisible]');
268 return (!$this->showOnClick || ($this->showOnClick && $isVisible && $isVisible->Value()));
269 }
270
271 function validate() {
272 $validator = $this->form->getValidator();
273 $name = $this->name;
274
275
276 if(!$this->isSaveable()) return true;
277
278 $passwordField = $this->children->fieldByName($name.'[_Password]');
279 $passwordConfirmField = $this->children->fieldByName($name.'[_ConfirmPassword]');
280 $passwordField->setValue($_POST[$name]['_Password']);
281 $passwordConfirmField->setValue($_POST[$name]['_ConfirmPassword']);
282
283 $value = $passwordField->Value();
284
285
286 if($value != $passwordConfirmField->Value()) {
287 $validator->validationError($name, _t('Form.VALIDATIONPASSWORDSDONTMATCH',"Passwords don't match"), "validation", false);
288 return false;
289 }
290
291 if(!$this->canBeEmpty) {
292
293 if(!$value || !$passwordConfirmField->Value()) {
294 $validator->validationError($name, _t('Form.VALIDATIONPASSWORDSNOTEMPTY', "Passwords can't be empty"), "validation", false);
295 return false;
296 }
297 }
298
299
300 if(($this->minLength || $this->maxLength)) {
301 if($this->minLength && $this->maxLength) {
302 $limit = "{{$this->minLength},{$this->maxLength}}";
303 $errorMsg = sprintf(_t('ConfirmedPasswordField.BETWEEN', 'Passwords must be %s to %s characters long.'), $this->minLength, $this->maxLength);
304 } elseif($this->minLength) {
305 $limit = "{{$this->minLength}}.*";
306 $errorMsg = sprintf(_t('ConfirmedPasswordField.ATLEAST', 'Passwords must be at least %s characters long.'), $this->minLength);
307 } elseif($this->maxLength) {
308 $limit = "{0,{$this->maxLength}}";
309 $errorMsg = sprintf(_t('ConfirmedPasswordField.MAXIMUM', 'Passwords must be at most %s characters long.'), $this->maxLength);
310 }
311 $limitRegex = '/^.' . $limit . '$/';
312 if(!empty($value) && !preg_match($limitRegex,$value)) {
313 $validator->validationError('Password', $errorMsg,
314 "validation",
315 false
316 );
317 }
318 }
319
320 if($this->requireStrongPassword) {
321 if(!preg_match('/^(([a-zA-Z]+\d+)|(\d+[a-zA-Z]+))[a-zA-Z0-9]*$/',$value)) {
322 $validator->validationError(
323 'Password',
324 _t('Form.VALIDATIONSTRONGPASSWORD', "Passwords must have at least one digit and one alphanumeric character."),
325 "validation",
326 false
327 );
328 return false;
329 }
330 }
331 return true;
332 }
333
334 335 336 337 338 339 340
341 function saveInto(DataObject $record) {
342 if(!$this->isSaveable()) return false;
343
344 if(!($this->canBeEmpty && !$this->value)) {
345 parent::saveInto($record);
346 }
347 }
348
349 350 351
352 function performReadonlyTransformation() {
353 $stars = '*****';
354
355 $field = new ReadonlyField($this->name, $this->title ? $this->title : _t('Member.PASSWORD'), $stars);
356 $field->setForm($this->form);
357 return $field;
358 }
359 }
360
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the
Silverstripe Forums to ask development related questions.
-