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