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