diff --git a/app/Livewire/Mod/ModerationActionButton.php b/app/Livewire/Mod/ModerationActionButton.php new file mode 100644 index 0000000..638f843 --- /dev/null +++ b/app/Livewire/Mod/ModerationActionButton.php @@ -0,0 +1,103 @@ + '$refresh']; + + public function mount(): void + { + $this->guid = uniqid('', true); + } + + public function render() + { + $this->allowActions = ! $this->isRunning; + + return view('livewire.mod.moderation-action-button'); + } + + public function runActionEvent(): void + { + $this->isRunning = true; + $this->dispatch("startAction.{$this->guid}"); + } + + #[On('startAction.{guid}')] + public function invokeAction(): void + { + if ($this->moderatedObjectId == null || $this->moderatedObjectId == '') { + Log::info('Failed: no ID specified.'); + + return; + } + + Log::info("Object ID: $this->moderatedObjectId"); + + if ($this->targetType !== 'mod' && $this->targetType !== 'modVersion') { + Log::info('Failed: invalid target type.'); + + return; + } + + switch ($this->targetType) { + case 'mod': + $moderatedObject = Mod::where('id', '=', $this->moderatedObjectId)->first(); + break; + + case 'modVersion': + $moderatedObject = ModVersion::where('id', '=', $this->moderatedObjectId)->first(); + break; + + default: + Log::info('Failed: invalid target type.'); + + return; + } + + if ($moderatedObject == null) { + Log::info('Failed: moderated object is null'); + + return; + } + + switch ($this->actionType) { + case 'delete': + + $moderatedObject->delete(); + break; + + case 'enable': + case 'disable': + + $moderatedObject->toggleDisabled(); + break; + + default: + Log::info('Failed: invalid action type.'); + + return; + } + + $this->js('window.location.reload()'); + } +} diff --git a/app/Livewire/Mod/ModerationOptions.php b/app/Livewire/Mod/ModerationOptions.php new file mode 100644 index 0000000..2a56948 --- /dev/null +++ b/app/Livewire/Mod/ModerationOptions.php @@ -0,0 +1,35 @@ +showDeleteDialog = true; + } + + public function confirmDisable(): void + { + $this->showDisableDialog = true; + } +} diff --git a/app/Models/Mod.php b/app/Models/Mod.php index 72e2c70..685e7d6 100644 --- a/app/Models/Mod.php +++ b/app/Models/Mod.php @@ -5,8 +5,8 @@ namespace App\Models; use App\Http\Filters\V1\QueryFilter; -use App\Models\Scopes\DisabledScope; use App\Models\Scopes\PublishedScope; +use App\Traits\CanModerate; use Database\Factories\ModFactory; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -54,6 +54,7 @@ */ class Mod extends Model { + use CanModerate; /** @use HasFactory */ use HasFactory; @@ -66,7 +67,6 @@ class Mod extends Model #[Override] protected static function booted(): void { - static::addGlobalScope(new DisabledScope); static::addGlobalScope(new PublishedScope); } diff --git a/app/Models/ModVersion.php b/app/Models/ModVersion.php index fde3b70..d3b94aa 100644 --- a/app/Models/ModVersion.php +++ b/app/Models/ModVersion.php @@ -5,9 +5,9 @@ namespace App\Models; use App\Exceptions\InvalidVersionNumberException; -use App\Models\Scopes\DisabledScope; use App\Models\Scopes\PublishedScope; use App\Support\Version; +use App\Traits\CanModerate; use Database\Factories\ModVersionFactory; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -50,6 +50,7 @@ */ class ModVersion extends Model { + use CanModerate; /** @use HasFactory */ use HasFactory; @@ -68,8 +69,6 @@ class ModVersion extends Model #[Override] protected static function booted(): void { - static::addGlobalScope(new DisabledScope); - static::addGlobalScope(new PublishedScope); static::saving(function (ModVersion $modVersion): void { diff --git a/app/Models/User.php b/app/Models/User.php index b2c194c..7a70644 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -192,6 +192,14 @@ public function isAdmin(): bool return Str::lower($this->role?->name) === 'administrator'; } + /** + * Conveniently check is a user is a mod or an admin + */ + public function isModOrAdmin(): bool + { + return $this->isMod() || $this->isAdmin(); + } + /** * Overwritten to instead use the queued version of the VerifyEmail notification. */ diff --git a/app/Policies/ModPolicy.php b/app/Policies/ModPolicy.php index d45b724..dcd9aee 100644 --- a/app/Policies/ModPolicy.php +++ b/app/Policies/ModPolicy.php @@ -22,7 +22,11 @@ public function viewAny(?User $user): bool */ public function view(?User $user, Mod $mod): bool { - // Everyone can view mods. + // Everyone can view mods, unless they are disabled. + if ($mod->disabled && ! $user?->isModOrAdmin()) { + return false; + } + return true; } @@ -39,7 +43,7 @@ public function create(User $user): bool */ public function update(User $user, Mod $mod): bool { - return false; + return $user->isMod() || $user->isAdmin() || $mod->users->contains($user); } /** @@ -47,7 +51,10 @@ public function update(User $user, Mod $mod): bool */ public function delete(User $user, Mod $mod): bool { - return false; + // I'm guessing we want the mod author to also be able to do this? + // what if there are multiple authors? + // I'm leaving that out for now -waffle.lazy + return $user->isAdmin(); } /** diff --git a/app/Traits/CanModerate.php b/app/Traits/CanModerate.php new file mode 100644 index 0000000..2be415b --- /dev/null +++ b/app/Traits/CanModerate.php @@ -0,0 +1,12 @@ +disabled = ! $this->disabled; + $this->save(); + } +} diff --git a/resources/css/app.css b/resources/css/app.css index 630605a..163b347 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -120,27 +120,34 @@ main a:not(.mod-list-component):not(.tab):not([role="menuitem"]) { } } -.ribbon { - font-size: 18px; - font-weight: bold; - color: #fff; +@layer components { + .ribbon { + font-size: 18px; + font-weight: bold; + @apply text-white bg-cyan-500 dark:bg-cyan-700; + } + + .ribbon { + --f: .5em; + position: absolute; + top: 0; + left: 0; + line-height: 1.5; + padding-inline: 1lh; + padding-bottom: var(--f); + border-image: conic-gradient(#0008 0 0) 51%/var(--f); + clip-path: polygon(100% calc(100% - var(--f)), 100% 100%, calc(100% - var(--f)) calc(100% - var(--f)), var(--f) calc(100% - var(--f)), 0 100%, 0 calc(100% - var(--f)), 999px calc(100% - var(--f) - 999px), calc(100% - 999px) calc(100% - var(--f) - 999px)); + transform: translate(calc((cos(45deg) - 1) * 100%), -100%) rotate(-45deg); + transform-origin: 100% 100%; + } } -.ribbon { - --f: .5em; - position: absolute; - top: 0; - left: 0; - line-height: 1.5; - padding-inline: 1lh; - padding-bottom: var(--f); - border-image: conic-gradient(#0008 0 0) 51%/var(--f); - clip-path: polygon(100% calc(100% - var(--f)), 100% 100%, calc(100% - var(--f)) calc(100% - var(--f)), var(--f) calc(100% - var(--f)), 0 100%, 0 calc(100% - var(--f)), 999px calc(100% - var(--f) - 999px), calc(100% - 999px) calc(100% - var(--f) - 999px)); - transform: translate(calc((cos(45deg) - 1) * 100%), -100%) rotate(-45deg); - transform-origin: 100% 100%; - background-color: #0e7490; +.ribbon-red { + @apply ribbon; + @apply text-white bg-red-500 dark:bg-red-700; } + .rainbow { height: 100%; width: 100%; diff --git a/resources/views/components/mod-card.blade.php b/resources/views/components/mod-card.blade.php index f340ecc..13e0be2 100644 --- a/resources/views/components/mod-card.blade.php +++ b/resources/views/components/mod-card.blade.php @@ -1,12 +1,15 @@ @props(['mod', 'version']) - @if ($mod->featured && !request()->routeIs('home')) + @if ($mod->featured && !$mod->disabled && !request()->routeIs('home'))
{{ __('Featured!') }}
@endif + @if ($mod->disabled) +
{{ __('Disabled') }}
+ @endif
-
+
@if ($mod->thumbnail) {{ $mod->name }} @else diff --git a/resources/views/livewire/mod/listing.blade.php b/resources/views/livewire/mod/listing.blade.php index 40acc4d..5d8de76 100644 --- a/resources/views/livewire/mod/listing.blade.php +++ b/resources/views/livewire/mod/listing.blade.php @@ -173,7 +173,9 @@ class="absolute top-7 right-0 z-10 flex w-full min-w-[12rem] flex-col divide-y d @if ($mods->isNotEmpty())
@foreach ($mods as $mod) - + @if(!$mod->disabled || (auth()->check() && auth()->user()->isModOrAdmin())) + + @endif @endforeach
@else diff --git a/resources/views/livewire/mod/moderation-action-button.blade.php b/resources/views/livewire/mod/moderation-action-button.blade.php new file mode 100644 index 0000000..3a63a49 --- /dev/null +++ b/resources/views/livewire/mod/moderation-action-button.blade.php @@ -0,0 +1,18 @@ +
+ @if($this->allowActions) + + {{ __('Cancel') }} + + + {{ __(ucfirst($this->actionType)) }} + + @endif + @if($this->isRunning) +
+ + + + +
+ @endif +
diff --git a/resources/views/livewire/mod/moderation-options.blade.php b/resources/views/livewire/mod/moderation-options.blade.php new file mode 100644 index 0000000..f65c083 --- /dev/null +++ b/resources/views/livewire/mod/moderation-options.blade.php @@ -0,0 +1,79 @@ +
+ + + + + + +
+ +
+ @if(auth()->user()->isAdmin()) +
+ +
+ @endif +
+ +
+ + @push('modals') + + +

{{__('Confirm')}} {{__($this->disabled ? 'Enable' : 'Disable')}}

+
+ +

Are you sure you want to {{__($this->disabled ? 'enable' : 'disable')}} '{{$this->displayName}}'?

+
+ + + +
+ @endpush + + @push('modals') + + +

{{ __('Confirm') }} {{ __('Delete') }}

+
+ +

Are you sure you want to {{__('delete')}} '{{$this->displayName}}'?

+
+ + + +
+ @endpush +
diff --git a/resources/views/mod/show.blade.php b/resources/views/mod/show.blade.php index 0c40953..76b57b5 100644 --- a/resources/views/mod/show.blade.php +++ b/resources/views/mod/show.blade.php @@ -11,9 +11,17 @@ {{-- Main Mod Details Card --}}