• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

visavi / rotor / 28340133337

28 Jun 2026 11:47PM UTC coverage: 16.561% (+0.09%) from 16.474%
28340133337

push

github

visavi
Ядро и модули переведены на datetime, удалена константа SITETIME

18 of 95 new or added lines in 31 files covered. (18.95%)

7 existing lines in 6 files now uncovered.

989 of 5972 relevant lines covered (16.56%)

2.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/Http/Controllers/User/UserController.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Http\Controllers\User;
6

7
use App\Classes\Validator;
8
use App\Http\Controllers\Controller;
9
use App\Models\BlackList;
10
use App\Models\Flood;
11
use App\Models\User;
12
use App\Models\UserField;
13
use Illuminate\Database\Query\JoinClause;
14
use Illuminate\Http\JsonResponse;
15
use Illuminate\Http\RedirectResponse;
16
use Illuminate\Http\Request;
17
use Illuminate\Support\Facades\Auth;
18
use Illuminate\Support\Facades\Hash;
19
use Illuminate\Support\Str;
20
use Illuminate\View\View;
21

22
class UserController extends Controller
23
{
24
    /**
25
     * User profile
26
     */
27
    public function index(string $login): View
×
28
    {
29
        if (! $user = getUserByLogin($login)) {
×
30
            abort(404, __('validator.user'));
×
31
        }
32

33
        $user->load('lastBan');
×
34
        $adminGroups = User::ADMIN_GROUPS;
×
35

36
        $fields = UserField::query()->withUserData($user->id)->whereNotNull('user_data.value')->get();
×
37

38
        return view('users/user', compact('user', 'adminGroups', 'fields'));
×
39
    }
40

41
    /**
42
     * Registration
43
     */
44
    public function register(Request $request, Validator $validator): View|RedirectResponse
×
45
    {
46
        if (getUser()) {
×
47
            abort(403, __('users.already_registered'));
×
48
        }
49

50
        if (! setting('openreg')) {
×
51
            abort(200, __('users.registration_suspended'));
×
52
        }
53

54
        if ($request->isMethod('post')) {
×
55
            if ($request->has(['login', 'password'])) {
×
56
                $login = (string) $request->input('login');
×
57
                $password = $request->input('password');
×
58
                $password2 = $request->input('password2');
×
59
                $email = strtolower((string) $request->input('email'));
×
60
                $domain = Str::substr(strrchr($email, '@'), 1);
×
61
                $gender = $request->input('gender') === User::MALE ? User::MALE : User::FEMALE;
×
62

63
                $validator->true(captchaVerify(), ['protect' => __('validator.captcha')])
×
64
                    ->regex($login, '|^[a-z0-9\-]+$|i', ['login' => __('validator.login')])
×
65
                    ->regex(Str::substr($login, 0, 1), '|^[a-z0-9]+$|i', ['login' => __('users.login_begin_requirements')])
×
66
                    ->email($email, ['email' => __('validator.email')])
×
67
                    ->length($login, 3, 20, ['login' => __('users.login_length_requirements')])
×
68
                    ->length($password, 6, 20, ['password' => __('users.password_length_requirements')])
×
69
                    ->equal($password, $password2, ['password2' => __('users.passwords_different')])
×
70
                    ->false(ctype_digit($login), ['login' => __('users.field_characters_requirements')])
×
71
                    ->false(ctype_digit($password), ['password' => __('users.field_characters_requirements')])
×
72
                    ->false(substr_count($login, '-') > 2, ['login' => __('users.login_hyphens_requirements')]);
×
73

74
                if (! empty($login)) {
×
75
                    // Проверка логина на существование
76
                    $checkLogin = User::query()->where('login', $login)->exists();
×
77
                    $validator->false($checkLogin, ['login' => __('users.login_already_exists')]);
×
78

79
                    // Проверка логина в черном списке
80
                    $validator->false(BlackList::isBlacklisted('login', strtolower($login)), ['login' => __('users.login_is_blacklisted')]);
×
81
                }
82

83
                // Проверка email на существование
84
                $checkMail = User::query()->where('email', $email)->exists();
×
85
                $validator->false($checkMail, ['email' => __('users.email_already_exists')]);
×
86

87
                // Проверка домена от email и email в черном списке
88
                $validator
×
89
                    ->false(BlackList::isBlacklisted('domain', $domain), ['email' => __('users.domain_is_blacklisted')])
×
90
                    ->false(BlackList::isBlacklisted('email', $email), ['email' => __('users.email_is_blacklisted')]);
×
91

92
                // Подтверждение регистрации
93
                $confirmToken = null;
×
94
                $confirmUrl = null;
×
95
                if (setting('regkeys')) {
×
96
                    $confirmToken = Str::random(32);
×
97
                    $confirmUrl = route('confirm', ['token' => $confirmToken]);
×
98
                }
99

100
                // Регистрация аккаунта
101
                if ($validator->isValid()) {
×
102
                    $user = User::query()->create([
×
103
                        'login'         => $login,
×
104
                        'password'      => Hash::make($password),
×
105
                        'email'         => $email,
×
106
                        'level'         => setting('regkeys') ? User::PENDED : User::USER,
×
107
                        'gender'        => $gender,
×
108
                        'themes'        => setting('themes'),
×
109
                        'point'         => 0,
×
110
                        'language'      => setting('language'),
×
111
                        'money'         => setting('registermoney'),
×
112
                        'subscribe'     => Str::random(32),
×
113
                        'confirm_token' => $confirmToken,
×
NEW
114
                        'updated_at'    => now(),
×
115
                    ]);
×
116

117
                    // ----- Уведомление в приват ----//
118
                    $textNotice = textNotice('register', ['username' => $login]);
×
119
                    $user->sendMessage(null, $textNotice);
×
120

121
                    // --- Уведомление о регистрации на email ---//
122
                    $subject = 'Регистрация на ' . setting('title');
×
123
                    $data = [
×
124
                        'to'         => $email,
×
125
                        'subject'    => $subject,
×
126
                        'login'      => $login,
×
127
                        'password'   => $password,
×
128
                        'confirmUrl' => $confirmUrl,
×
129
                    ];
×
130

131
                    sendMail('mailer.register', $data);
×
132

133
                    Auth::login($user, true);
×
134

135
                    setFlash('success', __('users.welcome', ['login' => $login]));
×
136

137
                    return redirect('/');
×
138
                }
139

140
                setInput($request->all());
×
141
                setFlash('danger', $validator->getErrors());
×
142
            }
143
        }
144

145
        return view('users/register');
×
146
    }
147

148
    /**
149
     * Login
150
     */
151
    public function login(Request $request, Validator $validator, Flood $flood): View|RedirectResponse
×
152
    {
153
        if (Auth::check()) {
×
154
            return redirect('/')->with('danger', __('main.already_authorized'));
×
155
        }
156

157
        $isFlood = $flood->isFlood();
×
158

159
        if ($request->isMethod('post')) {
×
160
            if ($request->has(['login', 'password'])) {
×
161
                if ($isFlood) {
×
162
                    $validator->true(captchaVerify(), ['protect' => __('validator.captcha')]);
×
163
                }
164

165
                if ($validator->isValid()) {
×
166
                    $login = Str::lower((string) $request->input('login'));
×
167
                    $password = $request->input('password');
×
168
                    $remember = $request->boolean('remember');
×
169

170
                    $field = strpos($request->input('login'), '@') ? 'email' : 'login';
×
171

172
                    $credentials = [
×
173
                        $field     => $login,
×
174
                        'password' => $password,
×
175
                    ];
×
176

177
                    try {
178
                        $authorized = Auth::attempt($credentials, $remember);
×
179
                    } catch (\RuntimeException) {
×
180
                        setInput($request->all());
×
181
                        setFlash('danger', __('users.password_reset_required'));
×
182

183
                        return redirect('recovery');
×
184
                    }
185

186
                    if ($authorized) {
×
187
                        $request->session()->regenerate();
×
188
                        $user = Auth::user();
×
189

190
                        return redirect()->intended()
×
191
                            ->with('success', __('users.welcome', ['login' => $user->getName()], $user->language));
×
192
                    }
193

194
                    $flood->saveState(300);
×
195
                    setInput($request->all());
×
196
                    setFlash('danger', __('users.incorrect_login_or_password'));
×
197
                } else {
198
                    setInput($request->all());
×
199
                    setFlash('danger', $validator->getErrors());
×
200
                }
201

202
                return redirect('login');
×
203
            }
204
        }
205

206
        // Запоминаем страницу, с которой пришёл гость, для возврата после входа
207
        $previous = url()->previous();
×
208
        if (! Str::contains($previous, ['/login', '/register', '/recovery'])) {
×
209
            $request->session()->put('url.intended', $previous);
×
210
        }
211

212
        return view('users/login', compact('isFlood'));
×
213
    }
214

215
    /**
216
     * Exit
217
     */
218
    public function logout(Request $request): RedirectResponse
×
219
    {
220
        Auth::logout();
×
221

222
        $request->session()->invalidate();
×
223
        $request->session()->regenerateToken();
×
224

225
        return redirect('/');
×
226
    }
227

228
    /**
229
     * Profile editing
230
     */
231
    public function profile(Request $request, Validator $validator): View|RedirectResponse
×
232
    {
233
        if (! $user = getUser()) {
×
234
            abort(403, __('main.not_authorized'));
×
235
        }
236

237
        $fields = UserField::query()
×
238
            ->select('uf.*', 'ud.value')
×
239
            ->from('user_fields as uf')
×
240
            ->leftJoin('user_data as ud', static function (JoinClause $join) use ($user) {
×
241
                $join->on('uf.id', 'ud.field_id')
×
242
                    ->where('ud.user_id', $user->id);
×
243
            })
×
244
            ->orderBy('uf.sort')
×
245
            ->get();
×
246

247
        if ($request->isMethod('post')) {
×
248
            $info = $request->input('info');
×
249
            $name = $request->input('name');
×
250
            $country = $request->input('country');
×
251
            $city = $request->input('city');
×
252
            $phone = preg_replace('/[^\d+]/', '', $request->input('phone') ?? '');
×
253
            $site = $request->input('site');
×
254
            $birthday = $request->input('birthday');
×
255
            $gender = $request->input('gender') === User::MALE ? User::MALE : User::FEMALE;
×
256

257
            $validator
×
258
                ->url($site, ['site' => __('validator.site')], false)
×
259
                ->regex($birthday, '#^[0-9]{2}+\.[0-9]{2}+\.[0-9]{4}$#', ['birthday' => __('validator.date')], false)
×
260
                ->phone($phone, ['phone' => __('validator.phone')], false)
×
261
                ->length($info, 0, 1000, ['info' => __('users.info_yourself_long')])
×
262
                ->length($name, 3, 20, ['name' => __('users.name_short_or_long')], false);
×
263

264
            foreach ($fields as $field) {
×
265
                $validator->length(
×
266
                    $request->input('field' . $field->id),
×
267
                    $field->min,
×
268
                    $field->max,
×
269
                    ['field' . $field->id => __('validator.text')],
×
270
                    $field->required
×
271
                );
×
272
            }
273

274
            if ($validator->isValid()) {
×
275
                $country = Str::substr($country, 0, 30);
×
276
                $city = Str::substr($city, 0, 50);
×
277

278
                $user->update([
×
279
                    'name'     => $name,
×
280
                    'gender'   => $gender,
×
281
                    'country'  => $country,
×
282
                    'city'     => $city,
×
283
                    'phone'    => $phone,
×
284
                    'site'     => $site,
×
285
                    'birthday' => $birthday,
×
286
                    'info'     => $info,
×
287
                ]);
×
288

289
                foreach ($fields as $field) {
×
290
                    $user->data()
×
291
                        ->updateOrCreate([
×
292
                            'field_id' => $field->id,
×
293
                        ], [
×
294
                            'value' => $field->sanitizeValue($request->input('field' . $field->id)),
×
295
                        ]);
×
296
                }
297

298
                setFlash('success', __('users.profile_success_changed'));
×
299

300
                return redirect('profile');
×
301
            }
302

303
            setInput($request->all());
×
304
            setFlash('danger', $validator->getErrors());
×
305
        }
306

307
        return view('users/profile', compact('user', 'fields'));
×
308
    }
309

310
    /**
311
     * Verify registration
312
     */
313
    public function verify(Request $request, Validator $validator): View|RedirectResponse
×
314
    {
315
        if (! $user = $request->user()) {
×
316
            abort(403, __('main.not_authorized'));
×
317
        }
318

319
        if (! setting('regkeys')) {
×
320
            abort(200, __('users.confirm_registration_disabled'));
×
321
        }
322

323
        if ($user->level !== User::PENDED) {
×
324
            abort(403, __('users.profile_not_confirmation'));
×
325
        }
326

327
        /* Повторная отправка */
328
        if ($request->has('email') && $request->isMethod('post')) {
×
329
            $email = strtolower((string) $request->input('email'));
×
330
            $domain = Str::substr(strrchr($email, '@'), 1);
×
331

332
            $validator
×
333
                ->true(captchaVerify(), ['protect' => __('validator.captcha')])
×
334
                ->email($email, ['email' => __('validator.email')]);
×
335

336
            $validator
×
337
                ->false(User::query()->where('login', '<>', $user->login)->where('email', $email)->exists(), ['email' => __('users.email_already_exists')])
×
338
                ->false(BlackList::isBlacklisted('email', $email), ['email' => __('users.email_is_blacklisted')])
×
339
                ->false(BlackList::isBlacklisted('domain', $domain), ['email' => __('users.domain_is_blacklisted')]);
×
340

341
            if ($validator->isValid()) {
×
342
                $token = Str::random(32);
×
343
                $confirmUrl = route('confirm', ['token' => $token]);
×
344

345
                $user->update([
×
346
                    'email'         => $email,
×
347
                    'confirm_token' => $token,
×
348
                ]);
×
349

350
                /* Уведомление о регистрации на email */
351
                $subject = 'Регистрация на ' . setting('title');
×
352
                $data = [
×
353
                    'to'         => $email,
×
354
                    'subject'    => $subject,
×
355
                    'login'      => $user->login,
×
356
                    'password'   => '*****',
×
357
                    'confirmUrl' => $confirmUrl,
×
358
                ];
×
359

360
                sendMail('mailer.register', $data);
×
361
                setFlash('success', __('users.confirm_code_success_sent'));
×
362

363
                return redirect()->route('verify');
×
364
            }
365

366
            setInput($request->all());
×
367
            setFlash('danger', $validator->getErrors());
×
368
        }
369

370
        return view('users/verify', compact('user'));
×
371
    }
372

373
    /**
374
     * Confirm registration
375
     */
376
    public function confirm(string $token): RedirectResponse
×
377
    {
378
        $user = User::query()->where('confirm_token', $token)->first();
×
379
        if (! $user) {
×
380
            abort(200, __('users.confirm_code_invalid'));
×
381
        }
382

383
        $user->update([
×
384
            'level'         => User::USER,
×
385
            'confirm_token' => null,
×
386
        ]);
×
387

388
        return redirect('/')->with('success', __('users.account_success_activated'));
×
389
    }
390

391
    /**
392
     * Settings
393
     */
394
    public function setting(Request $request, Validator $validator): View|RedirectResponse
×
395
    {
396
        if (! $user = getUser()) {
×
397
            abort(403, __('main.not_authorized'));
×
398
        }
399

400
        $setting['themes'] = getAvailableThemes();
×
401
        $setting['languages'] = getAvailableLanguages();
×
402
        $setting['timezones'] = range(-12, 12);
×
403

404
        if ($request->isMethod('post')) {
×
405
            $themes = $request->input('themes');
×
406
            $timezone = $request->input('timezone', 0);
×
407
            $language = $request->input('language');
×
408
            $notifyMention = $request->input('notify_mention') ? 1 : 0;
×
409
            $notifyReply = $request->input('notify_reply') ? 1 : 0;
×
410
            $notifyComment = $request->input('notify_comment') ? 1 : 0;
×
411
            $subscribe = $request->input('subscribe') ? Str::random(32) : null;
×
412

413
            $validator
×
414
                ->regex($themes, '|^[a-z0-9_\-]+$|i', ['themes' => __('users.theme_invalid')])
×
415
                ->true(in_array($themes, $setting['themes'], true) || empty($themes), ['themes' => __('users.theme_not_installed')])
×
416
                ->regex($language, '|^[a-z]+$|', ['language' => __('users.language_invalid')])
×
417
                ->in($language, $setting['languages'], ['language' => __('users.language_not_installed')])
×
418
                ->regex($timezone, '|^[\-\+]{0,1}[0-9]{1,2}$|', ['timezone' => __('users.timezone_invalid')]);
×
419

420
            if ($validator->isValid()) {
×
421
                $user->update([
×
422
                    'themes'         => $themes,
×
423
                    'timezone'       => $timezone,
×
424
                    'notify_mention' => $notifyMention,
×
425
                    'notify_reply'   => $notifyReply,
×
426
                    'notify_comment' => $notifyComment,
×
427
                    'subscribe'      => $subscribe,
×
428
                    'language'       => $language,
×
429
                ]);
×
430

431
                if (now()->month === 4 && now()->day === 1 && ! empty($themes)) {
×
432
                    $request->session()->put('april_fools_theme', $themes);
×
433
                }
434

435
                setFlash('success', __('users.settings_success_changed'));
×
436

437
                return redirect('settings');
×
438
            }
439

440
            setInput($request->all());
×
441
            setFlash('danger', $validator->getErrors());
×
442
        }
443

444
        return view('users/settings', compact('user', 'setting'));
×
445
    }
446

447
    /**
448
     * Проверка доступности логина
449
     */
450
    public function checkLogin(Request $request, Validator $validator): JsonResponse
×
451
    {
452
        $login = (string) $request->input('login');
×
453

454
        $validator
×
455
            ->true($request->ajax(), __('validator.not_ajax'))
×
456
            ->regex($login, '|^[a-z0-9\-]+$|i', __('validator.login'))
×
457
            ->regex(Str::substr($login, 0, 1), '|^[a-z0-9]+$|i', __('users.login_begin_requirements'))
×
458
            ->length($login, 3, 20, __('users.login_length_requirements'))
×
459
            ->false(ctype_digit($login), __('users.field_characters_requirements'))
×
460
            ->false(substr_count($login, '-') > 2, __('users.login_hyphens_requirements'));
×
461

462
        if ($validator->isValid()) {
×
463
            $existLogin = User::query()
×
464
                ->where('login', $login)
×
465
                ->exists();
×
466

467
            $validator
×
468
                ->false($existLogin, __('users.login_already_exists'))
×
469
                ->false(BlackList::isBlacklisted('login', strtolower($login)), __('users.login_is_blacklisted'));
×
470
        }
471

472
        if ($validator->isValid()) {
×
473
            return response()->json(['success' => true]);
×
474
        }
475

476
        return response()->json([
×
477
            'success' => false,
×
478
            'message' => current($validator->getErrors()),
×
479
        ]);
×
480
    }
481

482
    /**
483
     * Поиск пользователей для упоминаний
484
     */
485
    public function searchUsers(Request $request): JsonResponse
×
486
    {
487
        $query = (string) $request->input('query', '');
×
488

489
        if (mb_strlen($query) < 2) {
×
490
            return response()->json();
×
491
        }
492

493
        $users = User::query()
×
494
            ->where(function ($q) use ($query) {
×
495
                $q->where('login', 'like', $query . '%')
×
496
                    ->orWhere('name', 'like', $query . '%');
×
497
            })
×
498
            ->orderByDesc('point')
×
499
            ->limit(10)
×
500
            ->get(['login', 'name']);
×
501

502
        return response()->json($users);
×
503
    }
504
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc