xref: /webtrees/app/Services/UserService.php (revision fcfa147e10aaa6c7ff580c29bd6e5b88666befc1)
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 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Services;
21
22use Fisharebest\Webtrees\Auth;
23use Fisharebest\Webtrees\Carbon;
24use Fisharebest\Webtrees\Individual;
25use Fisharebest\Webtrees\Tree;
26use Fisharebest\Webtrees\User;
27use Illuminate\Database\Capsule\Manager as DB;
28use Illuminate\Database\Query\JoinClause;
29use Illuminate\Support\Collection;
30use InvalidArgumentException;
31use Psr\Http\Message\ServerRequestInterface;
32
33use function app;
34use function assert;
35
36/**
37 * Functions for managing users.
38 */
39class UserService
40{
41    /**
42     * Find the user with a specified user_id.
43     *
44     * @param int|null $user_id
45     *
46     * @return User|null
47     */
48    public function find($user_id): ?User
49    {
50        return app('cache.array')->rememberForever(__CLASS__ . $user_id, static function () use ($user_id): ?User {
51            return DB::table('user')
52                ->where('user_id', '=', $user_id)
53                ->get()
54                ->map(User::rowMapper())
55                ->first();
56        });
57    }
58
59    /**
60     * Find the user with a specified email address.
61     *
62     * @param string $email
63     *
64     * @return User|null
65     */
66    public function findByEmail($email): ?User
67    {
68        return DB::table('user')
69            ->where('email', '=', $email)
70            ->get()
71            ->map(User::rowMapper())
72            ->first();
73    }
74
75    /**
76     * Find the user with a specified user_name or email address.
77     *
78     * @param string $identifier
79     *
80     * @return User|null
81     */
82    public function findByIdentifier($identifier): ?User
83    {
84        return DB::table('user')
85            ->where('user_name', '=', $identifier)
86            ->orWhere('email', '=', $identifier)
87            ->get()
88            ->map(User::rowMapper())
89            ->first();
90    }
91
92    /**
93     * Find the user(s) with a specified genealogy record.
94     *
95     * @param Individual $individual
96     *
97     * @return Collection
98     */
99    public function findByIndividual(Individual $individual): Collection
100    {
101        return DB::table('user')
102            ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id')
103            ->where('gedcom_id', '=', $individual->tree()->id())
104            ->where('setting_value', '=', $individual->xref())
105            ->where('setting_name', '=', 'gedcomid')
106            ->select(['user.*'])
107            ->get()
108            ->map(User::rowMapper());
109    }
110
111    /**
112     * Find the user with a specified password reset token.
113     *
114     * @param string $token
115     *
116     * @return User|null
117     */
118    public function findByToken(string $token): ?User
119    {
120        return DB::table('user')
121            ->join('user_setting AS us1', 'us1.user_id', '=', 'user.user_id')
122            ->where('us1.setting_name', '=', 'password-token')
123            ->where('us1.setting_value', '=', $token)
124            ->join('user_setting AS us2', 'us2.user_id', '=', 'user.user_id')
125            ->where('us2.setting_name', '=', 'password-token-expire')
126            ->where('us2.setting_value', '>', Carbon::now()->timestamp)
127            ->select(['user.*'])
128            ->get()
129            ->map(User::rowMapper())
130            ->first();
131    }
132
133    /**
134     * Find the user with a specified user_name.
135     *
136     * @param string $user_name
137     *
138     * @return User|null
139     */
140    public function findByUserName($user_name): ?User
141    {
142        return DB::table('user')
143            ->where('user_name', '=', $user_name)
144            ->get()
145            ->map(User::rowMapper())
146            ->first();
147    }
148
149    /**
150     * Get a list of all users.
151     *
152     * @return Collection
153     */
154    public function all(): Collection
155    {
156        return DB::table('user')
157            ->where('user_id', '>', 0)
158            ->orderBy('real_name')
159            ->get()
160            ->map(User::rowMapper());
161    }
162
163    /**
164     * Get a list of all administrators.
165     *
166     * @return Collection
167     */
168    public function administrators(): Collection
169    {
170        return DB::table('user')
171            ->join('user_setting', static function (JoinClause $join): void {
172                $join
173                    ->on('user_setting.user_id', '=', 'user.user_id')
174                    ->where('user_setting.setting_name', '=', 'canadmin')
175                    ->where('user_setting.setting_value', '=', '1');
176            })
177            ->where('user.user_id', '>', 0)
178            ->orderBy('real_name')
179            ->select(['user.*'])
180            ->get()
181            ->map(User::rowMapper());
182    }
183
184    /**
185     * Get a list of all managers.
186     *
187     * @return Collection
188     */
189    public function managers(): Collection
190    {
191        return DB::table('user')
192            ->join('user_gedcom_setting', static function (JoinClause $join): void {
193                $join
194                    ->on('user_gedcom_setting.user_id', '=', 'user.user_id')
195                    ->where('user_gedcom_setting.setting_name', '=', 'canedit')
196                    ->where('user_gedcom_setting.setting_value', '=', 'admin');
197            })
198            ->where('user.user_id', '>', 0)
199            ->orderBy('real_name')
200            ->select(['user.*'])
201            ->get()
202            ->map(User::rowMapper());
203    }
204
205    /**
206     * Get a list of all moderators.
207     *
208     * @return Collection
209     */
210    public function moderators(): Collection
211    {
212        return DB::table('user')
213            ->join('user_gedcom_setting', static function (JoinClause $join): void {
214                $join
215                    ->on('user_gedcom_setting.user_id', '=', 'user.user_id')
216                    ->where('user_gedcom_setting.setting_name', '=', 'canedit')
217                    ->where('user_gedcom_setting.setting_value', '=', 'accept');
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     */
231    public function unapproved(): 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_by_admin')
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 verified users.
249     *
250     * @return Collection
251     */
252    public function unverified(): Collection
253    {
254        return DB::table('user')
255            ->join('user_setting', static function (JoinClause $join): void {
256                $join
257                    ->on('user_setting.user_id', '=', 'user.user_id')
258                    ->where('user_setting.setting_name', '=', 'verified')
259                    ->where('user_setting.setting_value', '=', '0');
260            })
261            ->where('user.user_id', '>', 0)
262            ->orderBy('real_name')
263            ->select(['user.*'])
264            ->get()
265            ->map(User::rowMapper());
266    }
267
268    /**
269     * Get a list of all users who are currently logged in.
270     *
271     * @return Collection
272     */
273    public function allLoggedIn(): Collection
274    {
275        return DB::table('user')
276            ->join('session', 'session.user_id', '=', 'user.user_id')
277            ->where('user.user_id', '>', 0)
278            ->orderBy('real_name')
279            ->select(['user.*'])
280            ->distinct()
281            ->get()
282            ->map(User::rowMapper());
283    }
284
285    /**
286     * Create a new user.
287     * The calling code needs to check for duplicates identifiers before calling
288     * this function.
289     *
290     * @param string $user_name
291     * @param string $real_name
292     * @param string $email
293     * @param string $password
294     *
295     * @return User
296     */
297    public function create(string $user_name, string $real_name, string $email, string $password): User
298    {
299        DB::table('user')->insert([
300            'user_name' => $user_name,
301            'real_name' => $real_name,
302            'email'     => $email,
303            'password'  => password_hash($password, PASSWORD_DEFAULT),
304        ]);
305
306        $user_id = (int) DB::connection()->getPdo()->lastInsertId();
307
308        return new User($user_id, $user_name, $real_name, $email);
309    }
310
311    /**
312     * Delete a user
313     *
314     * @param User $user
315     *
316     * @return void
317     */
318    public function delete(User $user): void
319    {
320        // Don't delete the logs, just set the user to null.
321        DB::table('log')
322            ->where('user_id', '=', $user->id())
323            ->update(['user_id' => null]);
324
325        // Take over the user’s pending changes. (What else could we do with them?)
326        DB::table('change')
327            ->where('user_id', '=', $user->id())
328            ->where('status', '=', 'rejected')
329            ->delete();
330
331        DB::table('change')
332            ->where('user_id', '=', $user->id())
333            ->update(['user_id' => Auth::id()]);
334
335        // Delete settings and preferences
336        DB::table('block_setting')
337            ->join('block', 'block_setting.block_id', '=', 'block.block_id')
338            ->where('user_id', '=', $user->id())
339            ->delete();
340
341        DB::table('block')->where('user_id', '=', $user->id())->delete();
342        DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete();
343        DB::table('user_setting')->where('user_id', '=', $user->id())->delete();
344        DB::table('message')->where('user_id', '=', $user->id())->delete();
345        DB::table('user')->where('user_id', '=', $user->id())->delete();
346    }
347
348    /**
349     * @param User                   $contact_user
350     * @param ServerRequestInterface $request
351     *
352     * @return string
353     */
354    public function contactLink(User $contact_user, ServerRequestInterface $request): string
355    {
356        $tree = $request->getAttribute('tree');
357        assert($tree instanceof Tree, new InvalidArgumentException());
358
359        $user = $request->getAttribute('user');
360
361        if ($contact_user->getPreference('contactmethod') === 'mailto') {
362            $url = 'mailto:' . $contact_user->email();
363        } elseif ($user instanceof User) {
364            // Logged-in users send direct messages
365            $url = route('message', ['to' => $contact_user->userName(), 'tree' => $tree->name()]);
366        } else {
367            // Visitors use the contact form.
368            $url = route('contact', [
369                'to'   => $contact_user->userName(),
370                'tree' => $tree->name(),
371                'url'  => (string) $request->getUri(),
372            ]);
373        }
374
375        return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>';
376    }
377}
378