Updated Drupal to 8.6. This goes with the following updates because it's possible...
[yaffs-website] / web / core / modules / user / src / Entity / User.php
1 <?php
2
3 namespace Drupal\user\Entity;
4
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\StatusItem;
13 use Drupal\user\TimeZoneItem;
14 use Drupal\user\UserInterface;
15
16 /**
17  * Defines the user entity class.
18  *
19  * The base table name here is plural, despite Drupal table naming standards,
20  * because "user" is a reserved word in many databases.
21  *
22  * @ContentEntityType(
23  *   id = "user",
24  *   label = @Translation("User"),
25  *   label_collection = @Translation("Users"),
26  *   label_singular = @Translation("user"),
27  *   label_plural = @Translation("users"),
28  *   label_count = @PluralTranslation(
29  *     singular = "@count user",
30  *     plural = "@count users",
31  *   ),
32  *   handlers = {
33  *     "storage" = "Drupal\user\UserStorage",
34  *     "storage_schema" = "Drupal\user\UserStorageSchema",
35  *     "access" = "Drupal\user\UserAccessControlHandler",
36  *     "list_builder" = "Drupal\user\UserListBuilder",
37  *     "views_data" = "Drupal\user\UserViewsData",
38  *     "route_provider" = {
39  *       "html" = "Drupal\user\Entity\UserRouteProvider",
40  *     },
41  *     "form" = {
42  *       "default" = "Drupal\user\ProfileForm",
43  *       "cancel" = "Drupal\user\Form\UserCancelForm",
44  *       "register" = "Drupal\user\RegisterForm"
45  *     },
46  *     "translation" = "Drupal\user\ProfileTranslationHandler"
47  *   },
48  *   admin_permission = "administer users",
49  *   base_table = "users",
50  *   data_table = "users_field_data",
51  *   label_callback = "user_format_name",
52  *   translatable = TRUE,
53  *   entity_keys = {
54  *     "id" = "uid",
55  *     "langcode" = "langcode",
56  *     "uuid" = "uuid"
57  *   },
58  *   links = {
59  *     "canonical" = "/user/{user}",
60  *     "edit-form" = "/user/{user}/edit",
61  *     "cancel-form" = "/user/{user}/cancel",
62  *     "collection" = "/admin/people",
63  *   },
64  *   field_ui_base_route = "entity.user.admin_form",
65  *   common_reference_target = TRUE
66  * )
67  */
68 class User extends ContentEntityBase implements UserInterface {
69
70   use EntityChangedTrait;
71
72   /**
73    * Stores a reference for a reusable anonymous user entity.
74    *
75    * @var \Drupal\user\UserInterface
76    */
77   protected static $anonymousUser;
78
79   /**
80    * {@inheritdoc}
81    */
82   public function isNew() {
83     return !empty($this->enforceIsNew) || $this->id() === NULL;
84   }
85
86   /**
87    * {@inheritdoc}
88    */
89   public function preSave(EntityStorageInterface $storage) {
90     parent::preSave($storage);
91
92     // Make sure that the authenticated/anonymous roles are not persisted.
93     foreach ($this->get('roles') as $index => $item) {
94       if (in_array($item->target_id, [RoleInterface::ANONYMOUS_ID, RoleInterface::AUTHENTICATED_ID])) {
95         $this->get('roles')->offsetUnset($index);
96       }
97     }
98
99     // Store account cancellation information.
100     foreach (['user_cancel_method', 'user_cancel_notify'] as $key) {
101       if (isset($this->{$key})) {
102         \Drupal::service('user.data')->set('user', $this->id(), substr($key, 5), $this->{$key});
103       }
104     }
105   }
106
107   /**
108    * {@inheritdoc}
109    */
110   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
111     parent::postSave($storage, $update);
112
113     if ($update) {
114       $session_manager = \Drupal::service('session_manager');
115       // If the password has been changed, delete all open sessions for the
116       // user and recreate the current one.
117       if ($this->pass->value != $this->original->pass->value) {
118         $session_manager->delete($this->id());
119         if ($this->id() == \Drupal::currentUser()->id()) {
120           \Drupal::service('session')->migrate();
121         }
122       }
123
124       // If the user was blocked, delete the user's sessions to force a logout.
125       if ($this->original->status->value != $this->status->value && $this->status->value == 0) {
126         $session_manager->delete($this->id());
127       }
128
129       // Send emails after we have the new user object.
130       if ($this->status->value != $this->original->status->value) {
131         // The user's status is changing; conditionally send notification email.
132         $op = $this->status->value == 1 ? 'status_activated' : 'status_blocked';
133         _user_mail_notify($op, $this);
134       }
135     }
136   }
137
138   /**
139    * {@inheritdoc}
140    */
141   public static function postDelete(EntityStorageInterface $storage, array $entities) {
142     parent::postDelete($storage, $entities);
143
144     $uids = array_keys($entities);
145     \Drupal::service('user.data')->delete(NULL, $uids);
146   }
147
148   /**
149    * {@inheritdoc}
150    */
151   public function getRoles($exclude_locked_roles = FALSE) {
152     $roles = [];
153
154     // Users with an ID always have the authenticated user role.
155     if (!$exclude_locked_roles) {
156       if ($this->isAuthenticated()) {
157         $roles[] = RoleInterface::AUTHENTICATED_ID;
158       }
159       else {
160         $roles[] = RoleInterface::ANONYMOUS_ID;
161       }
162     }
163
164     foreach ($this->get('roles') as $role) {
165       if ($role->target_id) {
166         $roles[] = $role->target_id;
167       }
168     }
169
170     return $roles;
171   }
172
173   /**
174    * {@inheritdoc}
175    */
176   public function hasRole($rid) {
177     return in_array($rid, $this->getRoles());
178   }
179
180   /**
181    * {@inheritdoc}
182    */
183   public function addRole($rid) {
184
185     if (in_array($rid, [RoleInterface::AUTHENTICATED_ID, RoleInterface::ANONYMOUS_ID])) {
186       throw new \InvalidArgumentException('Anonymous or authenticated role ID must not be assigned manually.');
187     }
188
189     $roles = $this->getRoles(TRUE);
190     $roles[] = $rid;
191     $this->set('roles', array_unique($roles));
192   }
193
194   /**
195    * {@inheritdoc}
196    */
197   public function removeRole($rid) {
198     $this->set('roles', array_diff($this->getRoles(TRUE), [$rid]));
199   }
200
201   /**
202    * {@inheritdoc}
203    */
204   public function hasPermission($permission) {
205     // User #1 has all privileges.
206     if ((int) $this->id() === 1) {
207       return TRUE;
208     }
209
210     return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles());
211   }
212
213   /**
214    * {@inheritdoc}
215    */
216   public function getPassword() {
217     return $this->get('pass')->value;
218   }
219
220   /**
221    * {@inheritdoc}
222    */
223   public function setPassword($password) {
224     $this->get('pass')->value = $password;
225     return $this;
226   }
227
228   /**
229    * {@inheritdoc}
230    */
231   public function getEmail() {
232     return $this->get('mail')->value;
233   }
234
235   /**
236    * {@inheritdoc}
237    */
238   public function setEmail($mail) {
239     $this->get('mail')->value = $mail;
240     return $this;
241   }
242
243   /**
244    * {@inheritdoc}
245    */
246   public function getCreatedTime() {
247     return $this->get('created')->value;
248   }
249
250   /**
251    * {@inheritdoc}
252    */
253   public function getLastAccessedTime() {
254     return $this->get('access')->value;
255   }
256
257   /**
258    * {@inheritdoc}
259    */
260   public function setLastAccessTime($timestamp) {
261     $this->get('access')->value = $timestamp;
262     return $this;
263   }
264
265   /**
266    * {@inheritdoc}
267    */
268   public function getLastLoginTime() {
269     return $this->get('login')->value;
270   }
271
272   /**
273    * {@inheritdoc}
274    */
275   public function setLastLoginTime($timestamp) {
276     $this->get('login')->value = $timestamp;
277     return $this;
278   }
279
280   /**
281    * {@inheritdoc}
282    */
283   public function isActive() {
284     return $this->get('status')->value == 1;
285   }
286
287   /**
288    * {@inheritdoc}
289    */
290   public function isBlocked() {
291     return $this->get('status')->value == 0;
292   }
293
294   /**
295    * {@inheritdoc}
296    */
297   public function activate() {
298     $this->get('status')->value = 1;
299     return $this;
300   }
301
302   /**
303    * {@inheritdoc}
304    */
305   public function block() {
306     $this->get('status')->value = 0;
307     return $this;
308   }
309
310   /**
311    * {@inheritdoc}
312    */
313   public function getTimeZone() {
314     return $this->get('timezone')->value;
315   }
316
317   /**
318    * {@inheritdoc}
319    */
320   public function getPreferredLangcode($fallback_to_default = TRUE) {
321     $language_list = $this->languageManager()->getLanguages();
322     $preferred_langcode = $this->get('preferred_langcode')->value;
323     if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) {
324       return $language_list[$preferred_langcode]->getId();
325     }
326     else {
327       return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
328     }
329   }
330
331   /**
332    * {@inheritdoc}
333    */
334   public function getPreferredAdminLangcode($fallback_to_default = TRUE) {
335     $language_list = $this->languageManager()->getLanguages();
336     $preferred_langcode = $this->get('preferred_admin_langcode')->value;
337     if (!empty($preferred_langcode) && isset($language_list[$preferred_langcode])) {
338       return $language_list[$preferred_langcode]->getId();
339     }
340     else {
341       return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
342     }
343   }
344
345   /**
346    * {@inheritdoc}
347    */
348   public function getInitialEmail() {
349     return $this->get('init')->value;
350   }
351
352   /**
353    * {@inheritdoc}
354    */
355   public function isAuthenticated() {
356     return $this->id() > 0;
357   }
358
359   /**
360    * {@inheritdoc}
361    */
362   public function isAnonymous() {
363     return $this->id() == 0;
364   }
365
366   /**
367    * {@inheritdoc}
368    */
369   public function getUsername() {
370     return $this->getAccountName();
371   }
372
373   /**
374    * {@inheritdoc}
375    */
376   public function getAccountName() {
377     return $this->get('name')->value ?: '';
378   }
379
380   /**
381    * {@inheritdoc}
382    */
383   public function getDisplayName() {
384     $name = $this->getAccountName() ?: \Drupal::config('user.settings')->get('anonymous');
385     \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
386     return $name;
387   }
388
389   /**
390    * {@inheritdoc}
391    */
392   public function setUsername($username) {
393     $this->set('name', $username);
394     return $this;
395   }
396
397   /**
398    * {@inheritdoc}
399    */
400   public function setExistingPassword($password) {
401     $this->get('pass')->existing = $password;
402   }
403
404   /**
405    * {@inheritdoc}
406    */
407   public function checkExistingPassword(UserInterface $account_unchanged) {
408     return strlen($this->get('pass')->existing) > 0 && \Drupal::service('password')->check(trim($this->get('pass')->existing), $account_unchanged->getPassword());
409   }
410
411   /**
412    * Returns an anonymous user entity.
413    *
414    * @return \Drupal\user\UserInterface
415    *   An anonymous user entity.
416    */
417   public static function getAnonymousUser() {
418     if (!isset(static::$anonymousUser)) {
419
420       // @todo Use the entity factory once available, see
421       //   https://www.drupal.org/node/1867228.
422       $entity_manager = \Drupal::entityManager();
423       $entity_type = $entity_manager->getDefinition('user');
424       $class = $entity_type->getClass();
425
426       static::$anonymousUser = new $class([
427         'uid' => [LanguageInterface::LANGCODE_DEFAULT => 0],
428         'name' => [LanguageInterface::LANGCODE_DEFAULT => ''],
429         // Explicitly set the langcode to ensure that field definitions do not
430         // need to be fetched to figure out a default.
431         'langcode' => [LanguageInterface::LANGCODE_DEFAULT => LanguageInterface::LANGCODE_NOT_SPECIFIED],
432       ], $entity_type->id());
433     }
434     return clone static::$anonymousUser;
435   }
436
437   /**
438    * {@inheritdoc}
439    */
440   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
441     /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
442     $fields = parent::baseFieldDefinitions($entity_type);
443
444     $fields['uid']->setLabel(t('User ID'))
445       ->setDescription(t('The user ID.'));
446
447     $fields['uuid']->setDescription(t('The user UUID.'));
448
449     $fields['langcode']->setLabel(t('Language code'))
450       ->setDescription(t('The user language code.'))
451       ->setDisplayOptions('form', ['region' => 'hidden']);
452
453     $fields['preferred_langcode'] = BaseFieldDefinition::create('language')
454       ->setLabel(t('Preferred language code'))
455       ->setDescription(t("The user's preferred language code for receiving emails and viewing the site."))
456       // @todo: Define this via an options provider once
457       // https://www.drupal.org/node/2329937 is completed.
458       ->addPropertyConstraints('value', [
459         'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'],
460       ]);
461
462     $fields['preferred_admin_langcode'] = BaseFieldDefinition::create('language')
463       ->setLabel(t('Preferred admin language code'))
464       ->setDescription(t("The user's preferred language code for viewing administration pages."))
465       // @todo: A default value of NULL is ignored, so we have to specify
466       // an empty field item structure instead. Fix this in
467       // https://www.drupal.org/node/2318605.
468       ->setDefaultValue([0 => ['value' => NULL]])
469       // @todo: Define this via an options provider once
470       // https://www.drupal.org/node/2329937 is completed.
471       ->addPropertyConstraints('value', [
472         'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedConfigurableLanguageCodes'],
473       ]);
474
475     // The name should not vary per language. The username is the visual
476     // identifier for a user and needs to be consistent in all languages.
477     $fields['name'] = BaseFieldDefinition::create('string')
478       ->setLabel(t('Name'))
479       ->setDescription(t('The name of this user.'))
480       ->setRequired(TRUE)
481       ->setConstraints([
482         // No Length constraint here because the UserName constraint also covers
483         // that.
484         'UserName' => [],
485         'UserNameUnique' => [],
486       ]);
487     $fields['name']->getItemDefinition()->setClass('\Drupal\user\UserNameItem');
488
489     $fields['pass'] = BaseFieldDefinition::create('password')
490       ->setLabel(t('Password'))
491       ->setDescription(t('The password of this user (hashed).'))
492       ->addConstraint('ProtectedUserField');
493
494     $fields['mail'] = BaseFieldDefinition::create('email')
495       ->setLabel(t('Email'))
496       ->setDescription(t('The email of this user.'))
497       ->setDefaultValue('')
498       ->addConstraint('UserMailUnique')
499       ->addConstraint('UserMailRequired')
500       ->addConstraint('ProtectedUserField');
501
502     $fields['timezone'] = BaseFieldDefinition::create('string')
503       ->setLabel(t('Timezone'))
504       ->setDescription(t('The timezone of this user.'))
505       ->setSetting('max_length', 32)
506       // @todo: Define this via an options provider once
507       // https://www.drupal.org/node/2329937 is completed.
508       ->addPropertyConstraints('value', [
509         'AllowedValues' => ['callback' => __CLASS__ . '::getAllowedTimezones'],
510       ]);
511     $fields['timezone']->getItemDefinition()->setClass(TimeZoneItem::class);
512
513     $fields['status'] = BaseFieldDefinition::create('boolean')
514       ->setLabel(t('User status'))
515       ->setDescription(t('Whether the user is active or blocked.'))
516       ->setDefaultValue(FALSE);
517     $fields['status']->getItemDefinition()->setClass(StatusItem::class);
518
519     $fields['created'] = BaseFieldDefinition::create('created')
520       ->setLabel(t('Created'))
521       ->setDescription(t('The time that the user was created.'));
522
523     $fields['changed'] = BaseFieldDefinition::create('changed')
524       ->setLabel(t('Changed'))
525       ->setDescription(t('The time that the user was last edited.'))
526       ->setTranslatable(TRUE);
527
528     $fields['access'] = BaseFieldDefinition::create('timestamp')
529       ->setLabel(t('Last access'))
530       ->setDescription(t('The time that the user last accessed the site.'))
531       ->setDefaultValue(0);
532
533     $fields['login'] = BaseFieldDefinition::create('timestamp')
534       ->setLabel(t('Last login'))
535       ->setDescription(t('The time that the user last logged in.'))
536       ->setDefaultValue(0);
537
538     $fields['init'] = BaseFieldDefinition::create('email')
539       ->setLabel(t('Initial email'))
540       ->setDescription(t('The email address used for initial account creation.'))
541       ->setDefaultValue('');
542
543     $fields['roles'] = BaseFieldDefinition::create('entity_reference')
544       ->setLabel(t('Roles'))
545       ->setCardinality(BaseFieldDefinition::CARDINALITY_UNLIMITED)
546       ->setDescription(t('The roles the user has.'))
547       ->setSetting('target_type', 'user_role');
548
549     return $fields;
550   }
551
552   /**
553    * Returns the role storage object.
554    *
555    * @return \Drupal\user\RoleStorageInterface
556    *   The role storage object.
557    */
558   protected function getRoleStorage() {
559     return \Drupal::entityManager()->getStorage('user_role');
560   }
561
562   /**
563    * Defines allowed timezones for the field's AllowedValues constraint.
564    *
565    * @return string[]
566    *   The allowed values.
567    */
568   public static function getAllowedTimezones() {
569     return array_keys(system_time_zones());
570   }
571
572   /**
573    * Defines allowed configurable language codes for AllowedValues constraints.
574    *
575    * @return string[]
576    *   The allowed values.
577    */
578   public static function getAllowedConfigurableLanguageCodes() {
579     return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE));
580   }
581
582 }