Version 1
[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\UserInterface;
13
14 /**
15  * Defines the user entity class.
16  *
17  * The base table name here is plural, despite Drupal table naming standards,
18  * because "user" is a reserved word in many databases.
19  *
20  * @ContentEntityType(
21  *   id = "user",
22  *   label = @Translation("User"),
23  *   handlers = {
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",
31  *     },
32  *     "form" = {
33  *       "default" = "Drupal\user\ProfileForm",
34  *       "cancel" = "Drupal\user\Form\UserCancelForm",
35  *       "register" = "Drupal\user\RegisterForm"
36  *     },
37  *     "translation" = "Drupal\user\ProfileTranslationHandler"
38  *   },
39  *   admin_permission = "administer users",
40  *   base_table = "users",
41  *   data_table = "users_field_data",
42  *   label_callback = "user_format_name",
43  *   translatable = TRUE,
44  *   entity_keys = {
45  *     "id" = "uid",
46  *     "langcode" = "langcode",
47  *     "uuid" = "uuid"
48  *   },
49  *   links = {
50  *     "canonical" = "/user/{user}",
51  *     "edit-form" = "/user/{user}/edit",
52  *     "cancel-form" = "/user/{user}/cancel",
53  *     "collection" = "/admin/people",
54  *   },
55  *   field_ui_base_route = "entity.user.admin_form",
56  *   common_reference_target = TRUE
57  * )
58  */
59 class User extends ContentEntityBase implements UserInterface {
60
61   use EntityChangedTrait;
62
63   /**
64    * Stores a reference for a reusable anonymous user entity.
65    *
66    * @var \Drupal\user\UserInterface
67    */
68   protected static $anonymousUser;
69
70   /**
71    * {@inheritdoc}
72    */
73   public function isNew() {
74     return !empty($this->enforceIsNew) || $this->id() === NULL;
75   }
76
77   /**
78    * {@inheritdoc}
79    */
80   public function preSave(EntityStorageInterface $storage) {
81     parent::preSave($storage);
82
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);
87       }
88     }
89
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});
94       }
95     }
96   }
97
98   /**
99    * {@inheritdoc}
100    */
101   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
102     parent::postSave($storage, $update);
103
104     if ($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();
112         }
113       }
114
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());
118       }
119
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);
125       }
126     }
127   }
128
129   /**
130    * {@inheritdoc}
131    */
132   public static function postDelete(EntityStorageInterface $storage, array $entities) {
133     parent::postDelete($storage, $entities);
134
135     $uids = array_keys($entities);
136     \Drupal::service('user.data')->delete(NULL, $uids);
137   }
138
139   /**
140    * {@inheritdoc}
141    */
142   public function getRoles($exclude_locked_roles = FALSE) {
143     $roles = [];
144
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;
149       }
150       else {
151         $roles[] = RoleInterface::ANONYMOUS_ID;
152       }
153     }
154
155     foreach ($this->get('roles') as $role) {
156       if ($role->target_id) {
157         $roles[] = $role->target_id;
158       }
159     }
160
161     return $roles;
162   }
163
164   /**
165    * {@inheritdoc}
166    */
167   public function hasRole($rid) {
168     return in_array($rid, $this->getRoles());
169   }
170
171   /**
172    * {@inheritdoc}
173    */
174   public function addRole($rid) {
175
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.');
178     }
179
180     $roles = $this->getRoles(TRUE);
181     $roles[] = $rid;
182     $this->set('roles', array_unique($roles));
183   }
184
185   /**
186    * {@inheritdoc}
187    */
188   public function removeRole($rid) {
189     $this->set('roles', array_diff($this->getRoles(TRUE), [$rid]));
190   }
191
192   /**
193    * {@inheritdoc}
194    */
195   public function hasPermission($permission) {
196     // User #1 has all privileges.
197     if ((int) $this->id() === 1) {
198       return TRUE;
199     }
200
201     return $this->getRoleStorage()->isPermissionInRoles($permission, $this->getRoles());
202   }
203
204   /**
205    * {@inheritdoc}
206    */
207   public function getPassword() {
208     return $this->get('pass')->value;
209   }
210
211   /**
212    * {@inheritdoc}
213    */
214   public function setPassword($password) {
215     $this->get('pass')->value = $password;
216     return $this;
217   }
218
219   /**
220    * {@inheritdoc}
221    */
222   public function getEmail() {
223     return $this->get('mail')->value;
224   }
225
226   /**
227    * {@inheritdoc}
228    */
229   public function setEmail($mail) {
230     $this->get('mail')->value = $mail;
231     return $this;
232   }
233
234   /**
235    * {@inheritdoc}
236    */
237   public function getCreatedTime() {
238     return $this->get('created')->value;
239   }
240
241   /**
242    * {@inheritdoc}
243    */
244   public function getLastAccessedTime() {
245     return $this->get('access')->value;
246   }
247
248   /**
249    * {@inheritdoc}
250    */
251   public function setLastAccessTime($timestamp) {
252     $this->get('access')->value = $timestamp;
253     return $this;
254   }
255
256   /**
257    * {@inheritdoc}
258    */
259   public function getLastLoginTime() {
260     return $this->get('login')->value;
261   }
262
263   /**
264    * {@inheritdoc}
265    */
266   public function setLastLoginTime($timestamp) {
267     $this->get('login')->value = $timestamp;
268     return $this;
269   }
270
271   /**
272    * {@inheritdoc}
273    */
274   public function isActive() {
275     return $this->get('status')->value == 1;
276   }
277
278   /**
279    * {@inheritdoc}
280    */
281   public function isBlocked() {
282     return $this->get('status')->value == 0;
283   }
284
285   /**
286    * {@inheritdoc}
287    */
288   public function activate() {
289     $this->get('status')->value = 1;
290     return $this;
291   }
292
293   /**
294    * {@inheritdoc}
295    */
296   public function block() {
297     $this->get('status')->value = 0;
298     return $this;
299   }
300
301   /**
302    * {@inheritdoc}
303    */
304   public function getTimeZone() {
305     return $this->get('timezone')->value;
306   }
307
308   /**
309    * {@inheritdoc}
310    */
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();
316     }
317     else {
318       return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
319     }
320   }
321
322   /**
323    * {@inheritdoc}
324    */
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();
330     }
331     else {
332       return $fallback_to_default ? $this->languageManager()->getDefaultLanguage()->getId() : '';
333     }
334   }
335
336   /**
337    * {@inheritdoc}
338    */
339   public function getInitialEmail() {
340     return $this->get('init')->value;
341   }
342
343   /**
344    * {@inheritdoc}
345    */
346   public function isAuthenticated() {
347     return $this->id() > 0;
348   }
349   /**
350    * {@inheritdoc}
351    */
352   public function isAnonymous() {
353     return $this->id() == 0;
354   }
355
356   /**
357    * {@inheritdoc}
358    */
359   public function getUsername() {
360     return $this->getAccountName();
361   }
362
363   /**
364    * {@inheritdoc}
365    */
366   public function getAccountName() {
367     return $this->get('name')->value ?: '';
368   }
369
370   /**
371    * {@inheritdoc}
372    */
373   public function getDisplayName() {
374     $name = $this->getAccountName() ?: \Drupal::config('user.settings')->get('anonymous');
375     \Drupal::moduleHandler()->alter('user_format_name', $name, $this);
376     return $name;
377   }
378
379   /**
380    * {@inheritdoc}
381    */
382   public function setUsername($username) {
383     $this->set('name', $username);
384     return $this;
385   }
386
387   /**
388    * {@inheritdoc}
389    */
390   public function setExistingPassword($password) {
391     $this->get('pass')->existing = $password;
392   }
393
394   /**
395    * {@inheritdoc}
396    */
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());
399   }
400
401   /**
402    * Returns an anonymous user entity.
403    *
404    * @return \Drupal\user\UserInterface
405    *   An anonymous user entity.
406    */
407   public static function getAnonymousUser() {
408     if (!isset(static::$anonymousUser)) {
409
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();
415
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());
423     }
424     return clone static::$anonymousUser;
425   }
426
427   /**
428    * {@inheritdoc}
429    */
430   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
431     /** @var \Drupal\Core\Field\BaseFieldDefinition[] $fields */
432     $fields = parent::baseFieldDefinitions($entity_type);
433
434     $fields['uid']->setLabel(t('User ID'))
435       ->setDescription(t('The user ID.'));
436
437     $fields['uuid']->setDescription(t('The user UUID.'));
438
439     $fields['langcode']->setLabel(t('Language code'))
440       ->setDescription(t('The user language code.'))
441       ->setDisplayOptions('form', ['region' => 'hidden']);
442
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'],
450       ]);
451
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'],
463       ]);
464
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.'))
470       ->setRequired(TRUE)
471       ->setConstraints([
472         // No Length constraint here because the UserName constraint also covers
473         // that.
474         'UserName' => [],
475         'UserNameUnique' => [],
476       ]);
477     $fields['name']->getItemDefinition()->setClass('\Drupal\user\UserNameItem');
478
479     $fields['pass'] = BaseFieldDefinition::create('password')
480       ->setLabel(t('Password'))
481       ->setDescription(t('The password of this user (hashed).'))
482       ->addConstraint('ProtectedUserField');
483
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');
491
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'],
500       ]);
501
502     $fields['status'] = BaseFieldDefinition::create('boolean')
503       ->setLabel(t('User status'))
504       ->setDescription(t('Whether the user is active or blocked.'))
505       ->setDefaultValue(FALSE);
506
507     $fields['created'] = BaseFieldDefinition::create('created')
508       ->setLabel(t('Created'))
509       ->setDescription(t('The time that the user was created.'));
510
511     $fields['changed'] = BaseFieldDefinition::create('changed')
512       ->setLabel(t('Changed'))
513       ->setDescription(t('The time that the user was last edited.'))
514       ->setTranslatable(TRUE);
515
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);
520
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);
525
526     $fields['init'] = BaseFieldDefinition::create('email')
527       ->setLabel(t('Initial email'))
528       ->setDescription(t('The email address used for initial account creation.'))
529       ->setDefaultValue('');
530
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');
536
537     return $fields;
538   }
539
540   /**
541    * Returns the role storage object.
542    *
543    * @return \Drupal\user\RoleStorageInterface
544    *   The role storage object.
545    */
546   protected function getRoleStorage() {
547     return \Drupal::entityManager()->getStorage('user_role');
548   }
549
550   /**
551    * Defines allowed timezones for the field's AllowedValues constraint.
552    *
553    * @return string[]
554    *   The allowed values.
555    */
556   public static function getAllowedTimezones() {
557     return array_keys(system_time_zones());
558   }
559
560   /**
561    * Defines allowed configurable language codes for AllowedValues constraints.
562    *
563    * @return string[]
564    *   The allowed values.
565    */
566   public static function getAllowedConfigurableLanguageCodes() {
567     return array_keys(\Drupal::languageManager()->getLanguages(LanguageInterface::STATE_CONFIGURABLE));
568   }
569
570 }