Просмотр файла app/Models/User.php

Размер файла: 22.17Kb
  1. <?php
  2.  
  3. declare(strict_types=1);
  4.  
  5. namespace App\Models;
  6.  
  7. use App\Traits\UploadTrait;
  8. use GuzzleHttp\Client;
  9. use GuzzleHttp\Exception\GuzzleException;
  10. use Illuminate\Auth\Authenticatable;
  11. use Illuminate\Auth\MustVerifyEmail;
  12. use Illuminate\Auth\Passwords\CanResetPassword;
  13. use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
  14. use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
  15. use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
  16. use Illuminate\Database\Eloquent\Builder;
  17. use Illuminate\Database\Eloquent\Collection;
  18. use Illuminate\Database\Eloquent\Factories\HasFactory;
  19. use Illuminate\Database\Eloquent\Model;
  20. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  21. use Illuminate\Database\Eloquent\Relations\HasMany;
  22. use Illuminate\Database\Eloquent\Relations\HasOne;
  23. use Illuminate\Database\Query\JoinClause;
  24. use Illuminate\Foundation\Auth\Access\Authorizable;
  25. use Illuminate\Notifications\Notifiable;
  26. use Illuminate\Support\Facades\Cache;
  27. use Illuminate\Support\HtmlString;
  28.  
  29. /**
  30. * Class User
  31. *
  32. * @property int id
  33. * @property string login
  34. * @property string password
  35. * @property string email
  36. * @property string level
  37. * @property string name
  38. * @property string country
  39. * @property string city
  40. * @property string language
  41. * @property string info
  42. * @property string site
  43. * @property string phone
  44. * @property string gender
  45. * @property string birthday
  46. * @property int visits
  47. * @property int newprivat
  48. * @property int newwall
  49. * @property int allforum
  50. * @property int allguest
  51. * @property int allcomments
  52. * @property string themes
  53. * @property string timezone
  54. * @property int point
  55. * @property int money
  56. * @property string status
  57. * @property string color
  58. * @property string avatar
  59. * @property string picture
  60. * @property int rating
  61. * @property int posrating
  62. * @property int negrating
  63. * @property string keypasswd
  64. * @property int timepasswd
  65. * @property int sendprivatmail
  66. * @property int timebonus
  67. * @property string confirmregkey
  68. * @property int newchat
  69. * @property int notify
  70. * @property string apikey
  71. * @property string subscribe
  72. * @property int timeban
  73. * @property int updated_at
  74. * @property int created_at
  75. *
  76. * @property Collection<UserData> data
  77. */
  78. class User extends BaseModel implements
  79. AuthenticatableContract,
  80. AuthorizableContract,
  81. CanResetPasswordContract
  82. {
  83. use Authenticatable;
  84. use Authorizable;
  85. use CanResetPassword;
  86. use MustVerifyEmail;
  87. use HasFactory;
  88. use Notifiable;
  89. use UploadTrait;
  90.  
  91. public const BOSS = 'boss'; // Владелец
  92. public const ADMIN = 'admin'; // Админ
  93. public const MODER = 'moder'; // Модератор
  94. public const EDITOR = 'editor'; // Редактор
  95. public const USER = 'user'; // Пользователь
  96. public const PENDED = 'pended'; // Ожидающий
  97. public const BANNED = 'banned'; // Забаненный
  98.  
  99. /**
  100. * Администраторы
  101. */
  102. public const ADMIN_GROUPS = [
  103. self::BOSS,
  104. self::ADMIN,
  105. self::MODER,
  106. self::EDITOR,
  107. ];
  108.  
  109. /**
  110. * Участники
  111. */
  112. public const USER_GROUPS = [
  113. self::BOSS,
  114. self::ADMIN,
  115. self::MODER,
  116. self::EDITOR,
  117. self::USER,
  118. ];
  119.  
  120. /**
  121. * Все пользователи
  122. */
  123. public const ALL_GROUPS = [
  124. self::BOSS,
  125. self::ADMIN,
  126. self::MODER,
  127. self::EDITOR,
  128. self::USER,
  129. self::PENDED,
  130. self::BANNED,
  131. ];
  132.  
  133. /**
  134. * Genders
  135. */
  136. public const MALE = 'male';
  137. public const FEMALE = 'female';
  138.  
  139. /**
  140. * Indicates if the model should be timestamped.
  141. *
  142. * @var bool
  143. */
  144. public $timestamps = false;
  145.  
  146. /**
  147. * The attributes that aren't mass assignable.
  148. *
  149. * @var array
  150. */
  151. protected $guarded = [];
  152.  
  153. /**
  154. * The attributes that should be hidden for arrays.
  155. *
  156. * @var array
  157. */
  158. protected $hidden = [
  159. 'password',
  160. 'remember_token',
  161. ];
  162.  
  163. /**
  164. * Директория загрузки файлов
  165. *
  166. * @var string
  167. */
  168. public $uploadPath = '/uploads/pictures';
  169.  
  170. /**
  171. * Директория загрузки аватаров
  172. *
  173. * @var string
  174. */
  175. public $uploadAvatarPath = '/uploads/avatars';
  176.  
  177. /**
  178. * Связь с таблицей online
  179. *
  180. * @return BelongsTo
  181. */
  182. public function online(): BelongsTo
  183. {
  184. return $this->belongsTo(Online::class, 'id', 'user_id')->withDefault();
  185. }
  186.  
  187. /**
  188. * Возвращает последний бан
  189. *
  190. * @return hasOne
  191. */
  192. public function lastBan(): hasOne
  193. {
  194. return $this->hasOne(Banhist::class, 'user_id', 'id')
  195. ->whereIn('type', ['ban', 'change'])
  196. ->orderByDesc('created_at')
  197. ->withDefault();
  198. }
  199.  
  200. /**
  201. * Возвращает заметку пользователя
  202. *
  203. * @return hasOne
  204. */
  205. public function note(): HasOne
  206. {
  207. return $this->hasOne(Note::class)->withDefault();
  208. }
  209.  
  210. /**
  211. * Возвращает дополнительные поля
  212. *
  213. * @return HasMany
  214. */
  215. public function data(): HasMany
  216. {
  217. return $this->hasMany(UserData::class, 'user_id');
  218. }
  219.  
  220. /**
  221. * Возвращает имя или логин пользователя
  222. *
  223. * @return string
  224. */
  225. public function getName(): string
  226. {
  227. if ($this->id) {
  228. return $this->name ?: $this->login;
  229. }
  230.  
  231. return setting('deleted_user');
  232. }
  233.  
  234. /**
  235. * Возвращает ссылку на профиль пользователя
  236. *
  237. * @return HtmlString Путь к профилю
  238. */
  239. public function getProfile(): HtmlString
  240. {
  241. if ($this->id) {
  242. $admin = null;
  243. $name = check($this->getName());
  244.  
  245. if ($this->color) {
  246. $name = '<span style="color:' . $this->color . '">' . $name . '</span>';
  247. }
  248.  
  249. if (in_array($this->level, self::ADMIN_GROUPS, true)) {
  250. $admin = ' <i class="fas fa-xs fa-star text-info" title="' . $this->getLevel() . '"></i>';
  251. }
  252.  
  253. $html = '<a class="section-author fw-bold" href="/users/' . $this->login . '" data-login="@' . $this->login . '">' . $name . '</a>';
  254.  
  255. return new HtmlString($html . $admin);
  256. }
  257.  
  258. $html = '<span class="section-author fw-bold" data-login="' . setting('deleted_user') . '">' . setting('deleted_user') . '</span>';
  259.  
  260. return new HtmlString($html);
  261. }
  262.  
  263. /**
  264. * Возвращает пол пользователя
  265. *
  266. * @return HtmlString пол пользователя
  267. */
  268. public function getGender(): HtmlString
  269. {
  270. if ($this->gender === 'female') {
  271. return new HtmlString('<i class="fa fa-female fa-lg"></i>');
  272. }
  273.  
  274. return new HtmlString('<i class="fa fa-male fa-lg"></i>');
  275. }
  276.  
  277. /**
  278. * Авторизует пользователя
  279. *
  280. * @param string $login Логин
  281. * @param string $password Пароль пользователя
  282. * @param bool $remember Запомнить пароль
  283. *
  284. * @return User|bool
  285. */
  286. public static function auth(string $login, string $password, bool $remember = true)
  287. {
  288. if (! empty($login) && ! empty($password)) {
  289. $user = getUserByLoginOrEmail($login);
  290.  
  291. if ($user && password_verify($password, $user->password)) {
  292. (new self())->rememberUser($user, $remember);
  293.  
  294. // Сохранение привязки к соц. сетям
  295. if (session()->has('social')) {
  296. Social::query()->create([
  297. 'user_id' => $user->id,
  298. 'network' => session()->get('social')->network,
  299. 'uid' => session()->get('social')->uid,
  300. 'created_at' => SITETIME,
  301. ]);
  302. }
  303.  
  304. $user->saveVisit(Login::AUTH);
  305.  
  306. return $user;
  307. }
  308. }
  309.  
  310. return false;
  311. }
  312.  
  313. /**
  314. * Авторизует через социальные сети
  315. *
  316. * @param string $token идентификатор Ulogin
  317. *
  318. * @return User|bool
  319. * @throws GuzzleException
  320. */
  321. public static function socialAuth(string $token)
  322. {
  323. $client = new Client(['timeout' => 30.0]);
  324.  
  325. $response = $client->get('//ulogin.ru/token.php', [
  326. 'query' => [
  327. 'token' => $token,
  328. 'host' => $_SERVER['HTTP_HOST'],
  329. ]
  330. ]);
  331.  
  332. if ($response->getStatusCode() === 200) {
  333. $network = json_decode($response->getBody()->getContents());
  334.  
  335. session()->put('social', $network);
  336.  
  337. /** @var Social $social */
  338. $social = Social::query()
  339. ->where('network', $network->network)
  340. ->where('uid', $network->uid)
  341. ->first();
  342.  
  343. if ($social && $user = getUserById($social->user_id)) {
  344. (new self())->rememberUser($user, true);
  345.  
  346. $user->saveVisit(Login::SOCIAL);
  347.  
  348. return $user;
  349. }
  350. }
  351.  
  352. return false;
  353. }
  354.  
  355. /**
  356. * Возвращает название уровня по ключу
  357. *
  358. * @param string $level
  359. * @return string
  360. */
  361. public static function getLevelByKey(string $level): string
  362. {
  363. switch ($level) {
  364. case self::BOSS:
  365. $status = __('main.boss');
  366. break;
  367. case self::ADMIN:
  368. $status = __('main.admin');
  369. break;
  370. case self::MODER:
  371. $status = __('main.moder');
  372. break;
  373. case self::EDITOR:
  374. $status = __('main.editor');
  375. break;
  376. case self::USER:
  377. $status = __('main.user');
  378. break;
  379. case self::PENDED:
  380. $status = __('main.pended');
  381. break;
  382. case self::BANNED:
  383. $status = __('main.banned');
  384. break;
  385. default:
  386. $status = setting('statusdef');
  387. }
  388.  
  389. return $status;
  390. }
  391.  
  392. /**
  393. * Возвращает уровень пользователя
  394. *
  395. * @return string Уровень пользователя
  396. */
  397. public function getLevel(): string
  398. {
  399. return self::getLevelByKey($this->level);
  400. }
  401.  
  402. /**
  403. * Is user online
  404. *
  405. * @return bool
  406. */
  407. public function isOnline(): bool
  408. {
  409. static $visits;
  410.  
  411. if (! $visits) {
  412. $visits = Cache::remember('visit', 10, static function () {
  413. return Online::query()
  414. ->whereNotNull('user_id')
  415. ->pluck('user_id', 'user_id')
  416. ->all();
  417. });
  418. }
  419.  
  420. return isset($visits[$this->id]);
  421. }
  422.  
  423. /**
  424. * User online status
  425. *
  426. * @return HtmlString онлайн-статус
  427. */
  428. public function getOnline(): HtmlString
  429. {
  430. $online = '';
  431.  
  432. if ($this->isOnline()) {
  433. $online = '<div class="user-status bg-success" title="' . __('main.online') . '"></div>';
  434. }
  435.  
  436. return new HtmlString($online);
  437. }
  438.  
  439. /**
  440. * Get last visit
  441. *
  442. * @return string
  443. */
  444. public function getVisit(): string
  445. {
  446. if ($this->isOnline()) {
  447. $visit = __('main.online');
  448. } else {
  449. $visit = dateFixed($this->updated_at);
  450. }
  451.  
  452. return $visit;
  453. }
  454.  
  455. /**
  456. * Возвращает статус пользователя
  457. *
  458. * @return HtmlString|string статус пользователя
  459. */
  460. public function getStatus()
  461. {
  462. static $status;
  463.  
  464. if (! $this->id) {
  465. return setting('statusdef');
  466. }
  467.  
  468. if (! $status) {
  469. $status = $this->getStatuses(6 * 3600);
  470. }
  471.  
  472. if (isset($status[$this->id])) {
  473. return new HtmlString($status[$this->id]);
  474. }
  475.  
  476. return setting('statusdef');
  477. }
  478.  
  479. /**
  480. * Возвращает аватар пользователя
  481. *
  482. * @return HtmlString аватар пользователя
  483. */
  484. public function getAvatar(): HtmlString
  485. {
  486. if (! $this->id) {
  487. return new HtmlString($this->getAvatarGuest());
  488. }
  489.  
  490. if ($this->avatar && file_exists(public_path($this->avatar))) {
  491. $avatar = $this->getAvatarImage();
  492. } else {
  493. $avatar = $this->getAvatarDefault();
  494. }
  495.  
  496. return new HtmlString('<a href="/users/' . $this->login . '">' . $avatar . '</a> ');
  497. }
  498.  
  499. /**
  500. * Возвращает изображение аватара
  501. *
  502. * @return HtmlString
  503. */
  504. public function getAvatarImage(): HtmlString
  505. {
  506. if (! $this->id) {
  507. return $this->getAvatarGuest();
  508. }
  509.  
  510. if ($this->avatar && file_exists(public_path($this->avatar))) {
  511. return new HtmlString('<img class="avatar-default rounded-circle" src="' . $this->avatar . '" alt="">');
  512. }
  513.  
  514. return $this->getAvatarDefault();
  515. }
  516.  
  517. /**
  518. * Get guest avatar
  519. *
  520. * @return HtmlString
  521. */
  522. public function getAvatarGuest(): HtmlString
  523. {
  524. return new HtmlString('<img class="avatar-default rounded-circle" src="/assets/img/images/avatar_guest.png" alt=""> ');
  525. }
  526.  
  527. /**
  528. * Возвращает аватар для пользователя по умолчанию
  529. *
  530. * @return HtmlString код аватара
  531. */
  532. private function getAvatarDefault(): HtmlString
  533. {
  534. $name = $this->getName();
  535. $color = '#' . substr(dechex(crc32($this->login)), 0, 6);
  536. $letter = mb_strtoupper(utfSubstr($name, 0, 1), 'utf-8');
  537.  
  538. return new HtmlString('<span class="avatar-default rounded-circle" style="background:' . $color . '">' . $letter . '</span>');
  539. }
  540.  
  541. /**
  542. * Кеширует статусы пользователей
  543. *
  544. * @param int $seconds время кеширования
  545. *
  546. * @return array
  547. */
  548. public function getStatuses(int $seconds): array
  549. {
  550. return Cache::remember('status', $seconds, static function () {
  551. $users = self::query()
  552. ->select('users.id', 'users.status', 'status.name', 'status.color')
  553. ->leftJoin('status', static function (JoinClause $join) {
  554. $join->whereRaw('users.point between status.topoint and status.point');
  555. })
  556. ->where('users.point', '>', 0)
  557. ->toBase()
  558. ->get();
  559.  
  560. $statuses = [];
  561. foreach ($users as $user) {
  562. if ($user->status) {
  563. $statuses[$user->id] = '<span style="color:#ff0000">' . check($user->status) . '</span>';
  564. continue;
  565. }
  566.  
  567. if ($user->color) {
  568. $statuses[$user->id] = '<span style="color:' . $user->color . '">' . check($user->name) . '</span>';
  569. continue;
  570. }
  571.  
  572. $statuses[$user->id] = check($user->name);
  573. }
  574.  
  575. return $statuses;
  576. });
  577. }
  578.  
  579. /**
  580. * Возвращает находится ли пользователь в контакатх
  581. *
  582. * @param User $user объект пользователя
  583. * @return bool находится ли в контактах
  584. */
  585. public function isContact(User $user): bool
  586. {
  587. $isContact = Contact::query()
  588. ->where('user_id', $this->id)
  589. ->where('contact_id', $user->id)
  590. ->first();
  591.  
  592. if ($isContact) {
  593. return true;
  594. }
  595.  
  596. return false;
  597. }
  598.  
  599. /**
  600. * Возвращает находится ли пользователь в игноре
  601. *
  602. * @param User $user объект пользователя
  603. * @return bool находится ли в игноре
  604. */
  605. public function isIgnore(User $user): bool
  606. {
  607. $isIgnore = Ignore::query()
  608. ->where('user_id', $this->id)
  609. ->where('ignore_id', $user->id)
  610. ->first();
  611.  
  612. if ($isIgnore) {
  613. return true;
  614. }
  615.  
  616. return false;
  617. }
  618.  
  619. /**
  620. * Отправляет приватное сообщение
  621. *
  622. * @param User|null $author Отправитель
  623. * @param string $text Текст сообщения
  624. * @param bool $withAuthor Создавать диалог для автора
  625. *
  626. * @return Builder|Model
  627. */
  628. public function sendMessage(?User $author, string $text, bool $withAuthor = true)
  629. {
  630. return (new Message())->createDialogue($this, $author, $text, $withAuthor);
  631. }
  632.  
  633. /**
  634. * Возвращает количество писем пользователя
  635. *
  636. * @return int количество писем
  637. */
  638. public function getCountMessages(): int
  639. {
  640. return Dialogue::query()->where('user_id', $this->id)->count();
  641. }
  642.  
  643. /**
  644. * Возвращает размер контакт-листа
  645. *
  646. * @return int количество контактов
  647. */
  648. public function getCountContact(): int
  649. {
  650. return Contact::query()->where('user_id', $this->id)->count();
  651. }
  652.  
  653. /**
  654. * Возвращает размер игнор-листа
  655. *
  656. * @return int количество игнорируемых
  657. */
  658. public function getCountIgnore(): int
  659. {
  660. return Ignore::query()->where('user_id', $this->id)->count();
  661. }
  662.  
  663. /**
  664. * Возвращает количество записей на стене сообщений
  665. *
  666. * @return int количество записей
  667. */
  668. public function getCountWall(): int
  669. {
  670. return Wall::query()->where('user_id', $this->id)->count();
  671. }
  672.  
  673. /**
  674. * Удаляет альбом пользователя
  675. *
  676. * @return void
  677. */
  678. public function deleteAlbum(): void
  679. {
  680. $photos = Photo::query()->where('user_id', $this->id)->get();
  681.  
  682. if ($photos->isNotEmpty()) {
  683. foreach ($photos as $photo) {
  684. $photo->comments()->delete();
  685. $photo->delete();
  686. }
  687. }
  688. }
  689.  
  690. /**
  691. * Удаляет записи пользователя из всех таблиц
  692. *
  693. * @return bool|null Результат удаления
  694. */
  695. public function delete(): ?bool
  696. {
  697. deleteFile(public_path($this->picture));
  698. deleteFile(public_path($this->avatar));
  699.  
  700. Message::query()->where('user_id', $this->id)->delete();
  701. Dialogue::query()->where('user_id', $this->id)->delete();
  702. Contact::query()->where('user_id', $this->id)->delete();
  703. Ignore::query()->where('user_id', $this->id)->delete();
  704. Rating::query()->where('user_id', $this->id)->delete();
  705. Wall::query()->where('user_id', $this->id)->delete();
  706. Note::query()->where('user_id', $this->id)->delete();
  707. Notebook::query()->where('user_id', $this->id)->delete();
  708. Banhist::query()->where('user_id', $this->id)->delete();
  709. Bookmark::query()->where('user_id', $this->id)->delete();
  710. Login::query()->where('user_id', $this->id)->delete();
  711. Invite::query()->where('user_id', $this->id)->orWhere('invite_user_id', $this->id)->delete();
  712.  
  713. return parent::delete();
  714. }
  715.  
  716.  
  717. /**
  718. * Updates count messages
  719. *
  720. * @return void
  721. */
  722. public function updatePrivate(): void
  723. {
  724. if ($this->newprivat) {
  725. $countDialogues = Dialogue::query()
  726. ->where('user_id', $this->id)
  727. ->where('reading', 0)
  728. ->count();
  729.  
  730. if ($countDialogues !== $this->newprivat) {
  731. $this->update([
  732. 'newprivat' => $countDialogues,
  733. 'sendprivatmail' => 0,
  734. ]);
  735. }
  736. }
  737. }
  738.  
  739. /**
  740. * Check user banned
  741. *
  742. * @return bool
  743. */
  744. public function isBanned(): bool
  745. {
  746. return $this->level === self::BANNED;
  747. }
  748.  
  749. /**
  750. * Check user pended
  751. *
  752. * @return bool
  753. */
  754. public function isPended(): bool
  755. {
  756. return setting('regkeys') && $this->level === self::PENDED;
  757. }
  758.  
  759. /**
  760. * Check user active
  761. *
  762. * @return bool
  763. */
  764. public function isActive(): bool
  765. {
  766. return in_array($this->level, self::USER_GROUPS, true);
  767. }
  768.  
  769. /**
  770. * Getting daily bonus
  771. *
  772. * @return void
  773. */
  774. public function gettingBonus(): void
  775. {
  776. if ($this->isActive() && $this->timebonus < strtotime('-23 hours', SITETIME)) {
  777. $this->increment('money', setting('bonusmoney'));
  778. $this->update(['timebonus' => SITETIME]);
  779.  
  780. setFlash('success', __('main.daily_bonus', ['money' => plural(setting('bonusmoney'), setting('moneyname'))]));
  781. }
  782. }
  783.  
  784. /**
  785. * Сохраняет посещения
  786. *
  787. * @param string $type
  788. *
  789. * @return void
  790. */
  791. public function saveVisit(string $type): void
  792. {
  793. $authorization = Login::query()
  794. ->where('user_id', $this->id)
  795. ->where('created_at', '>', SITETIME - 60)
  796. ->first();
  797.  
  798. if (! $authorization) {
  799. Login::query()->create([
  800. 'user_id' => $this->id,
  801. 'ip' => getIp(),
  802. 'brow' => getBrowser(),
  803. 'created_at' => SITETIME,
  804. 'type' => $type,
  805. ]);
  806. }
  807.  
  808. $this->increment('visits');
  809. }
  810.  
  811. /**
  812. * Remember user
  813. *
  814. * @param User $user
  815. * @param bool $remember
  816. */
  817. private function rememberUser(User $user, bool $remember = false): void
  818. {
  819. if ($remember) {
  820. cookie()->queue(cookie()->forever('login', $user->login));
  821. cookie()->queue(cookie()->forever('password', $user->password));
  822. }
  823.  
  824. session()->put('id', $user->id);
  825. session()->put('password', $user->password);
  826. session()->put('online');
  827. }
  828. }