xref: /webtrees/app/Services/UserService.php (revision 1ab2f3860fb69f09fd8de27f7972d3e9950ca62a)
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', 'user_setting.user_id', '=', 'user.user_id')
173            ->where('user_setting.setting_name', '=', 'canadmin')
174            ->where('user_setting.setting_value', '=', '1')
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', '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            ->where('user.user_id', '>', 0)
194            ->groupBy(['user.user_id'])
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', 'user_gedcom_setting.user_id', '=', 'user.user_id')
210            ->where('user_gedcom_setting.setting_name', '=', 'canedit')
211            ->where('user_gedcom_setting.setting_value', '=', 'accept')
212            ->where('user.user_id', '>', 0)
213            ->groupBy(['user.user_id'])
214            ->orderBy('real_name')
215            ->select(['user.*'])
216            ->get()
217            ->map(User::rowMapper());
218    }
219
220    /**
221     * Get a list of all verified users.
222     *
223     * @return Collection
224     */
225    public function unapproved(): Collection
226    {
227        return DB::table('user')
228            ->join('user_setting', 'user_setting.user_id', '=', 'user.user_id')
229            ->where('user_setting.setting_name', '=', 'verified_by_admin')
230            ->where('user_setting.setting_value', '<>', '1')
231            ->where('user.user_id', '>', 0)
232            ->orderBy('real_name')
233            ->select(['user.*'])
234            ->get()
235            ->map(User::rowMapper());
236    }
237
238    /**
239     * Get a list of all verified users.
240     *
241     * @return Collection
242     */
243    public function unverified(): Collection
244    {
245        return DB::table('user')
246            ->join('user_setting', 'user_setting.user_id', '=', 'user.user_id')
247            ->where('user_setting.setting_name', '=', 'verified')
248            ->where('user_setting.setting_value', '<>', '1')
249            ->where('user.user_id', '>', 0)
250            ->orderBy('real_name')
251            ->select(['user.*'])
252            ->get()
253            ->map(User::rowMapper());
254    }
255
256    /**
257     * Get a list of all users who are currently logged in.
258     *
259     * @return Collection
260     */
261    public function allLoggedIn(): Collection
262    {
263        return DB::table('user')
264            ->join('session', 'session.user_id', '=', 'user.user_id')
265            ->where('user.user_id', '>', 0)
266            ->orderBy('real_name')
267            ->select(['user.*'])
268            ->distinct()
269            ->get()
270            ->map(User::rowMapper());
271    }
272
273    /**
274     * Create a new user.
275     * The calling code needs to check for duplicates identifiers before calling
276     * this function.
277     *
278     * @param string $user_name
279     * @param string $real_name
280     * @param string $email
281     * @param string $password
282     *
283     * @return User
284     */
285    public function create(string $user_name, string $real_name, string $email, string $password): User
286    {
287        DB::table('user')->insert([
288            'user_name' => $user_name,
289            'real_name' => $real_name,
290            'email'     => $email,
291            'password'  => password_hash($password, PASSWORD_DEFAULT),
292        ]);
293
294        $user_id = (int) DB::connection()->getPdo()->lastInsertId();
295
296        return new User($user_id, $user_name, $real_name, $email);
297    }
298
299    /**
300     * Delete a user
301     *
302     * @param User $user
303     *
304     * @return void
305     */
306    public function delete(User $user): void
307    {
308        // Don't delete the logs, just set the user to null.
309        DB::table('log')
310            ->where('user_id', '=', $user->id())
311            ->update(['user_id' => null]);
312
313        // Take over the user’s pending changes. (What else could we do with them?)
314        DB::table('change')
315            ->where('user_id', '=', $user->id())
316            ->where('status', '=', 'rejected')
317            ->delete();
318
319        DB::table('change')
320            ->where('user_id', '=', $user->id())
321            ->update(['user_id' => Auth::id()]);
322
323        // Delete settings and preferences
324        DB::table('block_setting')
325            ->join('block', 'block_setting.block_id', '=', 'block.block_id')
326            ->where('user_id', '=', $user->id())
327            ->delete();
328
329        DB::table('block')->where('user_id', '=', $user->id())->delete();
330        DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete();
331        DB::table('user_setting')->where('user_id', '=', $user->id())->delete();
332        DB::table('message')->where('user_id', '=', $user->id())->delete();
333        DB::table('user')->where('user_id', '=', $user->id())->delete();
334    }
335
336    /**
337     * @param User                   $contact_user
338     * @param ServerRequestInterface $request
339     *
340     * @return string
341     */
342    public function contactLink(User $contact_user, ServerRequestInterface $request): string
343    {
344        $tree = $request->getAttribute('tree');
345        assert($tree instanceof Tree);
346
347        $user = $request->getAttribute('user');
348
349        if ($contact_user->getPreference('contactmethod') === 'mailto') {
350            $url = 'mailto:' . $contact_user->email();
351        } elseif ($user instanceof User) {
352            // Logged-in users send direct messages
353            $url = route(MessagePage::class, ['to' => $contact_user->userName(), 'tree' => $tree->name()]);
354        } else {
355            // Visitors use the contact form.
356            $url = route(ContactPage::class, [
357                'to'   => $contact_user->userName(),
358                'tree' => $tree->name(),
359                'url'  => (string) $request->getUri(),
360            ]);
361        }
362
363        return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>';
364    }
365}
366