Issue #1479454 by Hugo Wetterberg, galooph, dawehner, andypost, marcingy, heyrocker...
[project/drupal.git] / core / modules / user / lib / Drupal / user / AccountFormController.php
1 <?php
2
3 /**
4 * @file
5 * Definition of Drupal\user\AccountFormController.
6 */
7
8 namespace Drupal\user;
9
10 use Drupal\Core\Entity\EntityInterface;
11 use Drupal\Core\Entity\EntityFormController;
12
13 /**
14 * Form controller for the user account forms.
15 */
16 abstract class AccountFormController extends EntityFormController {
17
18 /**
19 * Overrides Drupal\Core\Entity\EntityFormController::form().
20 */
21 public function form(array $form, array &$form_state, EntityInterface $account) {
22 global $user;
23 $config = config('user.settings');
24
25 $language_interface = language(LANGUAGE_TYPE_INTERFACE);
26 $register = empty($account->uid);
27 $admin = user_access('administer users');
28
29 // Account information.
30 $form['account'] = array(
31 '#type' => 'container',
32 '#weight' => -10,
33 );
34
35 // Only show name field on registration form or user can change own username.
36 $form['account']['name'] = array(
37 '#type' => 'textfield',
38 '#title' => t('Username'),
39 '#maxlength' => USERNAME_MAX_LENGTH,
40 '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
41 '#required' => TRUE,
42 '#attributes' => array('class' => array('username'), 'autocomplete' => 'off'),
43 '#default_value' => (!$register ? $account->name : ''),
44 '#access' => ($register || ($user->uid == $account->uid && user_access('change own username')) || $admin),
45 '#weight' => -10,
46 );
47
48 // The mail field is NOT required if account originally had no mail set
49 // and the user performing the edit has 'administer users' permission.
50 // This allows users without e-mail address to be edited and deleted.
51 $form['account']['mail'] = array(
52 '#type' => 'email',
53 '#title' => t('E-mail address'),
54 '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
55 '#required' => !(empty($account->mail) && user_access('administer users')),
56 '#default_value' => (!$register ? $account->mail : ''),
57 '#attributes' => array('autocomplete' => 'off'),
58 );
59
60 // Display password field only for existing users or when user is allowed to
61 // assign a password during registration.
62 if (!$register) {
63 $form['account']['pass'] = array(
64 '#type' => 'password_confirm',
65 '#size' => 25,
66 '#description' => t('To change the current user password, enter the new password in both fields.'),
67 );
68
69 // To skip the current password field, the user must have logged in via a
70 // one-time link and have the token in the URL.
71 $pass_reset = isset($_SESSION['pass_reset_' . $account->uid]) && isset($_GET['pass-reset-token']) && ($_GET['pass-reset-token'] == $_SESSION['pass_reset_' . $account->uid]);
72 $protected_values = array();
73 $current_pass_description = '';
74
75 // The user may only change their own password without their current
76 // password if they logged in via a one-time login link.
77 if (!$pass_reset) {
78 $protected_values['mail'] = $form['account']['mail']['#title'];
79 $protected_values['pass'] = t('Password');
80 $request_new = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
81 $current_pass_description = t('Required if you want to change the %mail or %pass below. !request_new.', array('%mail' => $protected_values['mail'], '%pass' => $protected_values['pass'], '!request_new' => $request_new));
82 }
83
84 // The user must enter their current password to change to a new one.
85 if ($user->uid == $account->uid) {
86 $form['account']['current_pass_required_values'] = array(
87 '#type' => 'value',
88 '#value' => $protected_values,
89 );
90
91 $form['account']['current_pass'] = array(
92 '#type' => 'password',
93 '#title' => t('Current password'),
94 '#size' => 25,
95 '#access' => !empty($protected_values),
96 '#description' => $current_pass_description,
97 '#weight' => -5,
98 // Do not let web browsers remember this password, since we are
99 // trying to confirm that the person submitting the form actually
100 // knows the current one.
101 '#attributes' => array('autocomplete' => 'off'),
102 );
103
104 $form_state['user'] = $account;
105 $form['#validate'][] = 'user_validate_current_pass';
106 }
107 }
108 elseif (!$config->get('verify_mail') || $admin) {
109 $form['account']['pass'] = array(
110 '#type' => 'password_confirm',
111 '#size' => 25,
112 '#description' => t('Provide a password for the new account in both fields.'),
113 '#required' => TRUE,
114 );
115 }
116
117 if ($admin) {
118 $status = isset($account->status) ? $account->status : 1;
119 }
120 else {
121 $status = $register ? $config->get('register') == USER_REGISTER_VISITORS : $account->status;
122 }
123
124 $form['account']['status'] = array(
125 '#type' => 'radios',
126 '#title' => t('Status'),
127 '#default_value' => $status,
128 '#options' => array(t('Blocked'), t('Active')),
129 '#access' => $admin,
130 );
131
132 $roles = array_map('check_plain', user_role_names(TRUE));
133 // The disabled checkbox subelement for the 'authenticated user' role
134 // must be generated separately and added to the checkboxes element,
135 // because of a limitation in Form API not supporting a single disabled
136 // checkbox within a set of checkboxes.
137 // @todo This should be solved more elegantly. See issue #119038.
138 $checkbox_authenticated = array(
139 '#type' => 'checkbox',
140 '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
141 '#default_value' => TRUE,
142 '#disabled' => TRUE,
143 );
144 unset($roles[DRUPAL_AUTHENTICATED_RID]);
145
146 $form['account']['roles'] = array(
147 '#type' => 'checkboxes',
148 '#title' => t('Roles'),
149 '#default_value' => (!$register && isset($account->roles) ? array_keys($account->roles) : array()),
150 '#options' => $roles,
151 '#access' => $roles && user_access('administer permissions'),
152 DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
153 );
154
155 $form['account']['notify'] = array(
156 '#type' => 'checkbox',
157 '#title' => t('Notify user of new account'),
158 '#access' => $register && $admin,
159 );
160
161 // Signature.
162 $form['signature_settings'] = array(
163 '#type' => 'details',
164 '#title' => t('Signature settings'),
165 '#weight' => 1,
166 '#access' => (!$register && $config->get('signatures')),
167 );
168
169 $form['signature_settings']['signature'] = array(
170 '#type' => 'text_format',
171 '#title' => t('Signature'),
172 '#default_value' => isset($account->signature) ? $account->signature : '',
173 '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
174 '#format' => isset($account->signature_format) ? $account->signature_format : NULL,
175 );
176
177 $user_preferred_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account);
178
179 $user_preferred_admin_langcode = $register ? $language_interface->langcode : user_preferred_langcode($account, 'admin');
180
181 // Is default the interface language?
182 include_once DRUPAL_ROOT . '/core/includes/language.inc';
183 $interface_language_is_default = language_negotiation_method_get_first(LANGUAGE_TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_SELECTED;
184 $form['language'] = array(
185 '#type' => language_multilingual() ? 'details' : 'container',
186 '#title' => t('Language settings'),
187 // Display language selector when either creating a user on the admin
188 // interface or editing a user account.
189 '#access' => !$register || user_access('administer users'),
190 );
191
192 $form['language']['preferred_langcode'] = array(
193 '#type' => 'language_select',
194 '#title' => t('Site language'),
195 '#languages' => LANGUAGE_CONFIGURABLE,
196 '#default_value' => $user_preferred_langcode,
197 '#description' => $interface_language_is_default ? t("This account's preferred language for e-mails and site presentation.") : t("This account's preferred language for e-mails."),
198 );
199
200 $form['language']['preferred_admin_langcode'] = array(
201 '#type' => 'language_select',
202 '#title' => t('Administration pages language'),
203 '#languages' => LANGUAGE_CONFIGURABLE,
204 '#default_value' => $user_preferred_admin_langcode,
205 '#access' => user_access('access administration pages', $account),
206 );
207
208 // User entities contain both a langcode property (for identifying the
209 // language of the entity data) and a preferred_langcode property (see
210 // above). Rather than provide a UI forcing the user to choose both
211 // separately, assume that the user profile data is in the user's preferred
212 // language. This element provides that synchronization. For use-cases where
213 // this synchronization is not desired, a module can alter or remove this
214 // element.
215 $form['language']['langcode'] = array(
216 '#type' => 'value',
217 '#value_callback' => '_user_language_selector_langcode_value',
218 // For the synchronization to work, this element must have a larger weight
219 // than the preferred_langcode element. Set a large weight here in case
220 // a module alters the weight of the other element.
221 '#weight' => 100,
222 );
223
224 return parent::form($form, $form_state, $account);
225 }
226
227 /**
228 * Overrides Drupal\Core\Entity\EntityFormController::submit().
229 */
230 public function validate(array $form, array &$form_state) {
231 parent::validate($form, $form_state);
232
233 $account = $this->getEntity($form_state);
234 // Validate new or changing username.
235 if (isset($form_state['values']['name'])) {
236 if ($error = user_validate_name($form_state['values']['name'])) {
237 form_set_error('name', $error);
238 }
239 // Cast the user ID as an integer. It might have been set to NULL, which
240 // could lead to unexpected results.
241 else {
242 $name_taken = (bool) db_select('users')
243 ->fields('users', array('uid'))
244 ->condition('uid', (int) $account->uid, '<>')
245 ->condition('name', db_like($form_state['values']['name']), 'LIKE')
246 ->range(0, 1)
247 ->execute()
248 ->fetchField();
249
250 if ($name_taken) {
251 form_set_error('name', t('The name %name is already taken.', array('%name' => $form_state['values']['name'])));
252 }
253 }
254 }
255
256 $mail = $form_state['values']['mail'];
257
258 if (!empty($mail)) {
259 $mail_taken = (bool) db_select('users')
260 ->fields('users', array('uid'))
261 ->condition('uid', (int) $account->uid, '<>')
262 ->condition('mail', db_like($mail), 'LIKE')
263 ->range(0, 1)
264 ->execute()
265 ->fetchField();
266
267 if ($mail_taken) {
268 // Format error message dependent on whether the user is logged in or not.
269 if ($GLOBALS['user']->uid) {
270 form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $mail)));
271 }
272 else {
273 form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $mail, '@password' => url('user/password'))));
274 }
275 }
276 }
277
278 // Make sure the signature isn't longer than the size of the database field.
279 // Signatures are disabled by default, so make sure it exists first.
280 if (isset($form_state['values']['signature'])) {
281 // Move text format for user signature into 'signature_format'.
282 $form_state['values']['signature_format'] = $form_state['values']['signature']['format'];
283 // Move text value for user signature into 'signature'.
284 $form_state['values']['signature'] = $form_state['values']['signature']['value'];
285
286 $user_schema = drupal_get_schema('users');
287 if (drupal_strlen($form_state['values']['signature']) > $user_schema['fields']['signature']['length']) {
288 form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
289 }
290 }
291 }
292 }