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 Symfony\Component\HttpFoundation\Request; 29 30/** 31 * Functions for managing users. 32 */ 33class UserService 34{ 35 /** 36 * Find the user with a specified user_id. 37 * 38 * @param int|null $user_id 39 * 40 * @return User|null 41 */ 42 public function find($user_id): ?User 43 { 44 return app('cache.array')->rememberForever(__CLASS__ . $user_id, static function () use ($user_id): ?User { 45 return DB::table('user') 46 ->where('user_id', '=', $user_id) 47 ->get() 48 ->map(User::rowMapper()) 49 ->first(); 50 }); 51 } 52 53 /** 54 * Find the user with a specified email address. 55 * 56 * @param string $email 57 * 58 * @return User|null 59 */ 60 public function findByEmail($email): ?User 61 { 62 return DB::table('user') 63 ->where('email', '=', $email) 64 ->get() 65 ->map(User::rowMapper()) 66 ->first(); 67 } 68 69 /** 70 * Find the user with a specified user_name or email address. 71 * 72 * @param string $identifier 73 * 74 * @return User|null 75 */ 76 public function findByIdentifier($identifier): ?User 77 { 78 return DB::table('user') 79 ->where('user_name', '=', $identifier) 80 ->orWhere('email', '=', $identifier) 81 ->get() 82 ->map(User::rowMapper()) 83 ->first(); 84 } 85 86 /** 87 * Find the user(s) with a specified genealogy record. 88 * 89 * @param Individual $individual 90 * 91 * @return Collection 92 * @return User[] 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 * @return User[] 127 */ 128 public function all(): Collection 129 { 130 return DB::table('user') 131 ->where('user_id', '>', 0) 132 ->orderBy('real_name') 133 ->get() 134 ->map(User::rowMapper()); 135 } 136 137 /** 138 * Get a list of all administrators. 139 * 140 * @return Collection 141 * @return User[] 142 */ 143 public function administrators(): Collection 144 { 145 return DB::table('user') 146 ->join('user_setting', static function (JoinClause $join): void { 147 $join 148 ->on('user_setting.user_id', '=', 'user.user_id') 149 ->where('user_setting.setting_name', '=', 'canadmin') 150 ->where('user_setting.setting_value', '=', '1'); 151 }) 152 ->where('user.user_id', '>', 0) 153 ->orderBy('real_name') 154 ->select(['user.*']) 155 ->get() 156 ->map(User::rowMapper()); 157 } 158 159 /** 160 * Get a list of all managers. 161 * 162 * @return Collection 163 * @return User[] 164 */ 165 public function managers(): Collection 166 { 167 return DB::table('user') 168 ->join('user_gedcom_setting', static function (JoinClause $join): void { 169 $join 170 ->on('user_gedcom_setting.user_id', '=', 'user.user_id') 171 ->where('user_gedcom_setting.setting_name', '=', 'canedit') 172 ->where('user_gedcom_setting.setting_value', '=', 'admin'); 173 }) 174 ->where('user.user_id', '>', 0) 175 ->orderBy('real_name') 176 ->select(['user.*']) 177 ->get() 178 ->map(User::rowMapper()); 179 } 180 181 /** 182 * Get a list of all moderators. 183 * 184 * @return Collection 185 * @return User[] 186 */ 187 public function moderators(): 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', '=', 'accept'); 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 verified users. 205 * 206 * @return Collection 207 * @return User[] 208 */ 209 public function unapproved(): Collection 210 { 211 return DB::table('user') 212 ->join('user_setting', static function (JoinClause $join): void { 213 $join 214 ->on('user_setting.user_id', '=', 'user.user_id') 215 ->where('user_setting.setting_name', '=', 'verified_by_admin') 216 ->where('user_setting.setting_value', '=', '0'); 217 }) 218 ->where('user.user_id', '>', 0) 219 ->orderBy('real_name') 220 ->select(['user.*']) 221 ->get() 222 ->map(User::rowMapper()); 223 } 224 225 /** 226 * Get a list of all verified users. 227 * 228 * @return Collection 229 * @return User[] 230 */ 231 public function unverified(): Collection 232 { 233 return DB::table('user') 234 ->join('user_setting', static function (JoinClause $join): void { 235 $join 236 ->on('user_setting.user_id', '=', 'user.user_id') 237 ->where('user_setting.setting_name', '=', 'verified') 238 ->where('user_setting.setting_value', '=', '0'); 239 }) 240 ->where('user.user_id', '>', 0) 241 ->orderBy('real_name') 242 ->select(['user.*']) 243 ->get() 244 ->map(User::rowMapper()); 245 } 246 247 /** 248 * Get a list of all users who are currently logged in. 249 * 250 * @return Collection 251 * @return User[] 252 */ 253 public function allLoggedIn(): Collection 254 { 255 return DB::table('user') 256 ->join('session', 'session.user_id', '=', 'user.user_id') 257 ->where('user.user_id', '>', 0) 258 ->orderBy('real_name') 259 ->select(['user.*']) 260 ->distinct() 261 ->get() 262 ->map(User::rowMapper()); 263 } 264 265 /** 266 * Create a new user. 267 * The calling code needs to check for duplicates identifiers before calling 268 * this function. 269 * 270 * @param string $user_name 271 * @param string $real_name 272 * @param string $email 273 * @param string $password 274 * 275 * @return User 276 */ 277 public function create(string $user_name, string $real_name, string $email, string $password): User 278 { 279 DB::table('user')->insert([ 280 'user_name' => $user_name, 281 'real_name' => $real_name, 282 'email' => $email, 283 'password' => password_hash($password, PASSWORD_DEFAULT), 284 ]); 285 286 $user_id = (int) DB::connection()->getPdo()->lastInsertId(); 287 288 return new User($user_id, $user_name, $real_name, $email); 289 } 290 291 /** 292 * Delete a user 293 * 294 * @param User $user 295 * 296 * @return void 297 */ 298 public function delete(User $user): void 299 { 300 // Don't delete the logs, just set the user to null. 301 DB::table('log') 302 ->where('user_id', '=', $user->id()) 303 ->update(['user_id' => null]); 304 305 // Take over the user’s pending changes. (What else could we do with them?) 306 DB::table('change') 307 ->where('user_id', '=', $user->id()) 308 ->where('status', '=', 'rejected') 309 ->delete(); 310 311 DB::table('change') 312 ->where('user_id', '=', $user->id()) 313 ->update(['user_id' => Auth::id()]); 314 315 // Delete settings and preferences 316 DB::table('block_setting') 317 ->join('block', 'block_setting.block_id', '=', 'block.block_id') 318 ->where('user_id', '=', $user->id()) 319 ->delete(); 320 321 DB::table('block')->where('user_id', '=', $user->id())->delete(); 322 DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete(); 323 DB::table('user_setting')->where('user_id', '=', $user->id())->delete(); 324 DB::table('message')->where('user_id', '=', $user->id())->delete(); 325 DB::table('user')->where('user_id', '=', $user->id())->delete(); 326 } 327 328 /** 329 * @param User $contact_user 330 * 331 * @return string 332 */ 333 public function contactLink(User $contact_user): string 334 { 335 $tree = app(Tree::class); 336 $user = app(UserInterface::class); 337 $request = app(Request::class); 338 339 if ($contact_user->getPreference('contactmethod') === 'mailto') { 340 $url = 'mailto:' . $contact_user->email(); 341 } elseif ($user instanceof User) { 342 // Logged-in users send direct messages 343 $url = route('message', ['to' => $contact_user->userName()]); 344 } else { 345 // Visitors use the contact form. 346 $url = route('contact', [ 347 'ged' => $tree ? $tree->name() : '', 348 'to' => $contact_user->userName(), 349 'url' => $request->getRequestUri(), 350 ]); 351 } 352 353 return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>'; 354 } 355} 356