Passed
Push — master ( 5bbfbb...247434 )
by Greg
06:13
created

UserService::filterInactive()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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