3 namespace Drupal\user\Entity;
5 use Drupal\Core\Entity\ContentEntityBase;
6 use Drupal\Core\Entity\EntityChangedTrait;
7 use Drupal\Core\Entity\EntityStorageInterface;
8 use Drupal\Core\Entity\EntityTypeInterface;
9 use Drupal\Core\Field\BaseFieldDefinition;
10 use Drupal\Core\Language\LanguageInterface;
11 use Drupal\user\RoleInterface;
12 use Drupal\user\UserInterface;
15 * Defines the user entity class.
17 * The base table name here is plural, despite Drupal table naming standards,
18 * because "user" is a reserved word in many databases.
22 * label = @Translation("User"),
24 * "storage" = "Drupal\user\UserStorage",
25 * "storage_schema" = "Drupal\user\UserStorageSchema",
26 * "access" = "Drupal\user\UserAccessControlHandler",
27 * "list_builder" = "Drupal\user\UserListBuilder",
28 * "views_data" = "Drupal\user\UserViewsData",
29 * "route_provider" = {
30 * "html" = "Drupal\user\Entity\UserRouteProvider",
33 * "default" = "Drupal\user\ProfileForm",
34 * "cancel" = "Drupal\user\Form\UserCancelForm",
35 * "register" = "Drupal\user\RegisterForm"
37 * "translation" = "Drupal\user\ProfileTranslationHandler"
39 * admin_permission = "administer users",
40 * base_table = "users",
41 * data_table = "users_field_data",
42 * label_callback = "user_format_name",
43 * translatable = TRUE,
46 * "langcode" = "langcode",
50 * "canonical" = "/user/{user}",
51 * "edit-form" = "/user/{user}/edit",
52 * "cancel-form" = "/user/{user}/cancel",
53 * "collection" = "/admin/people",
55 * field_ui_base_route = "entity.user.admin_form",
56 * common_reference_target = TRUE
59 class User extends ContentEntityBase implements UserInterface {
61 use EntityChangedTrait;
64 * Stores a reference for a reusable anonymous user entity.
66 * @var \Drupal\user\UserInterface
68 protected static $anonymousUser;
73 public function isNew() {
74 return !empty($this->enforceIsNew) || $this->id() === NULL;
80 public function preSave(EntityStorageInterface $storage) {
81 parent::preSave($storage);
83 // Make sure that the authenticated/anonymous roles are not persisted.
84 foreach ($this->get('roles') as $index => $item) {
85 if (in_array($item->target_id, [RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID])) {
86 $this->get('roles')->offsetUnset($index);
90 // Store account cancellation information.
91 foreach (['user_cancel_method', 'user_cancel_notify'] as $key) {
92 if (isset($this->{$key})) {
93 \Drupal::service('user.data')->set('user', $this->id(), substr($key, 5), $this->{$key});
101 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
102 parent::postSave($storage, $update);
105 $session_manager = \Drupal::service('session_manager');
106 // If the password has been changed, delete all open sessions for the
107 // user and recreate the current one.
108 if ($this->pass->value != $this->original->pass->value) {
109 $session_manager->delete($this->id());
110 if ($this->id() == \Drupal::currentUser()->id()) {
111 \Drupal::service('session')->migrate();
115 // If the user was blocked, delete the user's sessions to force a logout.
116 if ($this->original->status->value != $this->status->value && $this->status->value == 0) {
117 $session_manager->delete($this->id());
120 // Send emails after we have the new user object.
121 if ($this->status->value != $this->original->status->value) {
122 // The user's status is changing; conditionally send notification email.
123 $op = $this->status->value == 1 ? 'status_activated' : 'status_blocked';
124 _user_mail_notify($op, $this);
132 public static function postDelete(EntityStorageInterface $storage, array $entities) {
133 parent::postDelete($storage, $entities);
135 $uids = array_keys($entities);
136 \Drupal::service('user.data')->delete(NULL, $uids);
142 public function getRoles($exclude_locked_roles = FALSE) {
145 // Users with an ID always have the authenticated user role.
146 if (!$exclude_locked_roles) {
147 if ($this->isAuthenticated()) {
148 $roles[] = RoleInterface::AUTHENTICATED_ID;
151 $roles[] = RoleInterface::ANONYMOUS_ID;
155 foreach ($this->get('roles') as $role) {
156 if ($role->target_id) {
157 $roles[] = $role->target_id;
167 public function hasRole($rid) {
168 return in_array($rid, $this->getRoles());
174 public function addRole($rid) {
176 if (in_array($rid, [RoleInterface::AUTHENTICATED_ID, RoleInterface::ANONYMOUS_ID])) {
177 throw new \InvalidArgumentException('Anonymous or authenticated role ID must not be assigned manually.');
180 $roles = $this->getRoles(TRUE);
182 $this->set('roles', array_unique($roles));
188 public function removeRole($rid) {
189 $this->set('roles', array_diff($this->getRoles(TRUE), [$rid]));
195 public function hasPermission($permission) {
196 // User #1 has all privileges.
197 if ((int) $this->id() === 1) {
201 return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles());
207 public function getPassword() {
208 return $this->get('pass')->value;
214 public function setPassword($password) {
215 $this->get('pass')->value = $password;
222 public function getEmail() {
223 return $this->get('mail')->value;
229 public function setEmail($mail) {
230 $this->get('mail')->value = $mail;
237 public function getCreatedTime() {
238 return $this->get('created')->value;
244 public function getLastAccessedTime() {
245 return $this->get('access')->value;
251 public function setLastAccessTime($timestamp) {
252 $this->get('access')->value = $timestamp;
259 public function getLastLoginTime() {
260 return $this->get('login')->value;
266 public function setLastLoginTime($timestamp) {
267 $this->get('login')->value = $timestamp;
274 public function isActive() {
275 return $this->get('status')->value == 1;
281 public function isBlocked() {
282 return $this->get('status')->value == 0;
288 public function activate() {
289 $this->get('status')->value = 1;
296 public function block() {
297 $this->get('status')->value = 0;
304 public function getTimeZone() {
305 return $this->get('timezone')->value;
311 public function getPreferredLangcode($fallback_to_default = TRUE) {
312 $language_list = $this->languageManager()->getLanguages();
313 $preferred_langcode = $this->get('preferred_langcode')->value;
314 if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) {
315 return $language_list[$preferred_langcode]->getId();
318 return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
325 public function getPreferredAdminLangcode($fallback_to_default = TRUE) {
326 $language_list = $this->languageManager()->getLanguages();
327 $preferred_langcode = $this->get('preferred_admin_langcode')->value;
328 if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) {
329 return $language_list[$preferred_langcode]->getId();
332 return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
339 public function getInitialEmail() {
340 return $this->get('init')->value;
346 public function isAuthenticated() {
347 return $this->id() > 0;
352 public function isAnonymous() {
353 return $this->id() == 0;
359 public function getUsername() {
360 return $this->getAccountName();
366 public function getAccountName() {
367 return $this->get('name')->value ?: '';
373 public function getDisplayName() {
374 $name = $this->getAccountName() ?: \Drupal::config('user.settings')->get('anonymous');
375 \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
382 public function setUsername($username) {
383 $this->set('name', $username);
390 public function setExistingPassword($password) {
391 $this->get('pass')->existing = $password;
397 public function checkExistingPassword(UserInterface $account_unchanged) {
398 return strlen($this->get('pass')->existing) > 0 && \Drupal::service('password')->check(trim($this->get('pass')->existing), $account_unchanged->getPassword());
402 * Returns an anonymous user entity.
404 * @return \Drupal\user\UserInterface
405 * An anonymous user entity.
407 public static function getAnonymousUser() {
408 if (!isset(static::$anonymousUser)) {
410 // @todo Use the entity factory once available, see
411 // https://www.drupal.org/node/1867228.
412 $entity_manager = \Drupal::entityManager();
413 $entity_type = $entity_manager->getDefinition('user');
414 $class = $entity_type->getClass();
416 static::$anonymousUser = new $class([
417 'uid' => [LanguageInterface::LANGCODE_DEFAULT => 0],
418 'name' => [LanguageInterface::LANGCODE_DEFAULT => ''],
419 // Explicitly set the langcode to ensure that field definitions do not
420 // need to be fetched to figure out a default.
421 'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED]
422 ], $entity_type->id());
424 return clone static::$anonymousUser;
430 public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
431 /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
432 $fields = parent::baseFieldDefinitions($entity_type);
434 $fields['uid']->setLabel(t('User ID'))
435 ->setDescription(t('The user ID.'));
437 $fields['uuid']->setDescription(t('The user UUID.'));
439 $fields['langcode']->setLabel(t('Language code'))
440 ->setDescription(t('The user language code.'))
441 ->setDisplayOptions('form', ['region' => 'hidden']);
443 $fields['preferred_langcode'] = BaseFieldDefinition::create('language')
444 ->setLabel(t('Preferred language code'))
445 ->setDescription(t("The user's preferred language code for receiving emails and viewing the site."))
446 // @todo: Define this via an options provider once
447 // https://www.drupal.org/node/2329937 is completed.
448 ->addPropertyConstraints('value', [
449 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'],
452 $fields['preferred_admin_langcode'] = BaseFieldDefinition::create('language')
453 ->setLabel(t('Preferred admin language code'))
454 ->setDescription(t("The user's preferred language code for viewing administration pages."))
455 // @todo: A default value of NULL is ignored, so we have to specify
456 // an empty field item structure instead. Fix this in
457 // https://www.drupal.org/node/2318605.
458 ->setDefaultValue([0 => ['value' => NULL]])
459 // @todo: Define this via an options provider once
460 // https://www.drupal.org/node/2329937 is completed.
461 ->addPropertyConstraints('value', [
462 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'],
465 // The name should not vary per language. The username is the visual
466 // identifier for a user and needs to be consistent in all languages.
467 $fields['name'] = BaseFieldDefinition::create('string')
468 ->setLabel(t('Name'))
469 ->setDescription(t('The name of this user.'))
472 // No Length constraint here because the UserName constraint also covers
475 'UserNameUnique' => [],
477 $fields['name']->getItemDefinition()->setClass('\Drupal\user\UserNameItem');
479 $fields['pass'] = BaseFieldDefinition::create('password')
480 ->setLabel(t('Password'))
481 ->setDescription(t('The password of this user (hashed).'))
482 ->addConstraint('ProtectedUserField');
484 $fields['mail'] = BaseFieldDefinition::create('email')
485 ->setLabel(t('Email'))
486 ->setDescription(t('The email of this user.'))
487 ->setDefaultValue('')
488 ->addConstraint('UserMailUnique')
489 ->addConstraint('UserMailRequired')
490 ->addConstraint('ProtectedUserField');
492 $fields['timezone'] = BaseFieldDefinition::create('string')
493 ->setLabel(t('Timezone'))
494 ->setDescription(t('The timezone of this user.'))
495 ->setSetting('max_length', 32)
496 // @todo: Define this via an options provider once
497 // https://www.drupal.org/node/2329937 is completed.
498 ->addPropertyConstraints('value', [
499 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedTimezones'],
502 $fields['status'] = BaseFieldDefinition::create('boolean')
503 ->setLabel(t('User status'))
504 ->setDescription(t('Whether the user is active or blocked.'))
505 ->setDefaultValue(FALSE);
507 $fields['created'] = BaseFieldDefinition::create('created')
508 ->setLabel(t('Created'))
509 ->setDescription(t('The time that the user was created.'));
511 $fields['changed'] = BaseFieldDefinition::create('changed')
512 ->setLabel(t('Changed'))
513 ->setDescription(t('The time that the user was last edited.'))
514 ->setTranslatable(TRUE);
516 $fields['access'] = BaseFieldDefinition::create('timestamp')
517 ->setLabel(t('Last access'))
518 ->setDescription(t('The time that the user last accessed the site.'))
519 ->setDefaultValue(0);
521 $fields['login'] = BaseFieldDefinition::create('timestamp')
522 ->setLabel(t('Last login'))
523 ->setDescription(t('The time that the user last logged in.'))
524 ->setDefaultValue(0);
526 $fields['init'] = BaseFieldDefinition::create('email')
527 ->setLabel(t('Initial email'))
528 ->setDescription(t('The email address used for initial account creation.'))
529 ->setDefaultValue('');
531 $fields['roles'] = BaseFieldDefinition::create('entity_reference')
532 ->setLabel(t('Roles'))
533 ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
534 ->setDescription(t('The roles the user has.'))
535 ->setSetting('target_type', 'user_role');
541 * Returns the role storage object.
543 * @return \Drupal\user\RoleStorageInterface
544 * The role storage object.
546 protected function getRoleStorage() {
547 return \Drupal::entityManager()->getStorage('user_role');
551 * Defines allowed timezones for the field's AllowedValues constraint.
554 * The allowed values.
556 public static function getAllowedTimezones() {
557 return array_keys(system_time_zones());
561 * Defines allowed configurable language codes for AllowedValues constraints.
564 * The allowed values.
566 public static function getAllowedConfigurableLanguageCodes() {
567 return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE));