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 * @return User[] 94 */ 95 public function findByIndividual(Individual $individual): Collection 96 { 97 return DB::table('user') 98 ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id') 99 ->where('gedcom_id', '=', $individual->tree()->id()) 100 ->where('setting_value', '=', $individual->xref()) 101 ->where('setting_name', '=', 'gedcomid') 102 ->select(['user.*']) 103 ->get() 104 ->map(User::rowMapper()); 105 } 106 107 /** 108 * Find the user with a specified user_name. 109 * 110 * @param string $user_name 111 * 112 * @return User|null 113 */ 114 public function findByUserName($user_name): ?User 115 { 116 return DB::table('user') 117 ->where('user_name', '=', $user_name) 118 ->get() 119 ->map(User::rowMapper()) 120 ->first(); 121 } 122 123 /** 124 * Get a list of all users. 125 * 126 * @return Collection 127 * @return User[] 128 */ 129 public function all(): Collection 130 { 131 return DB::table('user') 132 ->where('user_id', '>', 0) 133 ->orderBy('real_name') 134 ->get() 135 ->map(User::rowMapper()); 136 } 137 138 /** 139 * Get a list of all administrators. 140 * 141 * @return Collection 142 * @return User[] 143 */ 144 public function administrators(): Collection 145 { 146 return DB::table('user') 147 ->join('user_setting', static function (JoinClause $join): void { 148 $join 149 ->on('user_setting.user_id', '=', 'user.user_id') 150 ->where('user_setting.setting_name', '=', 'canadmin') 151 ->where('user_setting.setting_value', '=', '1'); 152 }) 153 ->where('user.user_id', '>', 0) 154 ->orderBy('real_name') 155 ->select(['user.*']) 156 ->get() 157 ->map(User::rowMapper()); 158 } 159 160 /** 161 * Get a list of all managers. 162 * 163 * @return Collection 164 * @return User[] 165 */ 166 public function managers(): Collection 167 { 168 return DB::table('user') 169 ->join('user_gedcom_setting', static function (JoinClause $join): void { 170 $join 171 ->on('user_gedcom_setting.user_id', '=', 'user.user_id') 172 ->where('user_gedcom_setting.setting_name', '=', 'canedit') 173 ->where('user_gedcom_setting.setting_value', '=', 'admin'); 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 moderators. 184 * 185 * @return Collection 186 * @return User[] 187 */ 188 public function moderators(): Collection 189 { 190 return DB::table('user') 191 ->join('user_gedcom_setting', static function (JoinClause $join): void { 192 $join 193 ->on('user_gedcom_setting.user_id', '=', 'user.user_id') 194 ->where('user_gedcom_setting.setting_name', '=', 'canedit') 195 ->where('user_gedcom_setting.setting_value', '=', 'accept'); 196 }) 197 ->where('user.user_id', '>', 0) 198 ->orderBy('real_name') 199 ->select(['user.*']) 200 ->get() 201 ->map(User::rowMapper()); 202 } 203 204 /** 205 * Get a list of all verified users. 206 * 207 * @return Collection 208 * @return User[] 209 */ 210 public function unapproved(): Collection 211 { 212 return DB::table('user') 213 ->join('user_setting', static function (JoinClause $join): void { 214 $join 215 ->on('user_setting.user_id', '=', 'user.user_id') 216 ->where('user_setting.setting_name', '=', 'verified_by_admin') 217 ->where('user_setting.setting_value', '=', '0'); 218 }) 219 ->where('user.user_id', '>', 0) 220 ->orderBy('real_name') 221 ->select(['user.*']) 222 ->get() 223 ->map(User::rowMapper()); 224 } 225 226 /** 227 * Get a list of all verified users. 228 * 229 * @return Collection 230 * @return User[] 231 */ 232 public function unverified(): Collection 233 { 234 return DB::table('user') 235 ->join('user_setting', static function (JoinClause $join): void { 236 $join 237 ->on('user_setting.user_id', '=', 'user.user_id') 238 ->where('user_setting.setting_name', '=', 'verified') 239 ->where('user_setting.setting_value', '=', '0'); 240 }) 241 ->where('user.user_id', '>', 0) 242 ->orderBy('real_name') 243 ->select(['user.*']) 244 ->get() 245 ->map(User::rowMapper()); 246 } 247 248 /** 249 * Get a list of all users who are currently logged in. 250 * 251 * @return Collection 252 * @return User[] 253 */ 254 public function allLoggedIn(): Collection 255 { 256 return DB::table('user') 257 ->join('session', 'session.user_id', '=', 'user.user_id') 258 ->where('user.user_id', '>', 0) 259 ->orderBy('real_name') 260 ->select(['user.*']) 261 ->distinct() 262 ->get() 263 ->map(User::rowMapper()); 264 } 265 266 /** 267 * Create a new user. 268 * The calling code needs to check for duplicates identifiers before calling 269 * this function. 270 * 271 * @param string $user_name 272 * @param string $real_name 273 * @param string $email 274 * @param string $password 275 * 276 * @return User 277 */ 278 public function create(string $user_name, string $real_name, string $email, string $password): User 279 { 280 DB::table('user')->insert([ 281 'user_name' => $user_name, 282 'real_name' => $real_name, 283 'email' => $email, 284 'password' => password_hash($password, PASSWORD_DEFAULT), 285 ]); 286 287 $user_id = (int) DB::connection()->getPdo()->lastInsertId(); 288 289 return new User($user_id, $user_name, $real_name, $email); 290 } 291 292 /** 293 * Delete a user 294 * 295 * @param User $user 296 * 297 * @return void 298 */ 299 public function delete(User $user): void 300 { 301 // Don't delete the logs, just set the user to null. 302 DB::table('log') 303 ->where('user_id', '=', $user->id()) 304 ->update(['user_id' => null]); 305 306 // Take over the user’s pending changes. (What else could we do with them?) 307 DB::table('change') 308 ->where('user_id', '=', $user->id()) 309 ->where('status', '=', 'rejected') 310 ->delete(); 311 312 DB::table('change') 313 ->where('user_id', '=', $user->id()) 314 ->update(['user_id' => Auth::id()]); 315 316 // Delete settings and preferences 317 DB::table('block_setting') 318 ->join('block', 'block_setting.block_id', '=', 'block.block_id') 319 ->where('user_id', '=', $user->id()) 320 ->delete(); 321 322 DB::table('block')->where('user_id', '=', $user->id())->delete(); 323 DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete(); 324 DB::table('user_setting')->where('user_id', '=', $user->id())->delete(); 325 DB::table('message')->where('user_id', '=', $user->id())->delete(); 326 DB::table('user')->where('user_id', '=', $user->id())->delete(); 327 } 328 329 /** 330 * @param User $contact_user 331 * 332 * @return string 333 */ 334 public function contactLink(User $contact_user): string 335 { 336 $tree = app(Tree::class); 337 $user = app(UserInterface::class); 338 $request = app(ServerRequestInterface::class); 339 340 if ($contact_user->getPreference('contactmethod') === 'mailto') { 341 $url = 'mailto:' . $contact_user->email(); 342 } elseif ($user instanceof User) { 343 // Logged-in users send direct messages 344 $url = route('message', ['to' => $contact_user->userName()]); 345 } else { 346 // Visitors use the contact form. 347 $url = route('contact', [ 348 'ged' => $tree ? $tree->name() : '', 349 'to' => $contact_user->userName(), 350 'url' => (string) $request->getUri(), 351 ]); 352 } 353 354 return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>'; 355 } 356} 357