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