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