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