From b48a9222d131e039c193dcffb373122a35d19a55 Mon Sep 17 00:00:00 2001 From: Roardom Date: Tue, 25 Jul 2023 22:45:09 +0000 Subject: [PATCH 01/62] update: cache personal freeleech existence instead of its value We only cache it if it exists, so let's not query it every single time it doesn't exist. We don't remove it from the hourly run command yet so that currently existing freeleeches can be deleted. --- app/Http/Controllers/HomeController.php | 2 +- app/Http/Controllers/SimilarTorrentController.php | 2 +- app/Http/Controllers/TorrentController.php | 2 +- app/Http/Controllers/User/TransactionController.php | 5 +++-- app/Http/Livewire/PersonCredit.php | 2 +- app/Http/Livewire/SimilarTorrent.php | 2 +- app/Http/Livewire/Top10.php | 2 +- app/Http/Livewire/TorrentSearch.php | 2 +- app/Jobs/ProcessAnnounce.php | 6 +----- app/Models/Torrent.php | 2 +- 10 files changed, 12 insertions(+), 15 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 04c367cd64..a3f577fa73 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -57,7 +57,7 @@ public function index(Request $request): \Illuminate\Contracts\View\Factory|\Ill return view('home.index', [ 'user' => $user, - 'personal_freeleech' => cache()->get('personal_freeleech:'.$user->id), + 'personal_freeleech' => cache()->has('personal_freeleech:'.$user->id), 'users' => cache()->remember( 'online_users', $expiresAt, diff --git a/app/Http/Controllers/SimilarTorrentController.php b/app/Http/Controllers/SimilarTorrentController.php index 5e8bf53a1b..b2c2549be6 100644 --- a/app/Http/Controllers/SimilarTorrentController.php +++ b/app/Http/Controllers/SimilarTorrentController.php @@ -89,7 +89,7 @@ public function show(int $categoryId, int $tmdbId): \Illuminate\Contracts\View\F break; } - $personalFreeleech = cache()->get('personal_freeleech:'.auth()->id()); + $personalFreeleech = cache()->has('personal_freeleech:'.auth()->id()); return view('torrent.similar', [ 'meta' => $meta, diff --git a/app/Http/Controllers/TorrentController.php b/app/Http/Controllers/TorrentController.php index 4341180c81..ff699b3e86 100644 --- a/app/Http/Controllers/TorrentController.php +++ b/app/Http/Controllers/TorrentController.php @@ -131,7 +131,7 @@ public function show(Request $request, int|string $id): \Illuminate\Contracts\Vi return view('torrent.show', [ 'torrent' => $torrent, 'user' => $user, - 'personal_freeleech' => cache()->get('personal_freeleech:'.$user->id), + 'personal_freeleech' => cache()->has('personal_freeleech:'.$user->id), 'freeleech_token' => cache()->get('freeleech_token:'.$user->id.':'.$torrent->id), 'meta' => $meta, 'trailer' => $trailer, diff --git a/app/Http/Controllers/User/TransactionController.php b/app/Http/Controllers/User/TransactionController.php index b490e49ce3..1a594bd432 100644 --- a/app/Http/Controllers/User/TransactionController.php +++ b/app/Http/Controllers/User/TransactionController.php @@ -83,13 +83,14 @@ public function store(StoreTransactionRequest $request, User $user): \Illuminate break; case $bonExchange->personal_freeleech: - if (cache()->get('personal_freeleech:'.$user->id)) { + if (cache()->has('personal_freeleech:'.$user->id)) { return back()->withErrors('Your previous personal freeleech is still active.'); } PersonalFreeleech::create(['user_id' => $user->id]); - cache()->put('personal_freeleech:'.$user->id, true); + // Allow the user 25 hours since the AutoRemovePersonalFreeleech command is only run every hour. + cache()->put('personal_freeleech:'.$user->id, true, now()->addHours(25)); Unit3dAnnounce::addPersonalFreeleech($user->id); diff --git a/app/Http/Livewire/PersonCredit.php b/app/Http/Livewire/PersonCredit.php index 22fa03c036..6f7c741611 100644 --- a/app/Http/Livewire/PersonCredit.php +++ b/app/Http/Livewire/PersonCredit.php @@ -49,7 +49,7 @@ final public function mount(): void final public function getPersonalFreeleechProperty() { - return cache()->get('personal_freeleech:'.auth()->user()->id); + return cache()->has('personal_freeleech:'.auth()->user()->id); } /* diff --git a/app/Http/Livewire/SimilarTorrent.php b/app/Http/Livewire/SimilarTorrent.php index b4a13ba558..41f1b43974 100644 --- a/app/Http/Livewire/SimilarTorrent.php +++ b/app/Http/Livewire/SimilarTorrent.php @@ -216,7 +216,7 @@ final public function deleteRecords(): void final public function getPersonalFreeleechProperty() { - return cache()->get('personal_freeleech:'.auth()->id()); + return cache()->has('personal_freeleech:'.auth()->id()); } final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application diff --git a/app/Http/Livewire/Top10.php b/app/Http/Livewire/Top10.php index dac81c1e5f..00a041b824 100644 --- a/app/Http/Livewire/Top10.php +++ b/app/Http/Livewire/Top10.php @@ -67,7 +67,7 @@ final public function getTorrentsAllProperty() final public function getPersonalFreeleechProperty() { - return cache()->get('personal_freeleech:'.auth()->id()); + return cache()->has('personal_freeleech:'.auth()->id()); } final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application diff --git a/app/Http/Livewire/TorrentSearch.php b/app/Http/Livewire/TorrentSearch.php index a2c103167b..b244ddb6d7 100644 --- a/app/Http/Livewire/TorrentSearch.php +++ b/app/Http/Livewire/TorrentSearch.php @@ -175,7 +175,7 @@ final public function updatedView(): void final public function getPersonalFreeleechProperty() { - return cache()->get('personal_freeleech:'.auth()->id()); + return cache()->has('personal_freeleech:'.auth()->id()); } final public function getTorrentsProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator diff --git a/app/Jobs/ProcessAnnounce.php b/app/Jobs/ProcessAnnounce.php index 5fb24b4385..9ecf5eec79 100644 --- a/app/Jobs/ProcessAnnounce.php +++ b/app/Jobs/ProcessAnnounce.php @@ -17,7 +17,6 @@ use App\Models\FreeleechToken; use App\Models\History; use App\Models\Peer; -use App\Models\PersonalFreeleech; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; @@ -117,10 +116,7 @@ public function handle(): void } // Modification of Upload and Download (Check cache but in case redis data was lost hit DB) - $personalFreeleech = cache()->get('personal_freeleech:'.$this->user->id) ?? - PersonalFreeleech::query() - ->where('user_id', '=', $this->user->id) - ->exists(); + $personalFreeleech = cache()->has('personal_freeleech:'.$this->user->id); $freeleechToken = cache()->get('freeleech_token:'.$this->user->id.':'.$this->torrent->id) ?? FreeleechToken::query() ->where('user_id', '=', $this->user->id) diff --git a/app/Models/Torrent.php b/app/Models/Torrent.php index 821c9f98ab..19bed49673 100644 --- a/app/Models/Torrent.php +++ b/app/Models/Torrent.php @@ -323,7 +323,7 @@ public function notifyUploader($type, $payload): bool */ public function isFreeleech($user = null): bool { - $pfree = $user && ($user->group->is_freeleech || cache()->get('personal_freeleech:'.$user->id)); + $pfree = $user && ($user->group->is_freeleech || cache()->has('personal_freeleech:'.$user->id)); return $this->free || config('other.freeleech') || $pfree; } From 7d3a9e244ebe0150ff5cf38a3a1818c67cbb655e Mon Sep 17 00:00:00 2001 From: Roardom Date: Wed, 26 Jul 2023 01:39:31 +0000 Subject: [PATCH 02/62] fix: redundant fetching of records from the database The `SerializesModels` trait fetches a new copy of the record from the database, causing 4 more queries than we thought we were using. This change reduces the query time in the ProcessAnnounce job by 55%. --- app/Http/Controllers/AnnounceController.php | 2 +- app/Jobs/ProcessAnnounce.php | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/AnnounceController.php b/app/Http/Controllers/AnnounceController.php index 852041b498..b3c655e99c 100644 --- a/app/Http/Controllers/AnnounceController.php +++ b/app/Http/Controllers/AnnounceController.php @@ -567,7 +567,7 @@ private function generateSuccessAnnounceResponse($queries, $torrent, $user): arr */ private function processAnnounceJob($queries, $user, $group, $torrent): void { - ProcessAnnounce::dispatch($queries, $user, $group, $torrent); + ProcessAnnounce::dispatch($queries, serialize($user), serialize($group), serialize($torrent)); } protected function generateFailedAnnounceResponse(TrackerException $trackerException): array diff --git a/app/Jobs/ProcessAnnounce.php b/app/Jobs/ProcessAnnounce.php index 5fb24b4385..2a56396eff 100644 --- a/app/Jobs/ProcessAnnounce.php +++ b/app/Jobs/ProcessAnnounce.php @@ -15,15 +15,18 @@ namespace App\Jobs; use App\Models\FreeleechToken; +use App\Models\Group; use App\Models\History; use App\Models\Peer; use App\Models\PersonalFreeleech; +use App\Models\Torrent; +use App\Models\User; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; +use Illuminate\Database\Eloquent\Collection; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\Middleware\WithoutOverlapping; -use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Redis; @@ -32,13 +35,19 @@ class ProcessAnnounce implements ShouldQueue use Dispatchable; use InteractsWithQueue; use Queueable; - use SerializesModels; + + private Torrent $torrent; + private User $user; + private Group $group; /** * Create a new job instance. */ - public function __construct(protected $queries, protected $user, protected $group, protected $torrent) + public function __construct(protected array $queries, string $user, string $group, string $torrent) { + $this->torrent = unserialize($torrent, ['allowed_classes' => [Torrent::class, Collection::class, Peer::class]]); + $this->user = unserialize($user, ['allowed_classes' => [User::class]]); + $this->group = unserialize($group, ['allowed_classes' => [Group::class]]); } /** From 88d6ae1d89bcc67019f40f39c84a864ef7e8fb19 Mon Sep 17 00:00:00 2001 From: Roardom Date: Wed, 26 Jul 2023 02:24:44 +0000 Subject: [PATCH 03/62] fix: ci --- app/Jobs/ProcessAnnounce.php | 10 +++++----- app/Models/Peer.php | 10 ++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/Jobs/ProcessAnnounce.php b/app/Jobs/ProcessAnnounce.php index 2a56396eff..01ec71fb57 100644 --- a/app/Jobs/ProcessAnnounce.php +++ b/app/Jobs/ProcessAnnounce.php @@ -165,7 +165,7 @@ public function handle(): void $peer->agent = $this->queries['user-agent']; $peer->uploaded = $realUploaded; $peer->downloaded = $realDownloaded; - $peer->seeder = (int) ($this->queries['left'] == 0); + $peer->seeder = $this->queries['left'] == 0; $peer->left = $this->queries['left']; $peer->torrent_id = $this->torrent->id; $peer->user_id = $this->user->id; @@ -178,14 +178,14 @@ public function handle(): void switch ($event) { case 'started': - $peer->active = 1; + $peer->active = true; $history->active = 1; $history->immune = (int) ($history->exists ? $history->immune && $this->group->is_immune : $this->group->is_immune); break; case 'completed': - $peer->active = 1; + $peer->active = true; $history->active = 1; $history->uploaded += $modUploaded; @@ -215,7 +215,7 @@ public function handle(): void break; case 'stopped': - $peer->active = 0; + $peer->active = false; $history->active = 0; $history->uploaded += $modUploaded; @@ -240,7 +240,7 @@ public function handle(): void // End User Update break; default: - $peer->active = 1; + $peer->active = true; $history->active = 1; $history->uploaded += $modUploaded; diff --git a/app/Models/Peer.php b/app/Models/Peer.php index 5ccf52e103..67c6d404ba 100644 --- a/app/Models/Peer.php +++ b/app/Models/Peer.php @@ -23,6 +23,16 @@ class Peer extends Model { use HasFactory; + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'active' => 'boolean', + 'seeder' => 'boolean', + ]; + /** * Prepare a date for array / JSON serialization. */ From c7db489c21e155d0acbdadbca9c94deeff1a1e98 Mon Sep 17 00:00:00 2001 From: HDVinnie Date: Tue, 25 Jul 2023 22:45:00 -0400 Subject: [PATCH 04/62] fix: broken route --- resources/views/torrent/partials/buttons.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/torrent/partials/buttons.blade.php b/resources/views/torrent/partials/buttons.blade.php index 6fa7f1f63d..42c9a87cf1 100644 --- a/resources/views/torrent/partials/buttons.blade.php +++ b/resources/views/torrent/partials/buttons.blade.php @@ -323,7 +323,7 @@ class="dialog__form" @endif - @if (DB::table('graveyard')->where('torrent_id', '=', $torrent->id)->where('rewarded', '=', 0)->exists()) + @if (DB::table('resurrections')->where('torrent_id', '=', $torrent->id)->where('rewarded', '=', 0)->exists())
  • -
      +
        @forelse($comments as $comment) @empty From cc62da5ce843f29bb22c9a6acb7fe20c7f992d1a Mon Sep 17 00:00:00 2001 From: Roardom Date: Tue, 1 Aug 2023 06:34:31 +0000 Subject: [PATCH 57/62] update: improve ticket search Entangle the "show closed ticket" state using alpinejs. Change the "show closed ticket" from a checkbox to two tabs showing open and closed tickets respectively. Remove the "closed" indicator because all tickets are either open or closed depending on the tab. Add an updated_at column as that's the default sort order. --- app/Http/Livewire/TicketSearch.php | 38 ++++------ phpstan-baseline.neon | 15 ---- resources/sass/components/_panel.scss | 9 +++ .../views/livewire/ticket-search.blade.php | 70 +++++++++++-------- 4 files changed, 64 insertions(+), 68 deletions(-) diff --git a/app/Http/Livewire/TicketSearch.php b/app/Http/Livewire/TicketSearch.php index ad9877635c..e84fb1109d 100644 --- a/app/Http/Livewire/TicketSearch.php +++ b/app/Http/Livewire/TicketSearch.php @@ -14,16 +14,20 @@ namespace App\Http\Livewire; use App\Models\Ticket; +use App\Models\User; use Livewire\Component; use Livewire\WithPagination; +/** + * @property \Illuminate\Contracts\Pagination\LengthAwarePaginator $tickets + */ class TicketSearch extends Component { use WithPagination; - public ?\Illuminate\Contracts\Auth\Authenticatable $user = null; + public ?User $user = null; - public bool $show = false; + public string $tab = 'open'; public int $perPage = 25; @@ -35,7 +39,7 @@ class TicketSearch extends Component protected $queryString = [ 'search' => ['except' => ''], - 'show' => ['except' => false], + 'tab' => ['except' => 'open'], ]; final public function mount(): void @@ -53,35 +57,21 @@ final public function updatingSearch(): void $this->resetPage(); } - final public function updatingShow(): void + final public function updatingTab(): void { $this->resetPage(); } - final public function toggleProperties($property): void - { - if ($property === 'show') { - $this->show = ! $this->show; - } - } - final public function getTicketsProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator { - if ($this->user->group->is_modo) { - return Ticket::query() - ->with(['user.group', 'staff.group', 'category', 'priority']) - ->when($this->show === false, fn ($query) => $query->whereNull('closed_at')) - ->when($this->show, fn ($query) => $query->whereNotNull('closed_at')) - ->when($this->search, fn ($query) => $query->where('subject', 'LIKE', '%'.$this->search.'%')) - ->orderBy($this->sortField, $this->sortDirection) - ->paginate($this->perPage); - } - return Ticket::query() ->with(['user.group', 'staff.group', 'category', 'priority']) - ->where('user_id', '=', $this->user->id) - ->when($this->show === false, fn ($query) => $query->whereNull('closed_at')) - ->when($this->show, fn ($query) => $query->whereNotNull('closed_at')) + ->when(! $this->user->group->is_modo, fn ($query) => $query->where('user_id', '=', $this->user->id)) + ->when( + $this->tab === 'open', + fn ($query) => $query->whereNull('closed_at'), + fn ($query) => $query->whereNotNull('closed_at') + ) ->when($this->search, fn ($query) => $query->where('subject', 'LIKE', '%'.$this->search.'%')) ->orderBy($this->sortField, $this->sortDirection) ->paginate($this->perPage); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c38cb2fd34..7b0eb448d5 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -960,21 +960,6 @@ parameters: count: 4 path: app/Http/Livewire/ThankButton.php - - - message: "#^Access to an undefined property App\\\\Http\\\\Livewire\\\\TicketSearch\\:\\:\\$tickets\\.$#" - count: 1 - path: app/Http/Livewire/TicketSearch.php - - - - message: "#^Access to an undefined property Illuminate\\\\Contracts\\\\Auth\\\\Authenticatable\\:\\:\\$group\\.$#" - count: 1 - path: app/Http/Livewire/TicketSearch.php - - - - message: "#^Access to an undefined property Illuminate\\\\Contracts\\\\Auth\\\\Authenticatable\\:\\:\\$id\\.$#" - count: 1 - path: app/Http/Livewire/TicketSearch.php - - message: "#^Access to an undefined property App\\\\Http\\\\Livewire\\\\Top10\\:\\:\\$personalFreeleech\\.$#" count: 1 diff --git a/resources/sass/components/_panel.scss b/resources/sass/components/_panel.scss index ef4ce1d1e3..b34a314245 100644 --- a/resources/sass/components/_panel.scss +++ b/resources/sass/components/_panel.scss @@ -100,6 +100,10 @@ background-color: var(--data-table-th-bg); } +.panel__tabs--centered { + justify-content: space-around; +} + .panel__tab { background-color: var(--data-table-th-fg); padding: 8px 16px 6px 16px; @@ -109,6 +113,11 @@ white-space: nowrap; } +.panel__tab--full-width { + flex: 1; + text-align: center; +} + .panel__tab--active { color: #b0b0b0; border-bottom: 2px solid #888; diff --git a/resources/views/livewire/ticket-search.blade.php b/resources/views/livewire/ticket-search.blade.php index 2b5b5ecbb9..64b3a90139 100644 --- a/resources/views/livewire/ticket-search.blade.php +++ b/resources/views/livewire/ticket-search.blade.php @@ -1,20 +1,7 @@ -
        +

        {{ __('ticket.helpdesk') }}

        -
        -
        - - -
        -
        -
        @@ -41,7 +28,7 @@ class="form__text" wire:model="search" placeholder=" " /> -
        @@ -53,6 +40,26 @@ class="form__text"
        + + + +
        @@ -73,10 +80,6 @@ class="form__text" {{ __('common.username') }} @include('livewire.includes._sort-icon', ['field' => 'user_id']) - + + @forelse ($tickets as $ticket) @@ -122,15 +133,6 @@ class="form__text" - + +
        - {{ __('common.status') }} - @include('livewire.includes._sort-icon', ['field' => 'closed_at']) - {{ __('ticket.assigned-staff') }} @include('livewire.includes._sort-icon', ['field' => 'staff_id']) @@ -85,6 +88,14 @@ class="form__text" {{ __('ticket.created') }} @include('livewire.includes._sort-icon', ['field' => 'created_at']) + {{ __('torrent.updated') }} + @include('livewire.includes._sort-icon', ['field' => 'updated_at']) + + {{ __('ticket.closed') }} + @include('livewire.includes._sort-icon', ['field' => 'closed_at']) + {{ __('common.action') }}
        - @if($ticket->closed_at) - - Closed - @else - - Open - @endif - @if ($ticket->staff) @@ -143,6 +145,16 @@ class="form__text" {{ $ticket->created_at->diffForHumans() }} + + + +
      1. From 929ac50f0b433bc39917c20773fdd5995c43ba12 Mon Sep 17 00:00:00 2001 From: Roardom Date: Fri, 14 Jul 2023 10:25:00 +0000 Subject: [PATCH 58/62] add: livewire user warnings --- app/Http/Livewire/UserWarnings.php | 254 ++++++++++++++ lang/en/user.php | 1 + .../views/livewire/user-warnings.blade.php | 271 ++++++++++++++ .../user/profile/partials/warnings.blade.php | 330 ------------------ resources/views/user/profile/show.blade.php | 6 +- 5 files changed, 530 insertions(+), 332 deletions(-) create mode 100644 app/Http/Livewire/UserWarnings.php create mode 100644 resources/views/livewire/user-warnings.blade.php delete mode 100644 resources/views/user/profile/partials/warnings.blade.php diff --git a/app/Http/Livewire/UserWarnings.php b/app/Http/Livewire/UserWarnings.php new file mode 100644 index 0000000000..a583937349 --- /dev/null +++ b/app/Http/Livewire/UserWarnings.php @@ -0,0 +1,254 @@ + + * @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0 + */ + +namespace App\Http\Livewire; + +use App\Models\PrivateMessage; +use App\Models\User; +use App\Models\Warning; +use Illuminate\Support\Carbon; +use Livewire\Component; +use Livewire\WithPagination; + +/** + * @property \Illuminate\Contracts\Pagination\LengthAwarePaginator $warnings + * @property int $automatedWarningsCount + * @property int $manualWarningsCount + * @property int $deletedWarningsCount + */ +class UserWarnings extends Component +{ + use WithPagination; + + public User $user; + + public string $warningTab = 'automated'; + + public string $message = ''; + + public int $perPage = 10; + + public string $sortField = 'created_at'; + + public string $sortDirection = 'desc'; + + protected $queryString = [ + 'warningTab' => ['except' => 'automated'], + ]; + + protected $rules = [ + 'message' => [ + 'required', + 'filled', + 'max:255', + ], + ]; + + final public function getWarningsProperty(): \Illuminate\Contracts\Pagination\LengthAwarePaginator + { + return $this->user + ->userwarning() + ->when( + auth()->user()->group->is_modo, + fn ($query) => $query->with('warneduser', 'staffuser', 'torrenttitle'), + fn ($query) => $query->with('warneduser', 'torrenttitle'), + ) + ->when($this->warningTab === 'automated', fn ($query) => $query->whereNotNull('torrent')) + ->when($this->warningTab === 'manual', fn ($query) => $query->whereNull('torrent')) + ->when($this->warningTab === 'deleted', fn ($query) => $query->onlyTrashed()) + ->paginate($this->perPage); + } + + final public function getAutomatedWarningsCountProperty(): int + { + return $this->user->userwarning()->whereNotNull('torrent')->count(); + } + + final public function getManualWarningsCountProperty(): int + { + return $this->user->userwarning()->whereNull('torrent')->count(); + } + + final public function getDeletedWarningsCountProperty(): int + { + return $this->user->userwarning()->onlyTrashed()->count(); + } + + /** + * Manually warn a user. + */ + final public function store(): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + $this->validate(); + + Warning::create([ + 'user_id' => $this->user->id, + 'warned_by' => auth()->user()->id, + 'torrent' => null, + 'reason' => $this->message, + 'expires_on' => Carbon::now()->addDays(config('hitrun.expire')), + 'active' => '1', + ]); + + $this->message = ''; + + PrivateMessage::create([ + 'sender_id' => User::SYSTEM_USER_ID, + 'receiver_id' => $this->user->id, + 'subject' => 'Received warning', + 'message' => 'You have received a [b]warning[/b]. Reason: '.$this->message, + ]); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'Warning issued successfully!']); + } + + /** + * Deactivate A Warning. + */ + final public function deactivate(Warning $warning): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + $staff = auth()->user(); + + $warning->update([ + 'expires_on' => now(), + 'active' => false, + ]); + + PrivateMessage::create([ + 'sender_id' => $staff->id, + 'receiver_id' => $this->user->id, + 'subject' => 'Hit and Run Warning Deleted', + 'message' => $staff->username.' has decided to deactivate your warning for torrent '.$warning->torrent.' You lucked out! [color=red][b]THIS IS AN AUTOMATED SYSTEM MESSAGE, PLEASE DO NOT REPLY![/b][/color]', + ]); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'Warning Was Successfully Deactivated']); + } + + /** + * Reactivate A Warning. + */ + final public function reactivate(Warning $warning): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + $warning->update([ + 'expires_on' => $warning->created_at->addDays(config('hitrun.expire')), + 'active' => true, + ]); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'Warning Was Successfully Reactivated']); + } + + /** + * Deactivate All Warnings. + */ + final public function massDeactivate(): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + $staff = auth()->user(); + + $this->user + ->warnings() + ->where('active', '=', 1) + ->update([ + 'expires_on' => now(), + 'active' => false, + ]); + + PrivateMessage::create([ + 'sender_id' => $staff->id, + 'receiver_id' => $this->user->id, + 'subject' => 'All Hit and Run Warnings Deleted', + 'message' => $staff->username.' has decided to deactivate all of your warnings. You lucked out! [color=red][b]THIS IS AN AUTOMATED SYSTEM MESSAGE, PLEASE DO NOT REPLY![/b][/color]', + ]); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'All Warnings Were Successfully Deactivated']); + } + + /** + * Delete A Warning. + */ + final public function destroy(Warning $warning): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + $staff = auth()->user(); + + $warning->update([ + 'deleted_by' => $staff->id, + ]); + + $warning->delete(); + + PrivateMessage::create([ + 'sender_id' => $staff->id, + 'receiver_id' => $this->user->id, + 'subject' => 'Hit and Run Warning Deleted', + 'message' => $staff->username.' has decided to delete your warning for torrent '.$warning->torrent.' You lucked out! [color=red][b]THIS IS AN AUTOMATED SYSTEM MESSAGE, PLEASE DO NOT REPLY![/b][/color]', + ]); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'Warning Was Successfully Deleted']); + } + + /** + * Delete All Warnings. + */ + final public function massDestroy(): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + $staff = auth()->user(); + + $this->user->warnings()->update([ + 'deleted_by' => $staff->id, + ]); + + $this->user->warnings()->delete(); + + PrivateMessage::create([ + 'sender_id' => $staff->id, + 'receiver_id' => $this->user->id, + 'subject' => 'All Hit and Run Warnings Deleted', + 'message' => $staff->username.' has decided to delete all of your warnings. You lucked out! [color=red][b]THIS IS AN AUTOMATED SYSTEM MESSAGE, PLEASE DO NOT REPLY![/b][/color]', + ]); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'All Warnings Were Successfully Deleted']); + } + + /** + * Restore A Soft Deleted Warning. + */ + final public function restore(int $id): void + { + abort_unless(auth()->user()->group->is_modo, 403); + + Warning::withTrashed()->findOrFail($id)->restore(); + + $this->dispatchBrowserEvent('success', ['type' => 'success', 'message' => 'Warning Was Successfully Restored']); + } + + final public function render(): \Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Contracts\Foundation\Application + { + return view('livewire.user-warnings', [ + 'warnings' => $this->warnings, + 'automatedWarningsCount' => $this->automatedWarningsCount, + 'manualWarningsCount' => $this->manualWarningsCount, + 'deletedWarningsCount' => $this->deletedWarningsCount, + ]); + } +} diff --git a/lang/en/user.php b/lang/en/user.php index fe1d3d1d6b..679686f64e 100644 --- a/lang/en/user.php +++ b/lang/en/user.php @@ -266,6 +266,7 @@ 'profile-privacy-help' => 'Control which statistics and pieces of information appear on your profile. These settings are overridden if you do not allow any groups to access your profile or if you Go Private', 'public-info' => 'Public Info', + 'reactivate' => 'Reactivate', 'reason-ban' => 'Ban Reason', 'reason-unban' => 'Unban Reason', 'recent-achievements' => 'Recent Achievements', diff --git a/resources/views/livewire/user-warnings.blade.php b/resources/views/livewire/user-warnings.blade.php new file mode 100644 index 0000000000..fd7eac9bcb --- /dev/null +++ b/resources/views/livewire/user-warnings.blade.php @@ -0,0 +1,271 @@ +
        +
        +

        {{ __('user.warnings') }}

        + @if (auth()->user()->group->is_modo) +
        +
        + + +

        + Warn user: {{ $user->username }} +

        +
        +

        + + +

        +

        + + +

        +
        +
        +
        +
        + @csrf + +
        +
        + @csrf + +
        +
        + @endif +
        + + + + + +
        + + + + + @if ($warningTab !== 'manual') + + @endif + + + + + + + + + @forelse ($warnings as $warning) + + + @if ($warningTab !== 'manual') + + @endif + + + + + @if(auth()->user()->group->is_modo) + + @endif + + @empty + + + + @endforelse + +
        {{ __('user.warned-by') }}{{ __('torrent.torrent') }}{{ __('common.reason') }}{{ __('user.created-on') }}{{ __('user.expires-on') }}{{ __('user.active') }}{{ __('common.actions') }}
        + + + @isset($warning->torrenttitle) + + {{ $warning->torrenttitle->name }} + + @else + n/a + @endif + {{ $warning->reason }} + + + + + @if ($warning->active) + + @else + + @endif + + + @if ($warningTab === 'deleted') +
      2. +
        + @csrf + @method('PATCH') + +
        +
      3. + @else + @if ($warning->created_at->addDays(config('hitrun.expire'))->isFuture()) + @if ($warning->active) +
      4. +
        + @csrf + +
        +
      5. + @else +
      6. +
        + @csrf + +
        +
      7. + @endif + @endif +
      8. +
        + @csrf + +
        +
      9. + @endif +
        +
        {{ __('user.no-warning') }}
        + {{ $warnings->links('partials.pagination') }} +
        +
        diff --git a/resources/views/user/profile/partials/warnings.blade.php b/resources/views/user/profile/partials/warnings.blade.php deleted file mode 100644 index 468ff65623..0000000000 --- a/resources/views/user/profile/partials/warnings.blade.php +++ /dev/null @@ -1,330 +0,0 @@ -
        -
        -

        {{ __('user.warnings') }}

        -
        -
        - - -

        - Warn user: {{ $user->username }} -

        -
        - @csrf -

        - - -

        -

        - - -

        -
        -
        -
        -
        - @csrf - @method('DELETE') - -
        -
        -
        - - - - - -
        - - - - - - - - - - - - - - @forelse ($autoWarnings as $warning) - - - - - - - - - - @empty - - - - @endforelse - -
        {{ __('user.warned-by') }}{{ __('torrent.torrent') }}{{ __('common.reason') }}{{ __('user.created-on') }}{{ __('user.expires-on') }}{{ __('user.active') }}{{ __('common.actions') }}
        - - - @isset($warning->torrenttitle) - - {{ $warning->torrenttitle->name }} - - @else - n/a - @endif - {{ $warning->reason }} - - - - - @if ($warning->active) - - @else - - @endif - - -
      10. -
        - @csrf - @method('DELETE') - -
        -
      11. -
        -
        {{ __('user.no-warning') }}
        - {{ $autoWarnings->links('partials.pagination') }} -
        -
        - - - - - - - - - - - - - @forelse ($manualWarnings as $warning) - - - - - - - - - @empty - - - - @endforelse - -
        {{ __('user.warned-by') }}{{ __('common.reason') }}{{ __('user.created-on') }}{{ __('user.expires-on') }}{{ __('user.active') }}{{ __('common.actions') }}
        - - {{ $warning->reason }} - - - - - @if ($warning->active) - - @else - - @endif - - -
      12. -
        - @csrf - @method('DELETE') - -
        -
      13. -
        -
        {{ __('user.no-warning') }}
        - {{ $manualWarnings->links('partials.pagination') }} -
        -
        - - - - - - - - - - - - - - @forelse ($softDeletedWarnings as $warning) - - - - - - - - - - @empty - - - - @endforelse - -
        {{ __('user.warned-by') }}{{ __('torrent.torrent') }}{{ __('common.reason') }}{{ __('user.created-on') }}{{ __('user.deleted-on') }}{{ __('user.deleted-by') }}{{ __('common.actions') }}
        - - - @isset($warning->torrenttitle) - - {{ $warning->torrenttitle->name }} - - @else - n/a - @endif - {{ $warning->reason }}{{ $warning->created_at }}{{ $warning->deleted_at }} - - - -
      14. -
        - @csrf - @method('PATCH') - -
        -
      15. -
        -
        {{ __('user.no-soft-warning') }}
        - {{ $softDeletedWarnings->links('partials.pagination') }} -
        -
        diff --git a/resources/views/user/profile/show.blade.php b/resources/views/user/profile/show.blade.php index 944c555b01..290a87ce8e 100644 --- a/resources/views/user/profile/show.blade.php +++ b/resources/views/user/profile/show.blade.php @@ -111,7 +111,7 @@ class="form__textarea"
        - + @if ($user->isOnline()) @@ -324,7 +324,9 @@ class="user-search__avatar" @endif @if (auth()->user()->group->is_modo) @include('user.profile.partials.bans', ['bans' => $user->userban]) - @include('user.profile.partials.warnings') + @endif + + @if (auth()->user()->group->is_modo)

        Watchlist

        From 900be86d0256f1227436a0c60cc621dfb43bc219 Mon Sep 17 00:00:00 2001 From: Roardom Date: Tue, 1 Aug 2023 09:47:09 +0000 Subject: [PATCH 59/62] add: torrent peers/history tabs --- resources/views/torrent/history.blade.php | 13 +++++++++++++ resources/views/torrent/peers.blade.php | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/resources/views/torrent/history.blade.php b/resources/views/torrent/history.blade.php index 4529463a30..e70eafad5d 100644 --- a/resources/views/torrent/history.blade.php +++ b/resources/views/torrent/history.blade.php @@ -24,6 +24,19 @@
      16. @endsection +@section('nav-tabs') + + +@endsection + @section('content')
        diff --git a/resources/views/torrent/peers.blade.php b/resources/views/torrent/peers.blade.php index 8d5f4e918b..5d20fec62d 100644 --- a/resources/views/torrent/peers.blade.php +++ b/resources/views/torrent/peers.blade.php @@ -24,6 +24,19 @@ @endsection +@section('nav-tabs') + + +@endsection + @section('main')

        {{ __('torrent.torrent') }} {{ __('torrent.peers') }}

        From 594e4e598b324769694c77f434343562ff39e5b9 Mon Sep 17 00:00:00 2001 From: Roardom Date: Tue, 1 Aug 2023 10:05:10 +0000 Subject: [PATCH 60/62] fix: exclude anon torrents from leaderboards For privacy reasons. --- app/Http/Controllers/HomeController.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 04c367cd64..f34efb5e4b 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -431,6 +431,7 @@ function () use ($user) { 'poll' => cache()->remember('latest_poll', $expiresAt, fn () => Poll::latest()->first()), 'uploaders' => cache()->remember('top_uploaders', $expiresAt, fn () => Torrent::with(['user', 'user.group']) ->select(DB::raw('user_id, count(*) as value')) + ->where('anon', '=', false) ->groupBy('user_id') ->latest('value') ->take(10) @@ -438,6 +439,7 @@ function () use ($user) { 'past_uploaders' => cache()->remember('month_uploaders', $expiresAt, fn () => Torrent::with(['user', 'user.group']) ->where('created_at', '>', now()->subDays(30)->toDateTimeString()) ->select(DB::raw('user_id, count(*) as value')) + ->where('anon', '=', false) ->groupBy('user_id') ->latest('value') ->take(10) From 75d4610df4356dd2320ed1fb1841ae517287c213 Mon Sep 17 00:00:00 2001 From: Roardom Date: Tue, 1 Aug 2023 10:20:46 +0000 Subject: [PATCH 61/62] fix: cache home page posts/topics by group Otherwise it bypasses forum permission settings --- app/Http/Controllers/HomeController.php | 34 ++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 04c367cd64..b53006ed9e 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -403,15 +403,41 @@ function () use ($user) { } ), 'topics' => cache()->remember( - 'latest_topics', + 'latest_topics:by-group:'.auth()->user()->group_id, $expiresAt, - fn () => Topic::with('forum', 'user', 'latestPoster')->latest()->take(5)->get() + fn () => Topic::query() + ->with('user', 'user.group', 'latestPoster') + ->whereRelation('forumPermissions', [['show_forum', '=', 1], ['group_id', '=', auth()->user()->group_id]]) + ->latest() + ->take(5) + ->get() ), 'posts' => cache()->remember( - 'latest_posts', + 'latest_posts:by-group:'.auth()->user()->group_id, $expiresAt, - fn () => Post::with('topic', 'user') + fn () => Post::query() + ->with('user', 'user.group', 'topic:id,name') ->withCount('likes', 'dislikes', 'authorPosts', 'authorTopics') + ->withSum('tips', 'cost') + ->withExists([ + 'likes' => fn ($query) => $query->where('user_id', '=', auth()->id()), + 'dislikes' => fn ($query) => $query->where('user_id', '=', auth()->id()), + ]) + ->whereNotIn( + 'topic_id', + Topic::query() + ->whereRelation( + 'forumPermissions', + fn ($query) => $query + ->where('group_id', '=', auth()->user()->group_id) + ->where( + fn ($query) => $query + ->where('show_forum', '!=', 1) + ->orWhere('read_topic', '!=', 1) + ) + ) + ->select('id') + ) ->latest() ->take(5) ->get() From d6a0d647a640ac636cc1b56f4c13ada4aaed45f4 Mon Sep 17 00:00:00 2001 From: Roardom Date: Tue, 1 Aug 2023 10:50:00 +0000 Subject: [PATCH 62/62] update: users online block cache and appearance Make sure the cache caches based on the group since the privacy setting for showing up here can be based on the group. Also show the data in columns to look a little nicer. --- app/Http/Controllers/HomeController.php | 6 ++++-- resources/views/blocks/online.blade.php | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 04c367cd64..31b6e57ca7 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -59,7 +59,7 @@ public function index(Request $request): \Illuminate\Contracts\View\Factory|\Ill 'user' => $user, 'personal_freeleech' => cache()->get('personal_freeleech:'.$user->id), 'users' => cache()->remember( - 'online_users', + 'online_users:by-group:'.auth()->user()->group_id, $expiresAt, fn () => User::with('group', 'privacy') ->withCount([ @@ -67,8 +67,10 @@ public function index(Request $request): \Illuminate\Contracts\View\Factory|\Ill $query->whereNotNull('torrent')->where('active', '1'); }, ]) - ->where('last_action', '>', now()->subMinutes(5)) + ->where('last_action', '>', now()->subDays(19)) + ->orderByRaw('(select position from `groups` where `groups`.id = users.group_id), group_id, username') ->get() + ->sortBy(fn ($user) => $user->hidden || ! $user->isVisible($user, 'other', 'show_online')) ), 'groups' => cache()->remember( 'user-groups', diff --git a/resources/views/blocks/online.blade.php b/resources/views/blocks/online.blade.php index 993f6a2cae..66f253edbe 100644 --- a/resources/views/blocks/online.blade.php +++ b/resources/views/blocks/online.blade.php @@ -4,7 +4,7 @@ {{ __('blocks.users-online') }} ({{ $users->count() }})
        -
          +
            @foreach ($users as $user)
          • @@ -21,9 +21,9 @@ class="{{ config('other.font-awesome') }} fa-exclamation-circle text-orange" @endforeach

          -
            +
              @foreach ($groups as $group) - +