1e5a6b4d4SGreg Roach<?php 2e5a6b4d4SGreg Roach/** 3e5a6b4d4SGreg Roach * webtrees: online genealogy 4e5a6b4d4SGreg Roach * Copyright (C) 2019 webtrees development team 5e5a6b4d4SGreg Roach * This program is free software: you can redistribute it and/or modify 6e5a6b4d4SGreg Roach * it under the terms of the GNU General Public License as published by 7e5a6b4d4SGreg Roach * the Free Software Foundation, either version 3 of the License, or 8e5a6b4d4SGreg Roach * (at your option) any later version. 9e5a6b4d4SGreg Roach * This program is distributed in the hope that it will be useful, 10e5a6b4d4SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of 11e5a6b4d4SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12e5a6b4d4SGreg Roach * GNU General Public License for more details. 13e5a6b4d4SGreg Roach * You should have received a copy of the GNU General Public License 14e5a6b4d4SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>. 15e5a6b4d4SGreg Roach */ 16e5a6b4d4SGreg Roachdeclare(strict_types=1); 17e5a6b4d4SGreg Roach 18e5a6b4d4SGreg Roachnamespace Fisharebest\Webtrees\Services; 19e5a6b4d4SGreg Roach 20e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Auth; 2186730b84SGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface; 22e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Individual; 2386730b84SGreg Roachuse Fisharebest\Webtrees\Tree; 24e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\User; 25e5a6b4d4SGreg Roachuse Illuminate\Database\Capsule\Manager as DB; 26e5a6b4d4SGreg Roachuse Illuminate\Database\Query\JoinClause; 27e5a6b4d4SGreg Roachuse Illuminate\Support\Collection; 286ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface; 296ccdf4f0SGreg Roachuse function app; 30e5a6b4d4SGreg Roach 31e5a6b4d4SGreg Roach/** 32e5a6b4d4SGreg Roach * Functions for managing users. 33e5a6b4d4SGreg Roach */ 34e5a6b4d4SGreg Roachclass UserService 35e5a6b4d4SGreg Roach{ 36e5a6b4d4SGreg Roach /** 37e5a6b4d4SGreg Roach * Find the user with a specified user_id. 38e5a6b4d4SGreg Roach * 39e5a6b4d4SGreg Roach * @param int|null $user_id 40e5a6b4d4SGreg Roach * 41e5a6b4d4SGreg Roach * @return User|null 42e5a6b4d4SGreg Roach */ 4325d7fe95SGreg Roach public function find($user_id): ?User 44e5a6b4d4SGreg Roach { 450b5fd0a6SGreg Roach return app('cache.array')->rememberForever(__CLASS__ . $user_id, static function () use ($user_id): ?User { 46e5a6b4d4SGreg Roach return DB::table('user') 47e5a6b4d4SGreg Roach ->where('user_id', '=', $user_id) 48e5a6b4d4SGreg Roach ->get() 49e5a6b4d4SGreg Roach ->map(User::rowMapper()) 50e5a6b4d4SGreg Roach ->first(); 51e5a6b4d4SGreg Roach }); 52e5a6b4d4SGreg Roach } 53e5a6b4d4SGreg Roach 54e5a6b4d4SGreg Roach /** 55e5a6b4d4SGreg Roach * Find the user with a specified email address. 56e5a6b4d4SGreg Roach * 57e5a6b4d4SGreg Roach * @param string $email 58e5a6b4d4SGreg Roach * 59e5a6b4d4SGreg Roach * @return User|null 60e5a6b4d4SGreg Roach */ 61e364afe4SGreg Roach public function findByEmail($email): ?User 62e5a6b4d4SGreg Roach { 63e5a6b4d4SGreg Roach return DB::table('user') 64e5a6b4d4SGreg Roach ->where('email', '=', $email) 65e5a6b4d4SGreg Roach ->get() 66e5a6b4d4SGreg Roach ->map(User::rowMapper()) 67e5a6b4d4SGreg Roach ->first(); 68e5a6b4d4SGreg Roach } 69e5a6b4d4SGreg Roach 70e5a6b4d4SGreg Roach /** 71e5a6b4d4SGreg Roach * Find the user with a specified user_name or email address. 72e5a6b4d4SGreg Roach * 73e5a6b4d4SGreg Roach * @param string $identifier 74e5a6b4d4SGreg Roach * 75e5a6b4d4SGreg Roach * @return User|null 76e5a6b4d4SGreg Roach */ 77e364afe4SGreg Roach public function findByIdentifier($identifier): ?User 78e5a6b4d4SGreg Roach { 79e5a6b4d4SGreg Roach return DB::table('user') 80e5a6b4d4SGreg Roach ->where('user_name', '=', $identifier) 81e5a6b4d4SGreg Roach ->orWhere('email', '=', $identifier) 82e5a6b4d4SGreg Roach ->get() 83e5a6b4d4SGreg Roach ->map(User::rowMapper()) 84e5a6b4d4SGreg Roach ->first(); 85e5a6b4d4SGreg Roach } 86e5a6b4d4SGreg Roach 87e5a6b4d4SGreg Roach /** 88e5a6b4d4SGreg Roach * Find the user(s) with a specified genealogy record. 89e5a6b4d4SGreg Roach * 90e5a6b4d4SGreg Roach * @param Individual $individual 91e5a6b4d4SGreg Roach * 9254c7f8dfSGreg Roach * @return Collection 93e5a6b4d4SGreg Roach */ 94e5a6b4d4SGreg Roach public function findByIndividual(Individual $individual): Collection 95e5a6b4d4SGreg Roach { 96e5a6b4d4SGreg Roach return DB::table('user') 97e5a6b4d4SGreg Roach ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id') 98e5a6b4d4SGreg Roach ->where('gedcom_id', '=', $individual->tree()->id()) 99e5a6b4d4SGreg Roach ->where('setting_value', '=', $individual->xref()) 100e5a6b4d4SGreg Roach ->where('setting_name', '=', 'gedcomid') 101e5a6b4d4SGreg Roach ->select(['user.*']) 102e5a6b4d4SGreg Roach ->get() 103e5a6b4d4SGreg Roach ->map(User::rowMapper()); 104e5a6b4d4SGreg Roach } 105e5a6b4d4SGreg Roach 106e5a6b4d4SGreg Roach /** 107e5a6b4d4SGreg Roach * Find the user with a specified user_name. 108e5a6b4d4SGreg Roach * 109e5a6b4d4SGreg Roach * @param string $user_name 110e5a6b4d4SGreg Roach * 111e5a6b4d4SGreg Roach * @return User|null 112e5a6b4d4SGreg Roach */ 113e364afe4SGreg Roach public function findByUserName($user_name): ?User 114e5a6b4d4SGreg Roach { 115e5a6b4d4SGreg Roach return DB::table('user') 116e5a6b4d4SGreg Roach ->where('user_name', '=', $user_name) 117e5a6b4d4SGreg Roach ->get() 118e5a6b4d4SGreg Roach ->map(User::rowMapper()) 119e5a6b4d4SGreg Roach ->first(); 120e5a6b4d4SGreg Roach } 121e5a6b4d4SGreg Roach 122e5a6b4d4SGreg Roach /** 123e5a6b4d4SGreg Roach * Get a list of all users. 124e5a6b4d4SGreg Roach * 12554c7f8dfSGreg Roach * @return Collection 126e5a6b4d4SGreg Roach */ 127e5a6b4d4SGreg Roach public function all(): Collection 128e5a6b4d4SGreg Roach { 129e5a6b4d4SGreg Roach return DB::table('user') 130e5a6b4d4SGreg Roach ->where('user_id', '>', 0) 131e5a6b4d4SGreg Roach ->orderBy('real_name') 132e5a6b4d4SGreg Roach ->get() 133e5a6b4d4SGreg Roach ->map(User::rowMapper()); 134e5a6b4d4SGreg Roach } 135e5a6b4d4SGreg Roach 136e5a6b4d4SGreg Roach /** 137e5a6b4d4SGreg Roach * Get a list of all administrators. 138e5a6b4d4SGreg Roach * 13954c7f8dfSGreg Roach * @return Collection 140e5a6b4d4SGreg Roach */ 141e5a6b4d4SGreg Roach public function administrators(): Collection 142e5a6b4d4SGreg Roach { 143e5a6b4d4SGreg Roach return DB::table('user') 1440b5fd0a6SGreg Roach ->join('user_setting', static function (JoinClause $join): void { 145e5a6b4d4SGreg Roach $join 146e5a6b4d4SGreg Roach ->on('user_setting.user_id', '=', 'user.user_id') 147e5a6b4d4SGreg Roach ->where('user_setting.setting_name', '=', 'canadmin') 148e5a6b4d4SGreg Roach ->where('user_setting.setting_value', '=', '1'); 149e5a6b4d4SGreg Roach }) 150e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 151e5a6b4d4SGreg Roach ->orderBy('real_name') 152e5a6b4d4SGreg Roach ->select(['user.*']) 153e5a6b4d4SGreg Roach ->get() 154e5a6b4d4SGreg Roach ->map(User::rowMapper()); 155e5a6b4d4SGreg Roach } 156e5a6b4d4SGreg Roach 157e5a6b4d4SGreg Roach /** 158e5a6b4d4SGreg Roach * Get a list of all managers. 159e5a6b4d4SGreg Roach * 16054c7f8dfSGreg Roach * @return Collection 161e5a6b4d4SGreg Roach */ 162e5a6b4d4SGreg Roach public function managers(): Collection 163e5a6b4d4SGreg Roach { 164e5a6b4d4SGreg Roach return DB::table('user') 1650b5fd0a6SGreg Roach ->join('user_gedcom_setting', static function (JoinClause $join): void { 166e5a6b4d4SGreg Roach $join 167e5a6b4d4SGreg Roach ->on('user_gedcom_setting.user_id', '=', 'user.user_id') 168e5a6b4d4SGreg Roach ->where('user_gedcom_setting.setting_name', '=', 'canedit') 169e5a6b4d4SGreg Roach ->where('user_gedcom_setting.setting_value', '=', 'admin'); 170e5a6b4d4SGreg Roach }) 171e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 172e5a6b4d4SGreg Roach ->orderBy('real_name') 173e5a6b4d4SGreg Roach ->select(['user.*']) 174e5a6b4d4SGreg Roach ->get() 175e5a6b4d4SGreg Roach ->map(User::rowMapper()); 176e5a6b4d4SGreg Roach } 177e5a6b4d4SGreg Roach 178e5a6b4d4SGreg Roach /** 179e5a6b4d4SGreg Roach * Get a list of all moderators. 180e5a6b4d4SGreg Roach * 18154c7f8dfSGreg Roach * @return Collection 182e5a6b4d4SGreg Roach */ 183e5a6b4d4SGreg Roach public function moderators(): Collection 184e5a6b4d4SGreg Roach { 185e5a6b4d4SGreg Roach return DB::table('user') 1860b5fd0a6SGreg Roach ->join('user_gedcom_setting', static function (JoinClause $join): void { 187e5a6b4d4SGreg Roach $join 188e5a6b4d4SGreg Roach ->on('user_gedcom_setting.user_id', '=', 'user.user_id') 189e5a6b4d4SGreg Roach ->where('user_gedcom_setting.setting_name', '=', 'canedit') 190e5a6b4d4SGreg Roach ->where('user_gedcom_setting.setting_value', '=', 'accept'); 191e5a6b4d4SGreg Roach }) 192e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 193e5a6b4d4SGreg Roach ->orderBy('real_name') 194e5a6b4d4SGreg Roach ->select(['user.*']) 195e5a6b4d4SGreg Roach ->get() 196e5a6b4d4SGreg Roach ->map(User::rowMapper()); 197e5a6b4d4SGreg Roach } 198e5a6b4d4SGreg Roach 199e5a6b4d4SGreg Roach /** 200e5a6b4d4SGreg Roach * Get a list of all verified users. 201e5a6b4d4SGreg Roach * 20254c7f8dfSGreg Roach * @return Collection 203e5a6b4d4SGreg Roach */ 204e5a6b4d4SGreg Roach public function unapproved(): Collection 205e5a6b4d4SGreg Roach { 206e5a6b4d4SGreg Roach return DB::table('user') 2070b5fd0a6SGreg Roach ->join('user_setting', static function (JoinClause $join): void { 208e5a6b4d4SGreg Roach $join 209e5a6b4d4SGreg Roach ->on('user_setting.user_id', '=', 'user.user_id') 210e5a6b4d4SGreg Roach ->where('user_setting.setting_name', '=', 'verified_by_admin') 211e5a6b4d4SGreg Roach ->where('user_setting.setting_value', '=', '0'); 212e5a6b4d4SGreg Roach }) 213e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 214e5a6b4d4SGreg Roach ->orderBy('real_name') 215e5a6b4d4SGreg Roach ->select(['user.*']) 216e5a6b4d4SGreg Roach ->get() 217e5a6b4d4SGreg Roach ->map(User::rowMapper()); 218e5a6b4d4SGreg Roach } 219e5a6b4d4SGreg Roach 220e5a6b4d4SGreg Roach /** 221e5a6b4d4SGreg Roach * Get a list of all verified users. 222e5a6b4d4SGreg Roach * 22354c7f8dfSGreg Roach * @return Collection 224e5a6b4d4SGreg Roach */ 225e5a6b4d4SGreg Roach public function unverified(): Collection 226e5a6b4d4SGreg Roach { 227e5a6b4d4SGreg Roach return DB::table('user') 2280b5fd0a6SGreg Roach ->join('user_setting', static function (JoinClause $join): void { 229e5a6b4d4SGreg Roach $join 230e5a6b4d4SGreg Roach ->on('user_setting.user_id', '=', 'user.user_id') 231e5a6b4d4SGreg Roach ->where('user_setting.setting_name', '=', 'verified') 232e5a6b4d4SGreg Roach ->where('user_setting.setting_value', '=', '0'); 233e5a6b4d4SGreg Roach }) 234e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 235e5a6b4d4SGreg Roach ->orderBy('real_name') 236e5a6b4d4SGreg Roach ->select(['user.*']) 237e5a6b4d4SGreg Roach ->get() 238e5a6b4d4SGreg Roach ->map(User::rowMapper()); 239e5a6b4d4SGreg Roach } 240e5a6b4d4SGreg Roach 241e5a6b4d4SGreg Roach /** 242e5a6b4d4SGreg Roach * Get a list of all users who are currently logged in. 243e5a6b4d4SGreg Roach * 24454c7f8dfSGreg Roach * @return Collection 245e5a6b4d4SGreg Roach */ 246e5a6b4d4SGreg Roach public function allLoggedIn(): Collection 247e5a6b4d4SGreg Roach { 248e5a6b4d4SGreg Roach return DB::table('user') 249e5a6b4d4SGreg Roach ->join('session', 'session.user_id', '=', 'user.user_id') 250e5a6b4d4SGreg Roach ->where('user.user_id', '>', 0) 251e5a6b4d4SGreg Roach ->orderBy('real_name') 252e5a6b4d4SGreg Roach ->select(['user.*']) 253e5a6b4d4SGreg Roach ->distinct() 254e5a6b4d4SGreg Roach ->get() 255e5a6b4d4SGreg Roach ->map(User::rowMapper()); 256e5a6b4d4SGreg Roach } 257e5a6b4d4SGreg Roach 258e5a6b4d4SGreg Roach /** 259e5a6b4d4SGreg Roach * Create a new user. 260e5a6b4d4SGreg Roach * The calling code needs to check for duplicates identifiers before calling 261e5a6b4d4SGreg Roach * this function. 262e5a6b4d4SGreg Roach * 263e5a6b4d4SGreg Roach * @param string $user_name 264e5a6b4d4SGreg Roach * @param string $real_name 265e5a6b4d4SGreg Roach * @param string $email 266e5a6b4d4SGreg Roach * @param string $password 267e5a6b4d4SGreg Roach * 268e5a6b4d4SGreg Roach * @return User 269e5a6b4d4SGreg Roach */ 2706be338f5SGreg Roach public function create(string $user_name, string $real_name, string $email, string $password): User 271e5a6b4d4SGreg Roach { 272e5a6b4d4SGreg Roach DB::table('user')->insert([ 273e5a6b4d4SGreg Roach 'user_name' => $user_name, 274e5a6b4d4SGreg Roach 'real_name' => $real_name, 275e5a6b4d4SGreg Roach 'email' => $email, 276e5a6b4d4SGreg Roach 'password' => password_hash($password, PASSWORD_DEFAULT), 277e5a6b4d4SGreg Roach ]); 278e5a6b4d4SGreg Roach 279e5a6b4d4SGreg Roach $user_id = (int) DB::connection()->getPdo()->lastInsertId(); 280e5a6b4d4SGreg Roach 281e5a6b4d4SGreg Roach return new User($user_id, $user_name, $real_name, $email); 282e5a6b4d4SGreg Roach } 283e5a6b4d4SGreg Roach 284e5a6b4d4SGreg Roach /** 285e5a6b4d4SGreg Roach * Delete a user 286e5a6b4d4SGreg Roach * 287e5a6b4d4SGreg Roach * @param User $user 288e5a6b4d4SGreg Roach * 289e5a6b4d4SGreg Roach * @return void 290e5a6b4d4SGreg Roach */ 291e364afe4SGreg Roach public function delete(User $user): void 292e5a6b4d4SGreg Roach { 293e5a6b4d4SGreg Roach // Don't delete the logs, just set the user to null. 294e5a6b4d4SGreg Roach DB::table('log') 295e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 296e5a6b4d4SGreg Roach ->update(['user_id' => null]); 297e5a6b4d4SGreg Roach 298e5a6b4d4SGreg Roach // Take over the user’s pending changes. (What else could we do with them?) 299e5a6b4d4SGreg Roach DB::table('change') 300e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 301e5a6b4d4SGreg Roach ->where('status', '=', 'rejected') 302e5a6b4d4SGreg Roach ->delete(); 303e5a6b4d4SGreg Roach 304e5a6b4d4SGreg Roach DB::table('change') 305e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 306e5a6b4d4SGreg Roach ->update(['user_id' => Auth::id()]); 307e5a6b4d4SGreg Roach 308e5a6b4d4SGreg Roach // Delete settings and preferences 309e5a6b4d4SGreg Roach DB::table('block_setting') 310e5a6b4d4SGreg Roach ->join('block', 'block_setting.block_id', '=', 'block.block_id') 311e5a6b4d4SGreg Roach ->where('user_id', '=', $user->id()) 312e5a6b4d4SGreg Roach ->delete(); 313e5a6b4d4SGreg Roach 314e5a6b4d4SGreg Roach DB::table('block')->where('user_id', '=', $user->id())->delete(); 315e5a6b4d4SGreg Roach DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete(); 316e5a6b4d4SGreg Roach DB::table('user_setting')->where('user_id', '=', $user->id())->delete(); 317e5a6b4d4SGreg Roach DB::table('message')->where('user_id', '=', $user->id())->delete(); 318e5a6b4d4SGreg Roach DB::table('user')->where('user_id', '=', $user->id())->delete(); 319e5a6b4d4SGreg Roach } 32086730b84SGreg Roach 32186730b84SGreg Roach /** 3224db4b4a9SGreg Roach * @param User $contact_user 32386730b84SGreg Roach * 32486730b84SGreg Roach * @return string 32586730b84SGreg Roach */ 326dcbe9044SGreg Roach public function contactLink(User $contact_user): string 327dcbe9044SGreg Roach { 328cab242e7SGreg Roach $tree = app(Tree::class); 329cab242e7SGreg Roach $user = app(UserInterface::class); 3306ccdf4f0SGreg Roach $request = app(ServerRequestInterface::class); 33186730b84SGreg Roach 33286730b84SGreg Roach if ($contact_user->getPreference('contactmethod') === 'mailto') { 33386730b84SGreg Roach $url = 'mailto:' . $contact_user->email(); 33486730b84SGreg Roach } elseif ($user instanceof User) { 33586730b84SGreg Roach // Logged-in users send direct messages 3365d284d5dSGreg Roach $url = route('message', ['to' => $contact_user->userName()]); 33786730b84SGreg Roach } else { 33886730b84SGreg Roach // Visitors use the contact form. 33986730b84SGreg Roach $url = route('contact', [ 34086730b84SGreg Roach 'ged' => $tree ? $tree->name() : '', 34186730b84SGreg Roach 'to' => $contact_user->userName(), 342*add3fa41SGreg Roach 'url' => $request->getAttribute('request_uri'), 34386730b84SGreg Roach ]); 34486730b84SGreg Roach } 34586730b84SGreg Roach 34686730b84SGreg Roach return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>'; 34786730b84SGreg Roach } 348e5a6b4d4SGreg Roach} 349