xref: /webtrees/app/Services/UserService.php (revision 2474349caa7b2aa11793e6008b000ebb1812e425)
1e5a6b4d4SGreg Roach<?php
23976b470SGreg Roach
3e5a6b4d4SGreg Roach/**
4e5a6b4d4SGreg Roach * webtrees: online genealogy
5e5a6b4d4SGreg Roach * Copyright (C) 2019 webtrees development team
6e5a6b4d4SGreg Roach * This program is free software: you can redistribute it and/or modify
7e5a6b4d4SGreg Roach * it under the terms of the GNU General Public License as published by
8e5a6b4d4SGreg Roach * the Free Software Foundation, either version 3 of the License, or
9e5a6b4d4SGreg Roach * (at your option) any later version.
10e5a6b4d4SGreg Roach * This program is distributed in the hope that it will be useful,
11e5a6b4d4SGreg Roach * but WITHOUT ANY WARRANTY; without even the implied warranty of
12e5a6b4d4SGreg Roach * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13e5a6b4d4SGreg Roach * GNU General Public License for more details.
14e5a6b4d4SGreg Roach * You should have received a copy of the GNU General Public License
15e5a6b4d4SGreg Roach * along with this program. If not, see <http://www.gnu.org/licenses/>.
16e5a6b4d4SGreg Roach */
17fcfa147eSGreg Roach
18e5a6b4d4SGreg Roachdeclare(strict_types=1);
19e5a6b4d4SGreg Roach
20e5a6b4d4SGreg Roachnamespace Fisharebest\Webtrees\Services;
21e5a6b4d4SGreg Roach
22*2474349cSGreg Roachuse Closure;
23e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Auth;
24a00bcc63SGreg Roachuse Fisharebest\Webtrees\Carbon;
25*2474349cSGreg Roachuse Fisharebest\Webtrees\Contracts\UserInterface;
26e381f98dSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\ContactPage;
27e381f98dSGreg Roachuse Fisharebest\Webtrees\Http\RequestHandlers\MessagePage;
28e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\Individual;
295229eadeSGreg Roachuse Fisharebest\Webtrees\Tree;
30e5a6b4d4SGreg Roachuse Fisharebest\Webtrees\User;
31e5a6b4d4SGreg Roachuse Illuminate\Database\Capsule\Manager as DB;
32e5a6b4d4SGreg Roachuse Illuminate\Support\Collection;
336ccdf4f0SGreg Roachuse Psr\Http\Message\ServerRequestInterface;
34f1d4b4a2SGreg Roach
356ccdf4f0SGreg Roachuse function app;
365229eadeSGreg Roachuse function assert;
37*2474349cSGreg Roachuse function max;
38e5a6b4d4SGreg Roach
39e5a6b4d4SGreg Roach/**
40e5a6b4d4SGreg Roach * Functions for managing users.
41e5a6b4d4SGreg Roach */
42e5a6b4d4SGreg Roachclass UserService
43e5a6b4d4SGreg Roach{
44e5a6b4d4SGreg Roach    /**
45e5a6b4d4SGreg Roach     * Find the user with a specified user_id.
46e5a6b4d4SGreg Roach     *
47e5a6b4d4SGreg Roach     * @param int|null $user_id
48e5a6b4d4SGreg Roach     *
49e5a6b4d4SGreg Roach     * @return User|null
50e5a6b4d4SGreg Roach     */
5125d7fe95SGreg Roach    public function find($user_id): ?User
52e5a6b4d4SGreg Roach    {
530b5fd0a6SGreg Roach        return app('cache.array')->rememberForever(__CLASS__ . $user_id, static function () use ($user_id): ?User {
54e5a6b4d4SGreg Roach            return DB::table('user')
55e5a6b4d4SGreg Roach                ->where('user_id', '=', $user_id)
56e5a6b4d4SGreg Roach                ->get()
57e5a6b4d4SGreg Roach                ->map(User::rowMapper())
58e5a6b4d4SGreg Roach                ->first();
59e5a6b4d4SGreg Roach        });
60e5a6b4d4SGreg Roach    }
61e5a6b4d4SGreg Roach
62e5a6b4d4SGreg Roach    /**
63e5a6b4d4SGreg Roach     * Find the user with a specified email address.
64e5a6b4d4SGreg Roach     *
65e5a6b4d4SGreg Roach     * @param string $email
66e5a6b4d4SGreg Roach     *
67e5a6b4d4SGreg Roach     * @return User|null
68e5a6b4d4SGreg Roach     */
69e364afe4SGreg Roach    public function findByEmail($email): ?User
70e5a6b4d4SGreg Roach    {
71e5a6b4d4SGreg Roach        return DB::table('user')
72e5a6b4d4SGreg Roach            ->where('email', '=', $email)
73e5a6b4d4SGreg Roach            ->get()
74e5a6b4d4SGreg Roach            ->map(User::rowMapper())
75e5a6b4d4SGreg Roach            ->first();
76e5a6b4d4SGreg Roach    }
77e5a6b4d4SGreg Roach
78e5a6b4d4SGreg Roach    /**
79e5a6b4d4SGreg Roach     * Find the user with a specified user_name or email address.
80e5a6b4d4SGreg Roach     *
81e5a6b4d4SGreg Roach     * @param string $identifier
82e5a6b4d4SGreg Roach     *
83e5a6b4d4SGreg Roach     * @return User|null
84e5a6b4d4SGreg Roach     */
85e364afe4SGreg Roach    public function findByIdentifier($identifier): ?User
86e5a6b4d4SGreg Roach    {
87e5a6b4d4SGreg Roach        return DB::table('user')
88e5a6b4d4SGreg Roach            ->where('user_name', '=', $identifier)
89e5a6b4d4SGreg Roach            ->orWhere('email', '=', $identifier)
90e5a6b4d4SGreg Roach            ->get()
91e5a6b4d4SGreg Roach            ->map(User::rowMapper())
92e5a6b4d4SGreg Roach            ->first();
93e5a6b4d4SGreg Roach    }
94e5a6b4d4SGreg Roach
95e5a6b4d4SGreg Roach    /**
96e5a6b4d4SGreg Roach     * Find the user(s) with a specified genealogy record.
97e5a6b4d4SGreg Roach     *
98e5a6b4d4SGreg Roach     * @param Individual $individual
99e5a6b4d4SGreg Roach     *
10054c7f8dfSGreg Roach     * @return Collection
101e5a6b4d4SGreg Roach     */
102e5a6b4d4SGreg Roach    public function findByIndividual(Individual $individual): Collection
103e5a6b4d4SGreg Roach    {
104e5a6b4d4SGreg Roach        return DB::table('user')
105e5a6b4d4SGreg Roach            ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id')
106e5a6b4d4SGreg Roach            ->where('gedcom_id', '=', $individual->tree()->id())
107e5a6b4d4SGreg Roach            ->where('setting_value', '=', $individual->xref())
108e5a6b4d4SGreg Roach            ->where('setting_name', '=', 'gedcomid')
109e5a6b4d4SGreg Roach            ->select(['user.*'])
110e5a6b4d4SGreg Roach            ->get()
111e5a6b4d4SGreg Roach            ->map(User::rowMapper());
112e5a6b4d4SGreg Roach    }
113e5a6b4d4SGreg Roach
114e5a6b4d4SGreg Roach    /**
115a00bcc63SGreg Roach     * Find the user with a specified password reset token.
116a00bcc63SGreg Roach     *
117a00bcc63SGreg Roach     * @param string $token
118a00bcc63SGreg Roach     *
119a00bcc63SGreg Roach     * @return User|null
120a00bcc63SGreg Roach     */
121a00bcc63SGreg Roach    public function findByToken(string $token): ?User
122a00bcc63SGreg Roach    {
123a00bcc63SGreg Roach        return DB::table('user')
124a00bcc63SGreg Roach            ->join('user_setting AS us1', 'us1.user_id', '=', 'user.user_id')
125a00bcc63SGreg Roach            ->where('us1.setting_name', '=', 'password-token')
126a00bcc63SGreg Roach            ->where('us1.setting_value', '=', $token)
127a00bcc63SGreg Roach            ->join('user_setting AS us2', 'us2.user_id', '=', 'user.user_id')
128a00bcc63SGreg Roach            ->where('us2.setting_name', '=', 'password-token-expire')
129a00bcc63SGreg Roach            ->where('us2.setting_value', '>', Carbon::now()->timestamp)
130a00bcc63SGreg Roach            ->select(['user.*'])
131a00bcc63SGreg Roach            ->get()
132a00bcc63SGreg Roach            ->map(User::rowMapper())
133a00bcc63SGreg Roach            ->first();
134a00bcc63SGreg Roach    }
135a00bcc63SGreg Roach
136a00bcc63SGreg Roach    /**
137e5a6b4d4SGreg Roach     * Find the user with a specified user_name.
138e5a6b4d4SGreg Roach     *
139e5a6b4d4SGreg Roach     * @param string $user_name
140e5a6b4d4SGreg Roach     *
141e5a6b4d4SGreg Roach     * @return User|null
142e5a6b4d4SGreg Roach     */
143e364afe4SGreg Roach    public function findByUserName($user_name): ?User
144e5a6b4d4SGreg Roach    {
145e5a6b4d4SGreg Roach        return DB::table('user')
146e5a6b4d4SGreg Roach            ->where('user_name', '=', $user_name)
147e5a6b4d4SGreg Roach            ->get()
148e5a6b4d4SGreg Roach            ->map(User::rowMapper())
149e5a6b4d4SGreg Roach            ->first();
150e5a6b4d4SGreg Roach    }
151e5a6b4d4SGreg Roach
152e5a6b4d4SGreg Roach    /**
153*2474349cSGreg Roach     * Callback to sort users by their last-login (or registration) time.
154*2474349cSGreg Roach     *
155*2474349cSGreg Roach     * @return Closure
156*2474349cSGreg Roach     */
157*2474349cSGreg Roach    public function sortByLastLogin(): Closure
158*2474349cSGreg Roach    {
159*2474349cSGreg Roach        return function (UserInterface $user1, UserInterface $user2) {
160*2474349cSGreg Roach            $registered_at1 = (int) $user1->getPreference('reg_timestamp');
161*2474349cSGreg Roach            $logged_in_at1  = (int) $user1->getPreference('sessiontime');
162*2474349cSGreg Roach            $registered_at2 = (int) $user2->getPreference('reg_timestamp');
163*2474349cSGreg Roach            $logged_in_at2  = (int) $user2->getPreference('sessiontime');
164*2474349cSGreg Roach
165*2474349cSGreg Roach            return max($registered_at1, $logged_in_at1) <=> max($registered_at2, $logged_in_at2);
166*2474349cSGreg Roach        };
167*2474349cSGreg Roach    }
168*2474349cSGreg Roach
169*2474349cSGreg Roach    /**
170*2474349cSGreg Roach     * Callback to filter users who have not logged in since a given time.
171*2474349cSGreg Roach     *
172*2474349cSGreg Roach     * @param int $timestamp
173*2474349cSGreg Roach     *
174*2474349cSGreg Roach     * @return Closure
175*2474349cSGreg Roach     */
176*2474349cSGreg Roach    public function filterInactive(int $timestamp): Closure
177*2474349cSGreg Roach    {
178*2474349cSGreg Roach        return function (UserInterface $user) use ($timestamp): bool {
179*2474349cSGreg Roach            $registered_at = (int) $user->getPreference('reg_timestamp');
180*2474349cSGreg Roach            $logged_in_at  = (int) $user->getPreference('sessiontime');
181*2474349cSGreg Roach
182*2474349cSGreg Roach            return max($registered_at, $logged_in_at) < $timestamp;
183*2474349cSGreg Roach        };
184*2474349cSGreg Roach    }
185*2474349cSGreg Roach
186*2474349cSGreg Roach    /**
187e5a6b4d4SGreg Roach     * Get a list of all users.
188e5a6b4d4SGreg Roach     *
18954c7f8dfSGreg Roach     * @return Collection
190e5a6b4d4SGreg Roach     */
191e5a6b4d4SGreg Roach    public function all(): Collection
192e5a6b4d4SGreg Roach    {
193e5a6b4d4SGreg Roach        return DB::table('user')
194e5a6b4d4SGreg Roach            ->where('user_id', '>', 0)
195e5a6b4d4SGreg Roach            ->orderBy('real_name')
196e5a6b4d4SGreg Roach            ->get()
197e5a6b4d4SGreg Roach            ->map(User::rowMapper());
198e5a6b4d4SGreg Roach    }
199e5a6b4d4SGreg Roach
200e5a6b4d4SGreg Roach    /**
201e5a6b4d4SGreg Roach     * Get a list of all administrators.
202e5a6b4d4SGreg Roach     *
20354c7f8dfSGreg Roach     * @return Collection
204e5a6b4d4SGreg Roach     */
205e5a6b4d4SGreg Roach    public function administrators(): Collection
206e5a6b4d4SGreg Roach    {
207e5a6b4d4SGreg Roach        return DB::table('user')
2081ab2f386SGreg Roach            ->join('user_setting', 'user_setting.user_id', '=', 'user.user_id')
209e5a6b4d4SGreg Roach            ->where('user_setting.setting_name', '=', 'canadmin')
2101ab2f386SGreg Roach            ->where('user_setting.setting_value', '=', '1')
211e5a6b4d4SGreg Roach            ->where('user.user_id', '>', 0)
212e5a6b4d4SGreg Roach            ->orderBy('real_name')
213e5a6b4d4SGreg Roach            ->select(['user.*'])
214e5a6b4d4SGreg Roach            ->get()
215e5a6b4d4SGreg Roach            ->map(User::rowMapper());
216e5a6b4d4SGreg Roach    }
217e5a6b4d4SGreg Roach
218e5a6b4d4SGreg Roach    /**
219e5a6b4d4SGreg Roach     * Get a list of all managers.
220e5a6b4d4SGreg Roach     *
22154c7f8dfSGreg Roach     * @return Collection
222e5a6b4d4SGreg Roach     */
223e5a6b4d4SGreg Roach    public function managers(): Collection
224e5a6b4d4SGreg Roach    {
225e5a6b4d4SGreg Roach        return DB::table('user')
2261ab2f386SGreg Roach            ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id')
227e5a6b4d4SGreg Roach            ->where('user_gedcom_setting.setting_name', '=', 'canedit')
2281ab2f386SGreg Roach            ->where('user_gedcom_setting.setting_value', '=', 'admin')
229e5a6b4d4SGreg Roach            ->where('user.user_id', '>', 0)
2301ab2f386SGreg Roach            ->groupBy(['user.user_id'])
231e5a6b4d4SGreg Roach            ->orderBy('real_name')
232e5a6b4d4SGreg Roach            ->select(['user.*'])
233e5a6b4d4SGreg Roach            ->get()
234e5a6b4d4SGreg Roach            ->map(User::rowMapper());
235e5a6b4d4SGreg Roach    }
236e5a6b4d4SGreg Roach
237e5a6b4d4SGreg Roach    /**
238e5a6b4d4SGreg Roach     * Get a list of all moderators.
239e5a6b4d4SGreg Roach     *
24054c7f8dfSGreg Roach     * @return Collection
241e5a6b4d4SGreg Roach     */
242e5a6b4d4SGreg Roach    public function moderators(): Collection
243e5a6b4d4SGreg Roach    {
244e5a6b4d4SGreg Roach        return DB::table('user')
2451ab2f386SGreg Roach            ->join('user_gedcom_setting', 'user_gedcom_setting.user_id', '=', 'user.user_id')
246e5a6b4d4SGreg Roach            ->where('user_gedcom_setting.setting_name', '=', 'canedit')
2471ab2f386SGreg Roach            ->where('user_gedcom_setting.setting_value', '=', 'accept')
248e5a6b4d4SGreg Roach            ->where('user.user_id', '>', 0)
2491ab2f386SGreg Roach            ->groupBy(['user.user_id'])
250e5a6b4d4SGreg Roach            ->orderBy('real_name')
251e5a6b4d4SGreg Roach            ->select(['user.*'])
252e5a6b4d4SGreg Roach            ->get()
253e5a6b4d4SGreg Roach            ->map(User::rowMapper());
254e5a6b4d4SGreg Roach    }
255e5a6b4d4SGreg Roach
256e5a6b4d4SGreg Roach    /**
257e5a6b4d4SGreg Roach     * Get a list of all verified users.
258e5a6b4d4SGreg Roach     *
25954c7f8dfSGreg Roach     * @return Collection
260e5a6b4d4SGreg Roach     */
261e5a6b4d4SGreg Roach    public function unapproved(): Collection
262e5a6b4d4SGreg Roach    {
263e5a6b4d4SGreg Roach        return DB::table('user')
2641ab2f386SGreg Roach            ->join('user_setting', 'user_setting.user_id', '=', 'user.user_id')
265e5a6b4d4SGreg Roach            ->where('user_setting.setting_name', '=', 'verified_by_admin')
2661ab2f386SGreg Roach            ->where('user_setting.setting_value', '<>', '1')
267e5a6b4d4SGreg Roach            ->where('user.user_id', '>', 0)
268e5a6b4d4SGreg Roach            ->orderBy('real_name')
269e5a6b4d4SGreg Roach            ->select(['user.*'])
270e5a6b4d4SGreg Roach            ->get()
271e5a6b4d4SGreg Roach            ->map(User::rowMapper());
272e5a6b4d4SGreg Roach    }
273e5a6b4d4SGreg Roach
274e5a6b4d4SGreg Roach    /**
275e5a6b4d4SGreg Roach     * Get a list of all verified users.
276e5a6b4d4SGreg Roach     *
27754c7f8dfSGreg Roach     * @return Collection
278e5a6b4d4SGreg Roach     */
279e5a6b4d4SGreg Roach    public function unverified(): Collection
280e5a6b4d4SGreg Roach    {
281e5a6b4d4SGreg Roach        return DB::table('user')
2821ab2f386SGreg Roach            ->join('user_setting', 'user_setting.user_id', '=', 'user.user_id')
283e5a6b4d4SGreg Roach            ->where('user_setting.setting_name', '=', 'verified')
2841ab2f386SGreg Roach            ->where('user_setting.setting_value', '<>', '1')
285e5a6b4d4SGreg Roach            ->where('user.user_id', '>', 0)
286e5a6b4d4SGreg Roach            ->orderBy('real_name')
287e5a6b4d4SGreg Roach            ->select(['user.*'])
288e5a6b4d4SGreg Roach            ->get()
289e5a6b4d4SGreg Roach            ->map(User::rowMapper());
290e5a6b4d4SGreg Roach    }
291e5a6b4d4SGreg Roach
292e5a6b4d4SGreg Roach    /**
293e5a6b4d4SGreg Roach     * Get a list of all users who are currently logged in.
294e5a6b4d4SGreg Roach     *
29554c7f8dfSGreg Roach     * @return Collection
296e5a6b4d4SGreg Roach     */
297e5a6b4d4SGreg Roach    public function allLoggedIn(): Collection
298e5a6b4d4SGreg Roach    {
299e5a6b4d4SGreg Roach        return DB::table('user')
300e5a6b4d4SGreg Roach            ->join('session', 'session.user_id', '=', 'user.user_id')
301e5a6b4d4SGreg Roach            ->where('user.user_id', '>', 0)
302e5a6b4d4SGreg Roach            ->orderBy('real_name')
303e5a6b4d4SGreg Roach            ->select(['user.*'])
304e5a6b4d4SGreg Roach            ->distinct()
305e5a6b4d4SGreg Roach            ->get()
306e5a6b4d4SGreg Roach            ->map(User::rowMapper());
307e5a6b4d4SGreg Roach    }
308e5a6b4d4SGreg Roach
309e5a6b4d4SGreg Roach    /**
310e5a6b4d4SGreg Roach     * Create a new user.
311e5a6b4d4SGreg Roach     * The calling code needs to check for duplicates identifiers before calling
312e5a6b4d4SGreg Roach     * this function.
313e5a6b4d4SGreg Roach     *
314e5a6b4d4SGreg Roach     * @param string $user_name
315e5a6b4d4SGreg Roach     * @param string $real_name
316e5a6b4d4SGreg Roach     * @param string $email
317e5a6b4d4SGreg Roach     * @param string $password
318e5a6b4d4SGreg Roach     *
319e5a6b4d4SGreg Roach     * @return User
320e5a6b4d4SGreg Roach     */
3216be338f5SGreg Roach    public function create(string $user_name, string $real_name, string $email, string $password): User
322e5a6b4d4SGreg Roach    {
323e5a6b4d4SGreg Roach        DB::table('user')->insert([
324e5a6b4d4SGreg Roach            'user_name' => $user_name,
325e5a6b4d4SGreg Roach            'real_name' => $real_name,
326e5a6b4d4SGreg Roach            'email'     => $email,
327e5a6b4d4SGreg Roach            'password'  => password_hash($password, PASSWORD_DEFAULT),
328e5a6b4d4SGreg Roach        ]);
329e5a6b4d4SGreg Roach
330e5a6b4d4SGreg Roach        $user_id = (int) DB::connection()->getPdo()->lastInsertId();
331e5a6b4d4SGreg Roach
332e5a6b4d4SGreg Roach        return new User($user_id, $user_name, $real_name, $email);
333e5a6b4d4SGreg Roach    }
334e5a6b4d4SGreg Roach
335e5a6b4d4SGreg Roach    /**
336e5a6b4d4SGreg Roach     * Delete a user
337e5a6b4d4SGreg Roach     *
338e5a6b4d4SGreg Roach     * @param User $user
339e5a6b4d4SGreg Roach     *
340e5a6b4d4SGreg Roach     * @return void
341e5a6b4d4SGreg Roach     */
342e364afe4SGreg Roach    public function delete(User $user): void
343e5a6b4d4SGreg Roach    {
344e5a6b4d4SGreg Roach        // Don't delete the logs, just set the user to null.
345e5a6b4d4SGreg Roach        DB::table('log')
346e5a6b4d4SGreg Roach            ->where('user_id', '=', $user->id())
347e5a6b4d4SGreg Roach            ->update(['user_id' => null]);
348e5a6b4d4SGreg Roach
349e5a6b4d4SGreg Roach        // Take over the user’s pending changes. (What else could we do with them?)
350e5a6b4d4SGreg Roach        DB::table('change')
351e5a6b4d4SGreg Roach            ->where('user_id', '=', $user->id())
352e5a6b4d4SGreg Roach            ->where('status', '=', 'rejected')
353e5a6b4d4SGreg Roach            ->delete();
354e5a6b4d4SGreg Roach
355e5a6b4d4SGreg Roach        DB::table('change')
356e5a6b4d4SGreg Roach            ->where('user_id', '=', $user->id())
357e5a6b4d4SGreg Roach            ->update(['user_id' => Auth::id()]);
358e5a6b4d4SGreg Roach
359e5a6b4d4SGreg Roach        // Delete settings and preferences
360e5a6b4d4SGreg Roach        DB::table('block_setting')
361e5a6b4d4SGreg Roach            ->join('block', 'block_setting.block_id', '=', 'block.block_id')
362e5a6b4d4SGreg Roach            ->where('user_id', '=', $user->id())
363e5a6b4d4SGreg Roach            ->delete();
364e5a6b4d4SGreg Roach
365e5a6b4d4SGreg Roach        DB::table('block')->where('user_id', '=', $user->id())->delete();
366e5a6b4d4SGreg Roach        DB::table('user_gedcom_setting')->where('user_id', '=', $user->id())->delete();
367e5a6b4d4SGreg Roach        DB::table('user_setting')->where('user_id', '=', $user->id())->delete();
368e5a6b4d4SGreg Roach        DB::table('message')->where('user_id', '=', $user->id())->delete();
369e5a6b4d4SGreg Roach        DB::table('user')->where('user_id', '=', $user->id())->delete();
370e5a6b4d4SGreg Roach    }
37186730b84SGreg Roach
37286730b84SGreg Roach    /**
3734db4b4a9SGreg Roach     * @param User                   $contact_user
374a992e8c1SGreg Roach     * @param ServerRequestInterface $request
37586730b84SGreg Roach     *
37686730b84SGreg Roach     * @return string
37786730b84SGreg Roach     */
378a992e8c1SGreg Roach    public function contactLink(User $contact_user, ServerRequestInterface $request): string
379dcbe9044SGreg Roach    {
380a992e8c1SGreg Roach        $tree = $request->getAttribute('tree');
38175964c75SGreg Roach        assert($tree instanceof Tree);
3825229eadeSGreg Roach
383a992e8c1SGreg Roach        $user = $request->getAttribute('user');
38486730b84SGreg Roach
38586730b84SGreg Roach        if ($contact_user->getPreference('contactmethod') === 'mailto') {
38686730b84SGreg Roach            $url = 'mailto:' . $contact_user->email();
38786730b84SGreg Roach        } elseif ($user instanceof User) {
38886730b84SGreg Roach            // Logged-in users send direct messages
389e381f98dSGreg Roach            $url = route(MessagePage::class, ['to' => $contact_user->userName(), 'tree' => $tree->name()]);
39086730b84SGreg Roach        } else {
39186730b84SGreg Roach            // Visitors use the contact form.
392e381f98dSGreg Roach            $url = route(ContactPage::class, [
39386730b84SGreg Roach                'to'   => $contact_user->userName(),
394d72b284aSGreg Roach                'tree' => $tree->name(),
395f567c3d8SGreg Roach                'url'  => (string) $request->getUri(),
39686730b84SGreg Roach            ]);
39786730b84SGreg Roach        }
39886730b84SGreg Roach
39986730b84SGreg Roach        return '<a href="' . e($url) . '" dir="auto">' . e($contact_user->realName()) . '</a>';
40086730b84SGreg Roach    }
401e5a6b4d4SGreg Roach}
402