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