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