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