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