]> git.mxchange.org Git - friendica.git/blob - src/Model/User.php
Logger Levels
[friendica.git] / src / Model / User.php
1 <?php
2 /**
3  * @file src/Model/User.php
4  * @brief This file includes the User class with user related database functions
5  */
6 namespace Friendica\Model;
7
8 use DivineOmega\PasswordExposed;
9 use Exception;
10 use Friendica\Core\Addon;
11 use Friendica\Core\Config;
12 use Friendica\Core\L10n;
13 use Friendica\Core\Logger;
14 use Friendica\Core\PConfig;
15 use Friendica\Core\Protocol;
16 use Friendica\Core\System;
17 use Friendica\Core\Worker;
18 use Friendica\Database\DBA;
19 use Friendica\Object\Image;
20 use Friendica\Util\Crypto;
21 use Friendica\Util\DateTimeFormat;
22 use Friendica\Util\Network;
23 use LightOpenID;
24
25 require_once 'boot.php';
26 require_once 'include/dba.php';
27 require_once 'include/enotify.php';
28 require_once 'include/text.php';
29 /**
30  * @brief This class handles User related functions
31  */
32 class User
33 {
34         /**
35          * Returns true if a user record exists with the provided id
36          *
37          * @param  integer $uid
38          * @return boolean
39          */
40         public static function exists($uid)
41         {
42                 return DBA::exists('user', ['uid' => $uid]);
43         }
44
45         /**
46          * @param  integer       $uid
47          * @return array|boolean User record if it exists, false otherwise
48          */
49         public static function getById($uid)
50         {
51                 return DBA::selectFirst('user', [], ['uid' => $uid]);
52         }
53
54         /**
55          * @brief Returns the user id of a given profile URL
56          *
57          * @param string $url
58          *
59          * @return integer user id
60          */
61         public static function getIdForURL($url)
62         {
63                 $self = DBA::selectFirst('contact', ['uid'], ['nurl' => normalise_link($url), 'self' => true]);
64                 if (!DBA::isResult($self)) {
65                         return false;
66                 } else {
67                         return $self['uid'];
68                 }
69         }
70
71         /**
72          * @brief Get owner data by user id
73          *
74          * @param int $uid
75          * @return boolean|array
76          */
77         public static function getOwnerDataById($uid) {
78                 $r = DBA::fetchFirst("SELECT
79                         `contact`.*,
80                         `user`.`prvkey` AS `uprvkey`,
81                         `user`.`timezone`,
82                         `user`.`nickname`,
83                         `user`.`sprvkey`,
84                         `user`.`spubkey`,
85                         `user`.`page-flags`,
86                         `user`.`account-type`,
87                         `user`.`prvnets`
88                         FROM `contact`
89                         INNER JOIN `user`
90                                 ON `user`.`uid` = `contact`.`uid`
91                         WHERE `contact`.`uid` = ?
92                         AND `contact`.`self`
93                         LIMIT 1",
94                         $uid
95                 );
96                 if (!DBA::isResult($r)) {
97                         return false;
98                 }
99                 return $r;
100         }
101
102         /**
103          * @brief Get owner data by nick name
104          *
105          * @param int $nick
106          * @return boolean|array
107          */
108         public static function getOwnerDataByNick($nick)
109         {
110                 $user = DBA::selectFirst('user', ['uid'], ['nickname' => $nick]);
111
112                 if (!DBA::isResult($user)) {
113                         return false;
114                 }
115
116                 return self::getOwnerDataById($user['uid']);
117         }
118
119         /**
120          * @brief Returns the default group for a given user and network
121          *
122          * @param int $uid User id
123          * @param string $network network name
124          *
125          * @return int group id
126          */
127         public static function getDefaultGroup($uid, $network = '')
128         {
129                 $default_group = 0;
130
131                 if ($network == Protocol::OSTATUS) {
132                         $default_group = PConfig::get($uid, "ostatus", "default_group");
133                 }
134
135                 if ($default_group != 0) {
136                         return $default_group;
137                 }
138
139                 $user = DBA::selectFirst('user', ['def_gid'], ['uid' => $uid]);
140
141                 if (DBA::isResult($user)) {
142                         $default_group = $user["def_gid"];
143                 }
144
145                 return $default_group;
146         }
147
148
149         /**
150          * Authenticate a user with a clear text password
151          *
152          * @brief Authenticate a user with a clear text password
153          * @param mixed $user_info
154          * @param string $password
155          * @return int|boolean
156          * @deprecated since version 3.6
157          * @see User::getIdFromPasswordAuthentication()
158          */
159         public static function authenticate($user_info, $password)
160         {
161                 try {
162                         return self::getIdFromPasswordAuthentication($user_info, $password);
163                 } catch (Exception $ex) {
164                         return false;
165                 }
166         }
167
168         /**
169          * Returns the user id associated with a successful password authentication
170          *
171          * @brief Authenticate a user with a clear text password
172          * @param mixed $user_info
173          * @param string $password
174          * @return int User Id if authentication is successful
175          * @throws Exception
176          */
177         public static function getIdFromPasswordAuthentication($user_info, $password)
178         {
179                 $user = self::getAuthenticationInfo($user_info);
180
181                 if (strpos($user['password'], '$') === false) {
182                         //Legacy hash that has not been replaced by a new hash yet
183                         if (self::hashPasswordLegacy($password) === $user['password']) {
184                                 self::updatePassword($user['uid'], $password);
185
186                                 return $user['uid'];
187                         }
188                 } elseif (!empty($user['legacy_password'])) {
189                         //Legacy hash that has been double-hashed and not replaced by a new hash yet
190                         //Warning: `legacy_password` is not necessary in sync with the content of `password`
191                         if (password_verify(self::hashPasswordLegacy($password), $user['password'])) {
192                                 self::updatePassword($user['uid'], $password);
193
194                                 return $user['uid'];
195                         }
196                 } elseif (password_verify($password, $user['password'])) {
197                         //New password hash
198                         if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) {
199                                 self::updatePassword($user['uid'], $password);
200                         }
201
202                         return $user['uid'];
203                 }
204
205                 throw new Exception(L10n::t('Login failed'));
206         }
207
208         /**
209          * Returns authentication info from various parameters types
210          *
211          * User info can be any of the following:
212          * - User DB object
213          * - User Id
214          * - User email or username or nickname
215          * - User array with at least the uid and the hashed password
216          *
217          * @param mixed $user_info
218          * @return array
219          * @throws Exception
220          */
221         private static function getAuthenticationInfo($user_info)
222         {
223                 $user = null;
224
225                 if (is_object($user_info) || is_array($user_info)) {
226                         if (is_object($user_info)) {
227                                 $user = (array) $user_info;
228                         } else {
229                                 $user = $user_info;
230                         }
231
232                         if (!isset($user['uid'])
233                                 || !isset($user['password'])
234                                 || !isset($user['legacy_password'])
235                         ) {
236                                 throw new Exception(L10n::t('Not enough information to authenticate'));
237                         }
238                 } elseif (is_int($user_info) || is_string($user_info)) {
239                         if (is_int($user_info)) {
240                                 $user = DBA::selectFirst('user', ['uid', 'password', 'legacy_password'],
241                                         [
242                                                 'uid' => $user_info,
243                                                 'blocked' => 0,
244                                                 'account_expired' => 0,
245                                                 'account_removed' => 0,
246                                                 'verified' => 1
247                                         ]
248                                 );
249                         } else {
250                                 $fields = ['uid', 'password', 'legacy_password'];
251                                 $condition = ["(`email` = ? OR `username` = ? OR `nickname` = ?)
252                                         AND NOT `blocked` AND NOT `account_expired` AND NOT `account_removed` AND `verified`",
253                                         $user_info, $user_info, $user_info];
254                                 $user = DBA::selectFirst('user', $fields, $condition);
255                         }
256
257                         if (!DBA::isResult($user)) {
258                                 throw new Exception(L10n::t('User not found'));
259                         }
260                 }
261
262                 return $user;
263         }
264
265         /**
266          * Generates a human-readable random password
267          *
268          * @return string
269          */
270         public static function generateNewPassword()
271         {
272                 return autoname(6) . mt_rand(100, 9999);
273         }
274
275         /**
276          * Checks if the provided plaintext password has been exposed or not
277          *
278          * @param string $password
279          * @return bool
280          */
281         public static function isPasswordExposed($password)
282         {
283                 $cache = new \DivineOmega\DOFileCachePSR6\CacheItemPool();
284                 $cache->changeConfig([
285                         'cacheDirectory' => get_temppath() . '/password-exposed-cache/',
286                 ]);
287
288                 $PasswordExposedCHecker = new PasswordExposed\PasswordExposedChecker(null, $cache);
289
290                 return $PasswordExposedCHecker->passwordExposed($password) === PasswordExposed\PasswordStatus::EXPOSED;
291         }
292
293         /**
294          * Legacy hashing function, kept for password migration purposes
295          *
296          * @param string $password
297          * @return string
298          */
299         private static function hashPasswordLegacy($password)
300         {
301                 return hash('whirlpool', $password);
302         }
303
304         /**
305          * Global user password hashing function
306          *
307          * @param string $password
308          * @return string
309          */
310         public static function hashPassword($password)
311         {
312                 if (!trim($password)) {
313                         throw new Exception(L10n::t('Password can\'t be empty'));
314                 }
315
316                 return password_hash($password, PASSWORD_DEFAULT);
317         }
318
319         /**
320          * Updates a user row with a new plaintext password
321          *
322          * @param int    $uid
323          * @param string $password
324          * @return bool
325          */
326         public static function updatePassword($uid, $password)
327         {
328                 return self::updatePasswordHashed($uid, self::hashPassword($password));
329         }
330
331         /**
332          * Updates a user row with a new hashed password.
333          * Empties the password reset token field just in case.
334          *
335          * @param int    $uid
336          * @param string $pasword_hashed
337          * @return bool
338          */
339         private static function updatePasswordHashed($uid, $pasword_hashed)
340         {
341                 $fields = [
342                         'password' => $pasword_hashed,
343                         'pwdreset' => null,
344                         'pwdreset_time' => null,
345                         'legacy_password' => false
346                 ];
347                 return DBA::update('user', $fields, ['uid' => $uid]);
348         }
349
350         /**
351          * @brief Checks if a nickname is in the list of the forbidden nicknames
352          *
353          * Check if a nickname is forbidden from registration on the node by the
354          * admin. Forbidden nicknames (e.g. role namess) can be configured in the
355          * admin panel.
356          *
357          * @param string $nickname The nickname that should be checked
358          * @return boolean True is the nickname is blocked on the node
359          */
360         public static function isNicknameBlocked($nickname)
361         {
362                 $forbidden_nicknames = Config::get('system', 'forbidden_nicknames', '');
363
364                 // if the config variable is empty return false
365                 if (empty($forbidden_nicknames)) {
366                         return false;
367                 }
368
369                 // check if the nickname is in the list of blocked nicknames
370                 $forbidden = explode(',', $forbidden_nicknames);
371                 $forbidden = array_map('trim', $forbidden);
372                 if (in_array(strtolower($nickname), $forbidden)) {
373                         return true;
374                 }
375
376                 // else return false
377                 return false;
378         }
379
380         /**
381          * @brief Catch-all user creation function
382          *
383          * Creates a user from the provided data array, either form fields or OpenID.
384          * Required: { username, nickname, email } or { openid_url }
385          *
386          * Performs the following:
387          * - Sends to the OpenId auth URL (if relevant)
388          * - Creates new key pairs for crypto
389          * - Create self-contact
390          * - Create profile image
391          *
392          * @param array $data
393          * @return string
394          * @throw Exception
395          */
396         public static function create(array $data)
397         {
398                 $a = get_app();
399                 $return = ['user' => null, 'password' => ''];
400
401                 $using_invites = Config::get('system', 'invitation_only');
402                 $num_invites   = Config::get('system', 'number_invites');
403
404                 $invite_id  = !empty($data['invite_id'])  ? notags(trim($data['invite_id']))  : '';
405                 $username   = !empty($data['username'])   ? notags(trim($data['username']))   : '';
406                 $nickname   = !empty($data['nickname'])   ? notags(trim($data['nickname']))   : '';
407                 $email      = !empty($data['email'])      ? notags(trim($data['email']))      : '';
408                 $openid_url = !empty($data['openid_url']) ? notags(trim($data['openid_url'])) : '';
409                 $photo      = !empty($data['photo'])      ? notags(trim($data['photo']))      : '';
410                 $password   = !empty($data['password'])   ? trim($data['password'])           : '';
411                 $password1  = !empty($data['password1'])  ? trim($data['password1'])          : '';
412                 $confirm    = !empty($data['confirm'])    ? trim($data['confirm'])            : '';
413                 $blocked    = !empty($data['blocked'])    ? intval($data['blocked'])          : 0;
414                 $verified   = !empty($data['verified'])   ? intval($data['verified'])         : 0;
415                 $language   = !empty($data['language'])   ? notags(trim($data['language']))   : 'en';
416
417                 $publish = !empty($data['profile_publish_reg']) && intval($data['profile_publish_reg']) ? 1 : 0;
418                 $netpublish = strlen(Config::get('system', 'directory')) ? $publish : 0;
419
420                 if ($password1 != $confirm) {
421                         throw new Exception(L10n::t('Passwords do not match. Password unchanged.'));
422                 } elseif ($password1 != '') {
423                         $password = $password1;
424                 }
425
426                 if ($using_invites) {
427                         if (!$invite_id) {
428                                 throw new Exception(L10n::t('An invitation is required.'));
429                         }
430
431                         if (!Register::existsByHash($invite_id)) {
432                                 throw new Exception(L10n::t('Invitation could not be verified.'));
433                         }
434                 }
435
436                 if (empty($username) || empty($email) || empty($nickname)) {
437                         if ($openid_url) {
438                                 if (!Network::isUrlValid($openid_url)) {
439                                         throw new Exception(L10n::t('Invalid OpenID url'));
440                                 }
441                                 $_SESSION['register'] = 1;
442                                 $_SESSION['openid'] = $openid_url;
443
444                                 $openid = new LightOpenID($a->getHostName());
445                                 $openid->identity = $openid_url;
446                                 $openid->returnUrl = System::baseUrl() . '/openid';
447                                 $openid->required = ['namePerson/friendly', 'contact/email', 'namePerson'];
448                                 $openid->optional = ['namePerson/first', 'media/image/aspect11', 'media/image/default'];
449                                 try {
450                                         $authurl = $openid->authUrl();
451                                 } catch (Exception $e) {
452                                         throw new Exception(L10n::t('We encountered a problem while logging in with the OpenID you provided. Please check the correct spelling of the ID.') . EOL . EOL . L10n::t('The error message was:') . $e->getMessage(), 0, $e);
453                                 }
454                                 System::externalRedirect($authurl);
455                                 // NOTREACHED
456                         }
457
458                         throw new Exception(L10n::t('Please enter the required information.'));
459                 }
460
461                 if (!Network::isUrlValid($openid_url)) {
462                         $openid_url = '';
463                 }
464
465                 $err = '';
466
467                 // collapse multiple spaces in name
468                 $username = preg_replace('/ +/', ' ', $username);
469
470                 $username_min_length = max(1, min(64, intval(Config::get('system', 'username_min_length', 3))));
471                 $username_max_length = max(1, min(64, intval(Config::get('system', 'username_max_length', 48))));
472
473                 if ($username_min_length > $username_max_length) {
474                         Logger::log(L10n::t('system.username_min_length (%s) and system.username_max_length (%s) are excluding each other, swapping values.', $username_min_length, $username_max_length), Logger::WARNING);
475                         $tmp = $username_min_length;
476                         $username_min_length = $username_max_length;
477                         $username_max_length = $tmp;
478                 }
479
480                 if (mb_strlen($username) < $username_min_length) {
481                         throw new Exception(L10n::tt('Username should be at least %s character.', 'Username should be at least %s characters.', $username_min_length));
482                 }
483
484                 if (mb_strlen($username) > $username_max_length) {
485                         throw new Exception(L10n::tt('Username should be at most %s character.', 'Username should be at most %s characters.', $username_max_length));
486                 }
487
488                 // So now we are just looking for a space in the full name.
489                 $loose_reg = Config::get('system', 'no_regfullname');
490                 if (!$loose_reg) {
491                         $username = mb_convert_case($username, MB_CASE_TITLE, 'UTF-8');
492                         if (strpos($username, ' ') === false) {
493                                 throw new Exception(L10n::t("That doesn't appear to be your full (First Last) name."));
494                         }
495                 }
496
497                 if (!Network::isEmailDomainAllowed($email)) {
498                         throw new Exception(L10n::t('Your email domain is not among those allowed on this site.'));
499                 }
500
501                 if (!valid_email($email) || !Network::isEmailDomainValid($email)) {
502                         throw new Exception(L10n::t('Not a valid email address.'));
503                 }
504                 if (self::isNicknameBlocked($nickname)) {
505                         throw new Exception(L10n::t('The nickname was blocked from registration by the nodes admin.'));
506                 }
507
508                 if (Config::get('system', 'block_extended_register', false) && DBA::exists('user', ['email' => $email])) {
509                         throw new Exception(L10n::t('Cannot use that email.'));
510                 }
511
512                 // Disallow somebody creating an account using openid that uses the admin email address,
513                 // since openid bypasses email verification. We'll allow it if there is not yet an admin account.
514                 if (Config::get('config', 'admin_email') && strlen($openid_url)) {
515                         $adminlist = explode(',', str_replace(' ', '', strtolower(Config::get('config', 'admin_email'))));
516                         if (in_array(strtolower($email), $adminlist)) {
517                                 throw new Exception(L10n::t('Cannot use that email.'));
518                         }
519                 }
520
521                 $nickname = $data['nickname'] = strtolower($nickname);
522
523                 if (!preg_match('/^[a-z0-9][a-z0-9\_]*$/', $nickname)) {
524                         throw new Exception(L10n::t('Your nickname can only contain a-z, 0-9 and _.'));
525                 }
526
527                 // Check existing and deleted accounts for this nickname.
528                 if (DBA::exists('user', ['nickname' => $nickname])
529                         || DBA::exists('userd', ['username' => $nickname])
530                 ) {
531                         throw new Exception(L10n::t('Nickname is already registered. Please choose another.'));
532                 }
533
534                 $new_password = strlen($password) ? $password : User::generateNewPassword();
535                 $new_password_encoded = self::hashPassword($new_password);
536
537                 $return['password'] = $new_password;
538
539                 $keys = Crypto::newKeypair(4096);
540                 if ($keys === false) {
541                         throw new Exception(L10n::t('SERIOUS ERROR: Generation of security keys failed.'));
542                 }
543
544                 $prvkey = $keys['prvkey'];
545                 $pubkey = $keys['pubkey'];
546
547                 // Create another keypair for signing/verifying salmon protocol messages.
548                 $sres = Crypto::newKeypair(512);
549                 $sprvkey = $sres['prvkey'];
550                 $spubkey = $sres['pubkey'];
551
552                 $insert_result = DBA::insert('user', [
553                         'guid'     => System::createUUID(),
554                         'username' => $username,
555                         'password' => $new_password_encoded,
556                         'email'    => $email,
557                         'openid'   => $openid_url,
558                         'nickname' => $nickname,
559                         'pubkey'   => $pubkey,
560                         'prvkey'   => $prvkey,
561                         'spubkey'  => $spubkey,
562                         'sprvkey'  => $sprvkey,
563                         'verified' => $verified,
564                         'blocked'  => $blocked,
565                         'language' => $language,
566                         'timezone' => 'UTC',
567                         'register_date' => DateTimeFormat::utcNow(),
568                         'default-location' => ''
569                 ]);
570
571                 if ($insert_result) {
572                         $uid = DBA::lastInsertId();
573                         $user = DBA::selectFirst('user', [], ['uid' => $uid]);
574                 } else {
575                         throw new Exception(L10n::t('An error occurred during registration. Please try again.'));
576                 }
577
578                 if (!$uid) {
579                         throw new Exception(L10n::t('An error occurred during registration. Please try again.'));
580                 }
581
582                 // if somebody clicked submit twice very quickly, they could end up with two accounts
583                 // due to race condition. Remove this one.
584                 $user_count = DBA::count('user', ['nickname' => $nickname]);
585                 if ($user_count > 1) {
586                         DBA::delete('user', ['uid' => $uid]);
587
588                         throw new Exception(L10n::t('Nickname is already registered. Please choose another.'));
589                 }
590
591                 $insert_result = DBA::insert('profile', [
592                         'uid' => $uid,
593                         'name' => $username,
594                         'photo' => System::baseUrl() . "/photo/profile/{$uid}.jpg",
595                         'thumb' => System::baseUrl() . "/photo/avatar/{$uid}.jpg",
596                         'publish' => $publish,
597                         'is-default' => 1,
598                         'net-publish' => $netpublish,
599                         'profile-name' => L10n::t('default')
600                 ]);
601                 if (!$insert_result) {
602                         DBA::delete('user', ['uid' => $uid]);
603
604                         throw new Exception(L10n::t('An error occurred creating your default profile. Please try again.'));
605                 }
606
607                 // Create the self contact
608                 if (!Contact::createSelfFromUserId($uid)) {
609                         DBA::delete('user', ['uid' => $uid]);
610
611                         throw new Exception(L10n::t('An error occurred creating your self contact. Please try again.'));
612                 }
613
614                 // Create a group with no members. This allows somebody to use it
615                 // right away as a default group for new contacts.
616                 $def_gid = Group::create($uid, L10n::t('Friends'));
617                 if (!$def_gid) {
618                         DBA::delete('user', ['uid' => $uid]);
619
620                         throw new Exception(L10n::t('An error occurred creating your default contact group. Please try again.'));
621                 }
622
623                 $fields = ['def_gid' => $def_gid];
624                 if (Config::get('system', 'newuser_private') && $def_gid) {
625                         $fields['allow_gid'] = '<' . $def_gid . '>';
626                 }
627
628                 DBA::update('user', $fields, ['uid' => $uid]);
629
630                 // if we have no OpenID photo try to look up an avatar
631                 if (!strlen($photo)) {
632                         $photo = Network::lookupAvatarByEmail($email);
633                 }
634
635                 // unless there is no avatar-addon loaded
636                 if (strlen($photo)) {
637                         $photo_failure = false;
638
639                         $filename = basename($photo);
640                         $img_str = Network::fetchUrl($photo, true);
641                         // guess mimetype from headers or filename
642                         $type = Image::guessType($photo, true);
643
644                         $Image = new Image($img_str, $type);
645                         if ($Image->isValid()) {
646                                 $Image->scaleToSquare(300);
647
648                                 $hash = Photo::newResource();
649
650                                 $r = Photo::store($Image, $uid, 0, $hash, $filename, L10n::t('Profile Photos'), 4);
651
652                                 if ($r === false) {
653                                         $photo_failure = true;
654                                 }
655
656                                 $Image->scaleDown(80);
657
658                                 $r = Photo::store($Image, $uid, 0, $hash, $filename, L10n::t('Profile Photos'), 5);
659
660                                 if ($r === false) {
661                                         $photo_failure = true;
662                                 }
663
664                                 $Image->scaleDown(48);
665
666                                 $r = Photo::store($Image, $uid, 0, $hash, $filename, L10n::t('Profile Photos'), 6);
667
668                                 if ($r === false) {
669                                         $photo_failure = true;
670                                 }
671
672                                 if (!$photo_failure) {
673                                         DBA::update('photo', ['profile' => 1], ['resource-id' => $hash]);
674                                 }
675                         }
676                 }
677
678                 Addon::callHooks('register_account', $uid);
679
680                 $return['user'] = $user;
681                 return $return;
682         }
683
684         /**
685          * @brief Sends pending registration confirmation email
686          *
687          * @param array  $user     User record array
688          * @param string $sitename
689          * @param string $siteurl
690          * @param string $password Plaintext password
691          * @return NULL|boolean from notification() and email() inherited
692          */
693         public static function sendRegisterPendingEmail($user, $sitename, $siteurl, $password)
694         {
695                 $body = deindent(L10n::t('
696                         Dear %1$s,
697                                 Thank you for registering at %2$s. Your account is pending for approval by the administrator.
698
699                         Your login details are as follows:
700
701                         Site Location:  %3$s
702                         Login Name:             %4$s
703                         Password:               %5$s
704                 ',
705                         $user['username'], $sitename, $siteurl, $user['nickname'], $password
706                 ));
707
708                 return notification([
709                         'type'     => SYSTEM_EMAIL,
710                         'uid'      => $user['uid'],
711                         'to_email' => $user['email'],
712                         'subject'  => L10n::t('Registration at %s', $sitename),
713                         'body'     => $body
714                 ]);
715         }
716
717         /**
718          * @brief Sends registration confirmation
719          *
720          * It's here as a function because the mail is sent from different parts
721          *
722          * @param array  $user     User record array
723          * @param string $sitename
724          * @param string $siteurl
725          * @param string $password Plaintext password
726          * @return NULL|boolean from notification() and email() inherited
727          */
728         public static function sendRegisterOpenEmail($user, $sitename, $siteurl, $password)
729         {
730                 $preamble = deindent(L10n::t('
731                         Dear %1$s,
732                                 Thank you for registering at %2$s. Your account has been created.
733                 ',
734                         $preamble, $user['username'], $sitename
735                 ));
736                 $body = deindent(L10n::t('
737                         The login details are as follows:
738
739                         Site Location:  %3$s
740                         Login Name:             %1$s
741                         Password:               %5$s
742
743                         You may change your password from your account "Settings" page after logging
744                         in.
745
746                         Please take a few moments to review the other account settings on that page.
747
748                         You may also wish to add some basic information to your default profile
749                         ' . "\x28" . 'on the "Profiles" page' . "\x29" . ' so that other people can easily find you.
750
751                         We recommend setting your full name, adding a profile photo,
752                         adding some profile "keywords" ' . "\x28" . 'very useful in making new friends' . "\x29" . ' - and
753                         perhaps what country you live in; if you do not wish to be more specific
754                         than that.
755
756                         We fully respect your right to privacy, and none of these items are necessary.
757                         If you are new and do not know anybody here, they may help
758                         you to make some new and interesting friends.
759
760                         If you ever want to delete your account, you can do so at %3$s/removeme
761
762                         Thank you and welcome to %2$s.',
763                         $user['email'], $sitename, $siteurl, $user['username'], $password
764                 ));
765
766                 return notification([
767                         'uid'      => $user['uid'],
768                         'language' => $user['language'],
769                         'type'     => SYSTEM_EMAIL,
770                         'to_email' => $user['email'],
771                         'subject'  => L10n::t('Registration details for %s', $sitename),
772                         'preamble' => $preamble,
773                         'body'     => $body
774                 ]);
775         }
776
777         /**
778          * @param object $uid user to remove
779          * @return void
780          */
781         public static function remove($uid)
782         {
783                 if (!$uid) {
784                         return;
785                 }
786
787                 $a = get_app();
788
789                 Logger::log('Removing user: ' . $uid);
790
791                 $user = DBA::selectFirst('user', [], ['uid' => $uid]);
792
793                 Addon::callHooks('remove_user', $user);
794
795                 // save username (actually the nickname as it is guaranteed
796                 // unique), so it cannot be re-registered in the future.
797                 DBA::insert('userd', ['username' => $user['nickname']]);
798
799                 // The user and related data will be deleted in "cron_expire_and_remove_users" (cronjobs.php)
800                 DBA::update('user', ['account_removed' => true, 'account_expires_on' => DateTimeFormat::utc(DateTimeFormat::utcNow() . " + 7 day")], ['uid' => $uid]);
801                 Worker::add(PRIORITY_HIGH, "Notifier", "removeme", $uid);
802
803                 // Send an update to the directory
804                 $self = DBA::selectFirst('contact', ['url'], ['uid' => $uid, 'self' => true]);
805                 Worker::add(PRIORITY_LOW, "Directory", $self['url']);
806
807                 // Remove the user relevant data
808                 Worker::add(PRIORITY_LOW, "RemoveUser", $uid);
809
810                 if ($uid == local_user()) {
811                         unset($_SESSION['authenticated']);
812                         unset($_SESSION['uid']);
813                         $a->internalRedirect();
814                 }
815         }
816 }