Chris@0: enforceIsNew) || $this->id() === NULL; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function preSave(EntityStorageInterface $storage) { Chris@0: parent::preSave($storage); Chris@0: Chris@0: // Make sure that the authenticated/anonymous roles are not persisted. Chris@0: foreach ($this->get('roles') as $index => $item) { Chris@0: if (in_array($item->target_id, [RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID])) { Chris@0: $this->get('roles')->offsetUnset($index); Chris@0: } Chris@0: } Chris@0: Chris@0: // Store account cancellation information. Chris@0: foreach (['user_cancel_method', 'user_cancel_notify'] as $key) { Chris@0: if (isset($this->{$key})) { Chris@0: \Drupal::service('user.data')->set('user', $this->id(), substr($key, 5), $this->{$key}); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function postSave(EntityStorageInterface $storage, $update = TRUE) { Chris@0: parent::postSave($storage, $update); Chris@0: Chris@0: if ($update) { Chris@0: $session_manager = \Drupal::service('session_manager'); Chris@0: // If the password has been changed, delete all open sessions for the Chris@0: // user and recreate the current one. Chris@0: if ($this->pass->value != $this->original->pass->value) { Chris@0: $session_manager->delete($this->id()); Chris@0: if ($this->id() == \Drupal::currentUser()->id()) { Chris@0: \Drupal::service('session')->migrate(); Chris@0: } Chris@0: } Chris@0: Chris@0: // If the user was blocked, delete the user's sessions to force a logout. Chris@0: if ($this->original->status->value != $this->status->value && $this->status->value == 0) { Chris@0: $session_manager->delete($this->id()); Chris@0: } Chris@0: Chris@0: // Send emails after we have the new user object. Chris@0: if ($this->status->value != $this->original->status->value) { Chris@0: // The user's status is changing; conditionally send notification email. Chris@0: $op = $this->status->value == 1 ? 'status_activated' : 'status_blocked'; Chris@0: _user_mail_notify($op, $this); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public static function postDelete(EntityStorageInterface $storage, array $entities) { Chris@0: parent::postDelete($storage, $entities); Chris@0: Chris@0: $uids = array_keys($entities); Chris@0: \Drupal::service('user.data')->delete(NULL, $uids); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getRoles($exclude_locked_roles = FALSE) { Chris@0: $roles = []; Chris@0: Chris@0: // Users with an ID always have the authenticated user role. Chris@0: if (!$exclude_locked_roles) { Chris@0: if ($this->isAuthenticated()) { Chris@0: $roles[] = RoleInterface::AUTHENTICATED_ID; Chris@0: } Chris@0: else { Chris@0: $roles[] = RoleInterface::ANONYMOUS_ID; Chris@0: } Chris@0: } Chris@0: Chris@0: foreach ($this->get('roles') as $role) { Chris@0: if ($role->target_id) { Chris@0: $roles[] = $role->target_id; Chris@0: } Chris@0: } Chris@0: Chris@0: return $roles; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function hasRole($rid) { Chris@0: return in_array($rid, $this->getRoles()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function addRole($rid) { Chris@0: Chris@0: if (in_array($rid, [RoleInterface::AUTHENTICATED_ID, RoleInterface::ANONYMOUS_ID])) { Chris@0: throw new \InvalidArgumentException('Anonymous or authenticated role ID must not be assigned manually.'); Chris@0: } Chris@0: Chris@0: $roles = $this->getRoles(TRUE); Chris@0: $roles[] = $rid; Chris@0: $this->set('roles', array_unique($roles)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function removeRole($rid) { Chris@0: $this->set('roles', array_diff($this->getRoles(TRUE), [$rid])); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function hasPermission($permission) { Chris@0: // User #1 has all privileges. Chris@0: if ((int) $this->id() === 1) { Chris@0: return TRUE; Chris@0: } Chris@0: Chris@0: return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getPassword() { Chris@0: return $this->get('pass')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setPassword($password) { Chris@0: $this->get('pass')->value = $password; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getEmail() { Chris@0: return $this->get('mail')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setEmail($mail) { Chris@0: $this->get('mail')->value = $mail; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getCreatedTime() { Chris@0: return $this->get('created')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLastAccessedTime() { Chris@0: return $this->get('access')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setLastAccessTime($timestamp) { Chris@0: $this->get('access')->value = $timestamp; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getLastLoginTime() { Chris@0: return $this->get('login')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setLastLoginTime($timestamp) { Chris@0: $this->get('login')->value = $timestamp; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function isActive() { Chris@0: return $this->get('status')->value == 1; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function isBlocked() { Chris@0: return $this->get('status')->value == 0; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function activate() { Chris@0: $this->get('status')->value = 1; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function block() { Chris@0: $this->get('status')->value = 0; Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getTimeZone() { Chris@0: return $this->get('timezone')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getPreferredLangcode($fallback_to_default = TRUE) { Chris@0: $language_list = $this->languageManager()->getLanguages(); Chris@0: $preferred_langcode = $this->get('preferred_langcode')->value; Chris@0: if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) { Chris@0: return $language_list[$preferred_langcode]->getId(); Chris@0: } Chris@0: else { Chris@0: return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : ''; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getPreferredAdminLangcode($fallback_to_default = TRUE) { Chris@0: $language_list = $this->languageManager()->getLanguages(); Chris@0: $preferred_langcode = $this->get('preferred_admin_langcode')->value; Chris@0: if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) { Chris@0: return $language_list[$preferred_langcode]->getId(); Chris@0: } Chris@0: else { Chris@0: return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : ''; Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getInitialEmail() { Chris@0: return $this->get('init')->value; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function isAuthenticated() { Chris@0: return $this->id() > 0; Chris@0: } Chris@17: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function isAnonymous() { Chris@0: return $this->id() == 0; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getUsername() { Chris@18: @trigger_error('\Drupal\Core\Session\AccountInterface::getUsername() is deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. Use \Drupal\Core\Session\AccountInterface::getAccountName() or \Drupal\user\UserInterface::getDisplayName() instead. See https://www.drupal.org/node/2572493', E_USER_DEPRECATED); Chris@0: return $this->getAccountName(); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getAccountName() { Chris@0: return $this->get('name')->value ?: ''; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function getDisplayName() { Chris@0: $name = $this->getAccountName() ?: \Drupal::config('user.settings')->get('anonymous'); Chris@0: \Drupal::moduleHandler()->alter('user_format_name', $name, $this); Chris@0: return $name; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setUsername($username) { Chris@0: $this->set('name', $username); Chris@0: return $this; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function setExistingPassword($password) { Chris@0: $this->get('pass')->existing = $password; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function checkExistingPassword(UserInterface $account_unchanged) { Chris@0: return strlen($this->get('pass')->existing) > 0 && \Drupal::service('password')->check(trim($this->get('pass')->existing), $account_unchanged->getPassword()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns an anonymous user entity. Chris@0: * Chris@0: * @return \Drupal\user\UserInterface Chris@0: * An anonymous user entity. Chris@0: */ Chris@0: public static function getAnonymousUser() { Chris@0: if (!isset(static::$anonymousUser)) { Chris@0: Chris@0: // @todo Use the entity factory once available, see Chris@0: // https://www.drupal.org/node/1867228. Chris@0: $entity_manager = \Drupal::entityManager(); Chris@0: $entity_type = $entity_manager->getDefinition('user'); Chris@0: $class = $entity_type->getClass(); Chris@0: Chris@0: static::$anonymousUser = new $class([ Chris@0: 'uid' => [LanguageInterface::LANGCODE_DEFAULT => 0], Chris@0: 'name' => [LanguageInterface::LANGCODE_DEFAULT => ''], Chris@0: // Explicitly set the langcode to ensure that field definitions do not Chris@0: // need to be fetched to figure out a default. Chris@17: 'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED], Chris@0: ], $entity_type->id()); Chris@0: } Chris@0: return clone static::$anonymousUser; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { Chris@0: /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */ Chris@0: $fields = parent::baseFieldDefinitions($entity_type); Chris@0: Chris@0: $fields['uid']->setLabel(t('User ID')) Chris@0: ->setDescription(t('The user ID.')); Chris@0: Chris@0: $fields['uuid']->setDescription(t('The user UUID.')); Chris@0: Chris@0: $fields['langcode']->setLabel(t('Language code')) Chris@0: ->setDescription(t('The user language code.')) Chris@0: ->setDisplayOptions('form', ['region' => 'hidden']); Chris@0: Chris@0: $fields['preferred_langcode'] = BaseFieldDefinition::create('language') Chris@0: ->setLabel(t('Preferred language code')) Chris@0: ->setDescription(t("The user's preferred language code for receiving emails and viewing the site.")) Chris@0: // @todo: Define this via an options provider once Chris@0: // https://www.drupal.org/node/2329937 is completed. Chris@0: ->addPropertyConstraints('value', [ Chris@0: 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'], Chris@0: ]); Chris@0: Chris@0: $fields['preferred_admin_langcode'] = BaseFieldDefinition::create('language') Chris@0: ->setLabel(t('Preferred admin language code')) Chris@0: ->setDescription(t("The user's preferred language code for viewing administration pages.")) Chris@0: // @todo: A default value of NULL is ignored, so we have to specify Chris@0: // an empty field item structure instead. Fix this in Chris@0: // https://www.drupal.org/node/2318605. Chris@0: ->setDefaultValue([0 => ['value' => NULL]]) Chris@0: // @todo: Define this via an options provider once Chris@0: // https://www.drupal.org/node/2329937 is completed. Chris@0: ->addPropertyConstraints('value', [ Chris@0: 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'], Chris@0: ]); Chris@0: Chris@0: // The name should not vary per language. The username is the visual Chris@0: // identifier for a user and needs to be consistent in all languages. Chris@0: $fields['name'] = BaseFieldDefinition::create('string') Chris@0: ->setLabel(t('Name')) Chris@0: ->setDescription(t('The name of this user.')) Chris@0: ->setRequired(TRUE) Chris@0: ->setConstraints([ Chris@0: // No Length constraint here because the UserName constraint also covers Chris@0: // that. Chris@0: 'UserName' => [], Chris@0: 'UserNameUnique' => [], Chris@0: ]); Chris@0: $fields['name']->getItemDefinition()->setClass('\Drupal\user\UserNameItem'); Chris@0: Chris@0: $fields['pass'] = BaseFieldDefinition::create('password') Chris@0: ->setLabel(t('Password')) Chris@0: ->setDescription(t('The password of this user (hashed).')) Chris@0: ->addConstraint('ProtectedUserField'); Chris@0: Chris@0: $fields['mail'] = BaseFieldDefinition::create('email') Chris@0: ->setLabel(t('Email')) Chris@0: ->setDescription(t('The email of this user.')) Chris@0: ->setDefaultValue('') Chris@0: ->addConstraint('UserMailUnique') Chris@0: ->addConstraint('UserMailRequired') Chris@0: ->addConstraint('ProtectedUserField'); Chris@0: Chris@0: $fields['timezone'] = BaseFieldDefinition::create('string') Chris@0: ->setLabel(t('Timezone')) Chris@0: ->setDescription(t('The timezone of this user.')) Chris@0: ->setSetting('max_length', 32) Chris@0: // @todo: Define this via an options provider once Chris@0: // https://www.drupal.org/node/2329937 is completed. Chris@0: ->addPropertyConstraints('value', [ Chris@0: 'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedTimezones'], Chris@0: ]); Chris@14: $fields['timezone']->getItemDefinition()->setClass(TimeZoneItem::class); Chris@0: Chris@0: $fields['status'] = BaseFieldDefinition::create('boolean') Chris@0: ->setLabel(t('User status')) Chris@0: ->setDescription(t('Whether the user is active or blocked.')) Chris@0: ->setDefaultValue(FALSE); Chris@14: $fields['status']->getItemDefinition()->setClass(StatusItem::class); Chris@0: Chris@0: $fields['created'] = BaseFieldDefinition::create('created') Chris@0: ->setLabel(t('Created')) Chris@0: ->setDescription(t('The time that the user was created.')); Chris@0: Chris@0: $fields['changed'] = BaseFieldDefinition::create('changed') Chris@0: ->setLabel(t('Changed')) Chris@0: ->setDescription(t('The time that the user was last edited.')) Chris@0: ->setTranslatable(TRUE); Chris@0: Chris@0: $fields['access'] = BaseFieldDefinition::create('timestamp') Chris@0: ->setLabel(t('Last access')) Chris@0: ->setDescription(t('The time that the user last accessed the site.')) Chris@0: ->setDefaultValue(0); Chris@0: Chris@0: $fields['login'] = BaseFieldDefinition::create('timestamp') Chris@0: ->setLabel(t('Last login')) Chris@0: ->setDescription(t('The time that the user last logged in.')) Chris@0: ->setDefaultValue(0); Chris@0: Chris@0: $fields['init'] = BaseFieldDefinition::create('email') Chris@0: ->setLabel(t('Initial email')) Chris@0: ->setDescription(t('The email address used for initial account creation.')) Chris@0: ->setDefaultValue(''); Chris@0: Chris@0: $fields['roles'] = BaseFieldDefinition::create('entity_reference') Chris@0: ->setLabel(t('Roles')) Chris@0: ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED) Chris@0: ->setDescription(t('The roles the user has.')) Chris@0: ->setSetting('target_type', 'user_role'); Chris@0: Chris@0: return $fields; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Returns the role storage object. Chris@0: * Chris@0: * @return \Drupal\user\RoleStorageInterface Chris@0: * The role storage object. Chris@0: */ Chris@0: protected function getRoleStorage() { Chris@0: return \Drupal::entityManager()->getStorage('user_role'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Defines allowed timezones for the field's AllowedValues constraint. Chris@0: * Chris@0: * @return string[] Chris@0: * The allowed values. Chris@0: */ Chris@0: public static function getAllowedTimezones() { Chris@0: return array_keys(system_time_zones()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Defines allowed configurable language codes for AllowedValues constraints. Chris@0: * Chris@0: * @return string[] Chris@0: * The allowed values. Chris@0: */ Chris@0: public static function getAllowedConfigurableLanguageCodes() { Chris@0: return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE)); Chris@0: } Chris@0: Chris@0: }