1e5a6b4d4SGreg Roach<?php 23976b470SGreg Roach 3e5a6b4d4SGreg Roach/** 4e5a6b4d4SGreg Roach * webtrees: online genealogy 51fe542e9SGreg Roach * Copyright (C) 2021 webtrees development team 6e5a6b4d4SGreg Roach * This program is free software: you can redistribute it and/or modify 7e5a6b4d4SGreg Roach * it under the terms of the GNU General Public License as published by 8e5a6b4d4SGreg Roach * the Free Software Foundation, either version 3 of the License, or 9e5a6b4d4SGreg Roach * (at your option) any later version. 10e5a6b4d4SGreg Roach * This program is distributed in the hope that it will be useful, 11e5a6b4d4SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 12e5a6b4d4SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13e5a6b4d4SGreg Roach * GNU General Public License for more details. 14e5a6b4d4SGreg Roach * You should have received a copy of the GNU General Public License 1589f7189bSGreg Roach * along with this program. If not, see <https://www.gnu.org/licenses/>. 16e5a6b4d4SGreg Roach */ 17fcfa147eSGreg Roach 18e5a6b4d4SGreg Roachdeclare(strict_types=1); 19e5a6b4d4SGreg Roach 20e5a6b4d4SGreg Roachnamespace Fisharebest\Webtrees\Services; 21e5a6b4d4SGreg Roach 222474349cSGreg Roachuse Closure; 23e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Auth; 242474349cSGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 256b9cb339SGreg Roachuse Fisharebest\Webtrees\Registry; 26e381f98dSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\ContactPage; 27e381f98dSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MessagePage; 28e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Individual; 29e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\User; 30*b55cbc6bSGreg Roachuse Fisharebest\Webtrees\Validator; 31e5a6b4d4SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 327c4add84SGreg Roachuse Illuminate\Database\Query\Builder; 337c4add84SGreg Roachuse Illuminate\Database\Query\JoinClause; 34e5a6b4d4SGreg Roachuse Illuminate\Support\Collection; 356ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 36f1d4b4a2SGreg Roach 372474349cSGreg Roachuse function max; 38d97083feSGreg Roachuse function time; 39e5a6b4d4SGreg Roach 40e5a6b4d4SGreg Roach/** 41e5a6b4d4SGreg Roach * Functions for managing users. 42e5a6b4d4SGreg Roach */ 43e5a6b4d4SGreg Roachclass UserService 44e5a6b4d4SGreg Roach{ 45e5a6b4d4SGreg Roach /** 46e5a6b4d4SGreg Roach * Find the user with a specified user_id. 47e5a6b4d4SGreg Roach * 48e5a6b4d4SGreg Roach * @param int|null $user_id 49e5a6b4d4SGreg Roach * 50e5a6b4d4SGreg Roach * @return User|null 51e5a6b4d4SGreg Roach */ 5224f2a3afSGreg Roach public function find(?int $user_id): ?User 53e5a6b4d4SGreg Roach { 546b9cb339SGreg Roach return Registry::cache()->array()->remember('user-' . $user_id, static function () use ($user_id): ?User { 55e5a6b4d4SGreg Roach return DB::table('user') 56e5a6b4d4SGreg Roach ->where('user_id', '=', $user_id) 57e5a6b4d4SGreg Roach ->get() 58e5a6b4d4SGreg Roach ->map(User::rowMapper()) 59e5a6b4d4SGreg Roach ->first(); 60e5a6b4d4SGreg Roach }); 61e5a6b4d4SGreg Roach } 62e5a6b4d4SGreg Roach 63e5a6b4d4SGreg Roach /** 64e5a6b4d4SGreg Roach * Find the user with a specified email address. 65e5a6b4d4SGreg Roach * 66e5a6b4d4SGreg Roach * @param string $email 67e5a6b4d4SGreg Roach * 68e5a6b4d4SGreg Roach * @return User|null 69e5a6b4d4SGreg Roach */ 7024f2a3afSGreg Roach public function findByEmail(string $email): ?User 71e5a6b4d4SGreg Roach { 72e5a6b4d4SGreg Roach return DB::table('user') 73e5a6b4d4SGreg Roach ->where('email', '=', $email) 74e5a6b4d4SGreg Roach ->get() 75e5a6b4d4SGreg Roach ->map(User::rowMapper()) 76e5a6b4d4SGreg Roach ->first(); 77e5a6b4d4SGreg Roach } 78e5a6b4d4SGreg Roach 79e5a6b4d4SGreg Roach /** 80e5a6b4d4SGreg Roach * Find the user with a specified user_name or email address. 81e5a6b4d4SGreg Roach * 82e5a6b4d4SGreg Roach * @param string $identifier 83e5a6b4d4SGreg Roach * 84e5a6b4d4SGreg Roach * @return User|null 85e5a6b4d4SGreg Roach */ 8624f2a3afSGreg Roach public function findByIdentifier(string $identifier): ?User 87e5a6b4d4SGreg Roach { 88e5a6b4d4SGreg Roach return DB::table('user') 89e5a6b4d4SGreg Roach ->where('user_name', '=', $identifier) 90e5a6b4d4SGreg Roach ->orWhere('email', '=', $identifier) 91e5a6b4d4SGreg Roach ->get() 92e5a6b4d4SGreg Roach ->map(User::rowMapper()) 93e5a6b4d4SGreg Roach ->first(); 94e5a6b4d4SGreg Roach } 95e5a6b4d4SGreg Roach 96e5a6b4d4SGreg Roach /** 97e5a6b4d4SGreg Roach * Find the user(s) with a specified genealogy record. 98e5a6b4d4SGreg Roach * 99e5a6b4d4SGreg Roach * @param Individual $individual 100e5a6b4d4SGreg Roach * 10136779af1SGreg Roach * @return Collection<int,User> 102e5a6b4d4SGreg Roach */ 103e5a6b4d4SGreg Roach public function findByIndividual(Individual $individual): Collection 104e5a6b4d4SGreg Roach { 105e5a6b4d4SGreg Roach return DB::table('user') 106e5a6b4d4SGreg Roach ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id') 107e5a6b4d4SGreg Roach ->where('gedcom_id', '=', $individual->tree()->id()) 108e5a6b4d4SGreg Roach ->where('setting_value', '=', $individual->xref()) 1091fe542e9SGreg Roach ->where('setting_name', '=', UserInterface::PREF_TREE_ACCOUNT_XREF) 110e5a6b4d4SGreg Roach ->select(['user.*']) 111e5a6b4d4SGreg Roach ->get() 112e5a6b4d4SGreg Roach ->map(User::rowMapper()); 113e5a6b4d4SGreg Roach } 114e5a6b4d4SGreg Roach 115e5a6b4d4SGreg Roach /** 116a00bcc63SGreg Roach * Find the user with a specified password reset token. 117a00bcc63SGreg Roach * 118a00bcc63SGreg Roach * @param string $token 119a00bcc63SGreg Roach * 120a00bcc63SGreg Roach * @return User|null 121a00bcc63SGreg Roach */ 122a00bcc63SGreg Roach public function findByToken(string $token): ?User 123a00bcc63SGreg Roach { 124a00bcc63SGreg Roach return DB::table('user') 125a00bcc63SGreg Roach ->join('user_setting AS us1', 'us1.user_id', '=', 'user.user_id') 126a00bcc63SGreg Roach ->where('us1.setting_name', '=', 'password-token') 127a00bcc63SGreg Roach ->where('us1.setting_value', '=', $token) 128a00bcc63SGreg Roach ->join('user_setting AS us2', 'us2.user_id', '=', 'user.user_id') 129a00bcc63SGreg Roach ->where('us2.setting_name', '=', 'password-token-expire') 130d97083feSGreg Roach ->where('us2.setting_value', '>', time()) 131a00bcc63SGreg Roach ->select(['user.*']) 132a00bcc63SGreg Roach ->get() 133a00bcc63SGreg Roach ->map(User::rowMapper()) 134a00bcc63SGreg Roach ->first(); 135a00bcc63SGreg Roach } 136a00bcc63SGreg Roach 137a00bcc63SGreg Roach /** 138e5a6b4d4SGreg Roach * Find the user with a specified user_name. 139e5a6b4d4SGreg Roach * 140e5a6b4d4SGreg Roach * @param string $user_name 141e5a6b4d4SGreg Roach * 142e5a6b4d4SGreg Roach * @return User|null 143e5a6b4d4SGreg Roach */ 14424f2a3afSGreg Roach public function findByUserName(string $user_name): ?User 145e5a6b4d4SGreg Roach { 146e5a6b4d4SGreg Roach return DB::table('user') 147e5a6b4d4SGreg Roach ->where('user_name', '=', $user_name) 148e5a6b4d4SGreg Roach ->get() 149e5a6b4d4SGreg Roach ->map(User::rowMapper()) 150e5a6b4d4SGreg Roach ->first(); 151e5a6b4d4SGreg Roach } 152e5a6b4d4SGreg Roach 153e5a6b4d4SGreg Roach /** 1542474349cSGreg Roach * Callback to sort users by their last-login (or registration) time. 1552474349cSGreg Roach * 1562474349cSGreg Roach * @return Closure 1572474349cSGreg Roach */ 1582474349cSGreg Roach public function sortByLastLogin(): Closure 1592474349cSGreg Roach { 1607c4add84SGreg Roach return static function (UserInterface $user1, UserInterface $user2) { 1611fe542e9SGreg Roach $registered_at1 = (int) $user1->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED); 1621fe542e9SGreg Roach $logged_in_at1 = (int) $user1->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE); 1631fe542e9SGreg Roach $registered_at2 = (int) $user2->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED); 1641fe542e9SGreg Roach $logged_in_at2 = (int) $user2->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE); 1652474349cSGreg Roach 1662474349cSGreg Roach return max($registered_at1, $logged_in_at1) <=> max($registered_at2, $logged_in_at2); 1672474349cSGreg Roach }; 1682474349cSGreg Roach } 1692474349cSGreg Roach 1702474349cSGreg Roach /** 1712474349cSGreg Roach * Callback to filter users who have not logged in since a given time. 1722474349cSGreg Roach * 1732474349cSGreg Roach * @param int $timestamp 1742474349cSGreg Roach * 1752474349cSGreg Roach * @return Closure 1762474349cSGreg Roach */ 1772474349cSGreg Roach public function filterInactive(int $timestamp): Closure 1782474349cSGreg Roach { 1797c4add84SGreg Roach return static function (UserInterface $user) use ($timestamp): bool { 1801fe542e9SGreg Roach $registered_at = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_REGISTERED); 1811fe542e9SGreg Roach $logged_in_at = (int) $user->getPreference(UserInterface::PREF_TIMESTAMP_ACTIVE); 1822474349cSGreg Roach 1832474349cSGreg Roach return max($registered_at, $logged_in_at) < $timestamp; 1842474349cSGreg Roach }; 1852474349cSGreg Roach } 1862474349cSGreg Roach 1872474349cSGreg Roach /** 188e5a6b4d4SGreg Roach * Get a list of all users. 189e5a6b4d4SGreg Roach * 19036779af1SGreg Roach * @return Collection<int,User> 191e5a6b4d4SGreg Roach */ 192e5a6b4d4SGreg Roach public function all(): Collection 193e5a6b4d4SGreg Roach { 194e5a6b4d4SGreg Roach return DB::table('user') 195e5a6b4d4SGreg Roach ->where('user_id', '>', 0) 196e5a6b4d4SGreg Roach ->orderBy('real_name') 197e5a6b4d4SGreg Roach ->get() 198e5a6b4d4SGreg Roach ->map(User::rowMapper()); 199e5a6b4d4SGreg Roach } 200e5a6b4d4SGreg Roach 201e5a6b4d4SGreg Roach /** 202e5a6b4d4SGreg Roach * Get a list of all administrators. 203e5a6b4d4SGreg Roach * 20436779af1SGreg Roach * @return Collection<int,User> 205e5a6b4d4SGreg Roach */ 206e5a6b4d4SGreg Roach public function administrators(): Collection 207e5a6b4d4SGreg Roach { 208e5a6b4d4SGreg Roach return DB::table('user') 2091ab2f386SGreg Roach ->join('user_setting', 'user_setting.user_id', '=', 'user.user_id') 2101fe542e9SGreg Roach ->where('user_setting.setting_name', '=', UserInterface::PREF_IS_ADMINISTRATOR) 2111ab2f386SGreg Roach ->where('user_setting.setting_value', '=', '1') 212e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 213e5a6b4d4SGreg Roach ->orderBy('real_name') 214e5a6b4d4SGreg Roach ->select(['user.*']) 215e5a6b4d4SGreg Roach ->get() 216e5a6b4d4SGreg Roach ->map(User::rowMapper()); 217e5a6b4d4SGreg Roach } 218e5a6b4d4SGreg Roach 219e5a6b4d4SGreg Roach /** 220e5a6b4d4SGreg Roach * Get a list of all managers. 221e5a6b4d4SGreg Roach * 22236779af1SGreg Roach * @return Collection<int,User> 223e5a6b4d4SGreg Roach */ 224e5a6b4d4SGreg Roach public function managers(): Collection 225e5a6b4d4SGreg Roach { 226e5a6b4d4SGreg Roach return DB::table('user') 2271ab2f386SGreg Roach ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id') 2281fe542e9SGreg Roach ->where('user_gedcom_setting.setting_name', '=', UserInterface::PREF_TREE_ROLE) 2291fe542e9SGreg Roach ->where('user_gedcom_setting.setting_value', '=', UserInterface::ROLE_MANAGER) 230e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 2311ab2f386SGreg Roach ->groupBy(['user.user_id']) 232e5a6b4d4SGreg Roach ->orderBy('real_name') 233e5a6b4d4SGreg Roach ->select(['user.*']) 234e5a6b4d4SGreg Roach ->get() 235e5a6b4d4SGreg Roach ->map(User::rowMapper()); 236e5a6b4d4SGreg Roach } 237e5a6b4d4SGreg Roach 238e5a6b4d4SGreg Roach /** 239e5a6b4d4SGreg Roach * Get a list of all moderators. 240e5a6b4d4SGreg Roach * 24136779af1SGreg Roach * @return Collection<int,User> 242e5a6b4d4SGreg Roach */ 243e5a6b4d4SGreg Roach public function moderators(): Collection 244e5a6b4d4SGreg Roach { 245e5a6b4d4SGreg Roach return DB::table('user') 2461ab2f386SGreg Roach ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id') 2471fe542e9SGreg Roach ->where('user_gedcom_setting.setting_name', '=', UserInterface::PREF_TREE_ROLE) 2481fe542e9SGreg Roach ->where('user_gedcom_setting.setting_value', '=', UserInterface::ROLE_MODERATOR) 249e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 2501ab2f386SGreg Roach ->groupBy(['user.user_id']) 251e5a6b4d4SGreg Roach ->orderBy('real_name') 252e5a6b4d4SGreg Roach ->select(['user.*']) 253e5a6b4d4SGreg Roach ->get() 254e5a6b4d4SGreg Roach ->map(User::rowMapper()); 255e5a6b4d4SGreg Roach } 256e5a6b4d4SGreg Roach 257e5a6b4d4SGreg Roach /** 258e5a6b4d4SGreg Roach * Get a list of all verified users. 259e5a6b4d4SGreg Roach * 26036779af1SGreg Roach * @return Collection<int,User> 261e5a6b4d4SGreg Roach */ 262e5a6b4d4SGreg Roach public function unapproved(): Collection 263e5a6b4d4SGreg Roach { 264e5a6b4d4SGreg Roach return DB::table('user') 2657c4add84SGreg Roach ->leftJoin('user_setting', static function (JoinClause $join): void { 2667c4add84SGreg Roach $join 2677c4add84SGreg Roach ->on('user_setting.user_id', '=', 'user.user_id') 2681fe542e9SGreg Roach ->where('user_setting.setting_name', '=', UserInterface::PREF_IS_ACCOUNT_APPROVED); 2697c4add84SGreg Roach }) 2707c4add84SGreg Roach ->where(static function (Builder $query): void { 2717c4add84SGreg Roach $query 2721ab2f386SGreg Roach ->where('user_setting.setting_value', '<>', '1') 2737c4add84SGreg Roach ->orWhereNull('user_setting.setting_value'); 2747c4add84SGreg Roach }) 275e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 276e5a6b4d4SGreg Roach ->orderBy('real_name') 277090fa720SGreg Roach ->select(['user.*']) 278e5a6b4d4SGreg Roach ->get() 279e5a6b4d4SGreg Roach ->map(User::rowMapper()); 280e5a6b4d4SGreg Roach } 281e5a6b4d4SGreg Roach 282e5a6b4d4SGreg Roach /** 283e5a6b4d4SGreg Roach * Get a list of all verified users. 284e5a6b4d4SGreg Roach * 28536779af1SGreg Roach * @return Collection<int,User> 286e5a6b4d4SGreg Roach */ 287e5a6b4d4SGreg Roach public function unverified(): Collection 288e5a6b4d4SGreg Roach { 289e5a6b4d4SGreg Roach return DB::table('user') 2907c4add84SGreg Roach ->leftJoin('user_setting', static function (JoinClause $join): void { 2917c4add84SGreg Roach $join 2927c4add84SGreg Roach ->on('user_setting.user_id', '=', 'user.user_id') 2931fe542e9SGreg Roach ->where('user_setting.setting_name', '=', UserInterface::PREF_IS_EMAIL_VERIFIED); 2947c4add84SGreg Roach }) 2957c4add84SGreg Roach ->where(static function (Builder $query): void { 2967c4add84SGreg Roach $query 2971ab2f386SGreg Roach ->where('user_setting.setting_value', '<>', '1') 2987c4add84SGreg Roach ->orWhereNull('user_setting.setting_value'); 2997c4add84SGreg Roach }) 300e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 301e5a6b4d4SGreg Roach ->orderBy('real_name') 302090fa720SGreg Roach ->select(['user.*']) 303e5a6b4d4SGreg Roach ->get() 304e5a6b4d4SGreg Roach ->map(User::rowMapper()); 305e5a6b4d4SGreg Roach } 306e5a6b4d4SGreg Roach 307e5a6b4d4SGreg Roach /** 308e5a6b4d4SGreg Roach * Get a list of all users who are currently logged in. 309e5a6b4d4SGreg Roach * 31036779af1SGreg Roach * @return Collection<int,User> 311e5a6b4d4SGreg Roach */ 312e5a6b4d4SGreg Roach public function allLoggedIn(): Collection 313e5a6b4d4SGreg Roach { 314e5a6b4d4SGreg Roach return DB::table('user') 315e5a6b4d4SGreg Roach ->join('session', 'session.user_id', '=', 'user.user_id') 316e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 317e5a6b4d4SGreg Roach ->orderBy('real_name') 318e5a6b4d4SGreg Roach ->select(['user.*']) 319e5a6b4d4SGreg Roach ->distinct() 320e5a6b4d4SGreg Roach ->get() 321e5a6b4d4SGreg Roach ->map(User::rowMapper()); 322e5a6b4d4SGreg Roach } 323e5a6b4d4SGreg Roach 324e5a6b4d4SGreg Roach /** 325e5a6b4d4SGreg Roach * Create a new user. 326e5a6b4d4SGreg Roach * The calling code needs to check for duplicates identifiers before calling 327e5a6b4d4SGreg Roach * this function. 328e5a6b4d4SGreg Roach * 329e5a6b4d4SGreg Roach * @param string $user_name 330e5a6b4d4SGreg Roach * @param string $real_name 331e5a6b4d4SGreg Roach * @param string $email 332e5a6b4d4SGreg Roach * @param string $password 333e5a6b4d4SGreg Roach * 334e5a6b4d4SGreg Roach * @return User 335e5a6b4d4SGreg Roach */ 3366be338f5SGreg Roach public function create(string $user_name, string $real_name, string $email, string $password): User 337e5a6b4d4SGreg Roach { 338e5a6b4d4SGreg Roach DB::table('user')->insert([ 339e5a6b4d4SGreg Roach 'user_name' => $user_name, 340e5a6b4d4SGreg Roach 'real_name' => $real_name, 341e5a6b4d4SGreg Roach 'email' => $email, 342e5a6b4d4SGreg Roach 'password' => password_hash($password, PASSWORD_DEFAULT), 343e5a6b4d4SGreg Roach ]); 344e5a6b4d4SGreg Roach 345e5a6b4d4SGreg Roach $user_id = (int) DB::connection()->getPdo()->lastInsertId(); 346e5a6b4d4SGreg Roach 347e5a6b4d4SGreg Roach return new User($user_id, $user_name, $real_name, $email); 348e5a6b4d4SGreg Roach } 349e5a6b4d4SGreg Roach 350e5a6b4d4SGreg Roach /** 351e5a6b4d4SGreg Roach * Delete a user 352e5a6b4d4SGreg Roach * 353e5a6b4d4SGreg Roach * @param User $user 354e5a6b4d4SGreg Roach * 355e5a6b4d4SGreg Roach * @return void 356e5a6b4d4SGreg Roach */ 357e364afe4SGreg Roach public function delete(User $user): void 358e5a6b4d4SGreg Roach { 359e5a6b4d4SGreg Roach // Don't delete the logs, just set the user to null. 360e5a6b4d4SGreg Roach DB::table('log') 361e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 362e5a6b4d4SGreg Roach ->update(['user_id' => null]); 363e5a6b4d4SGreg Roach 364e5a6b4d4SGreg Roach // Take over the user’s pending changes. (What else could we do with them?) 365e5a6b4d4SGreg Roach DB::table('change') 366e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 367e5a6b4d4SGreg Roach ->where('status', '=', 'rejected') 368e5a6b4d4SGreg Roach ->delete(); 369e5a6b4d4SGreg Roach 370e5a6b4d4SGreg Roach DB::table('change') 371e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 372e5a6b4d4SGreg Roach ->update(['user_id' => Auth::id()]); 373e5a6b4d4SGreg Roach 374e5a6b4d4SGreg Roach // Delete settings and preferences 375e5a6b4d4SGreg Roach DB::table('block_setting') 376e5a6b4d4SGreg Roach ->join('block', 'block_setting.block_id', '=', 'block.block_id') 377e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 378e5a6b4d4SGreg Roach ->delete(); 379e5a6b4d4SGreg Roach 380e5a6b4d4SGreg Roach DB::table('block')->where('user_id', '=', $user->id())->delete(); 381e5a6b4d4SGreg Roach DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete(); 382e5a6b4d4SGreg Roach DB::table('user_setting')->where('user_id', '=', $user->id())->delete(); 383e5a6b4d4SGreg Roach DB::table('message')->where('user_id', '=', $user->id())->delete(); 384e5a6b4d4SGreg Roach DB::table('user')->where('user_id', '=', $user->id())->delete(); 385e5a6b4d4SGreg Roach } 38686730b84SGreg Roach 38786730b84SGreg Roach /** 3884db4b4a9SGreg Roach * @param User $contact_user 389a992e8c1SGreg Roach * @param ServerRequestInterface $request 39086730b84SGreg Roach * 39186730b84SGreg Roach * @return string 39286730b84SGreg Roach */ 393a992e8c1SGreg Roach public function contactLink(User $contact_user, ServerRequestInterface $request): string 394dcbe9044SGreg Roach { 395*b55cbc6bSGreg Roach $tree = Validator::attributes($request)->tree(); 396*b55cbc6bSGreg Roach $user = Validator::attributes($request)->user(); 39786730b84SGreg Roach 3981fe542e9SGreg Roach if ($contact_user->getPreference(UserInterface::PREF_CONTACT_METHOD) === 'mailto') { 39986730b84SGreg Roach $url = 'mailto:' . $contact_user->email(); 40086730b84SGreg Roach } elseif ($user instanceof User) { 40186730b84SGreg Roach // Logged-in users send direct messages 4020bf22806SGreg Roach $url = route(MessagePage::class, [ 4030bf22806SGreg Roach 'to' => $contact_user->userName(), 4040bf22806SGreg Roach 'tree' => $tree->name(), 4050bf22806SGreg Roach 'url' => (string) $request->getUri(), 4060bf22806SGreg Roach ]); 40786730b84SGreg Roach } else { 40886730b84SGreg Roach // Visitors use the contact form. 409e381f98dSGreg Roach $url = route(ContactPage::class, [ 41086730b84SGreg Roach 'to' => $contact_user->userName(), 411d72b284aSGreg Roach 'tree' => $tree->name(), 412f567c3d8SGreg Roach 'url' => (string) $request->getUri(), 41386730b84SGreg Roach ]); 41486730b84SGreg Roach } 41586730b84SGreg Roach 41686730b84SGreg Roach return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>'; 41786730b84SGreg Roach } 418e5a6b4d4SGreg Roach} 419