Final changes

This commit is contained in:
2026-06-30 14:06:11 +02:00
parent 176a883633
commit 22893fd957
29 changed files with 316 additions and 60 deletions

View File

@@ -19,7 +19,7 @@ class AuthorController extends Controller
{
$items = Author::withCount('entries')
->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['name']))
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)
->withQueryString();

View File

@@ -18,7 +18,7 @@ class GameController extends Controller
public function index()
{
$items = Game::withCount('entries')->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['name']))
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)->withQueryString();
$platforms = Platform::orderBy('name')->get();
$genres = Genre::orderBy('name')->get();

View File

@@ -18,7 +18,7 @@ class GenreController extends Controller
{
$items = Genre::withCount('games')
->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['name']))
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)
->withQueryString();

View File

@@ -17,7 +17,7 @@ class LanguageController extends Controller
{
$items = Language::withCount('entries')
->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['name']))
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)
->withQueryString();

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers\ModCP;
use App\Helpers\EntryHelpers;
use App\Http\Controllers\Controller;
use App\Models\Level;
use App\Traits\ModCPSearch;
use Illuminate\Http\Request;
class LevelController extends Controller
{
use ModCPSearch;
public function index()
{
$items = Level::withCount('entries')
->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)
->withQueryString();
return view('modcp.resources', [
'items' => $items,
'title' => 'Levels',
'singular' => 'Level',
'storeRoute' => 'modcp.levels.store',
'updateRoute' => 'modcp.levels.update',
'destroyRoute' => 'modcp.levels.destroy'
]);
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255|unique:levels,name',
]);
Level::create([
'name' => trim($request->name),
'slug' => EntryHelpers::uniqueSlug( $request->name, Level::class ),
]);
return back()->with('success', 'Level added.');
}
public function update(Request $request, Level $level)
{
$request->validate([
'name' => 'required|string|max:255|unique:levels,name,' . $level->id,
]);
$level->update([
'name' => trim($request->name),
'slug' => EntryHelpers::uniqueSlug( $request->name, Level::class, $level->id ),
]);
return back()->with('success', 'Level updated.');
}
public function destroy(Level $level)
{
$level->delete();
return back()->with('success', 'Level deleted.');
}
}

View File

@@ -0,0 +1,67 @@
<?php
namespace App\Http\Controllers\ModCP;
use App\Helpers\EntryHelpers;
use App\Http\Controllers\Controller;
use App\Models\Modification;
use App\Traits\ModCPSearch;
use Illuminate\Http\Request;
class ModificationsController extends Controller
{
use ModCPSearch;
public function index()
{
$items = Modification::withCount('entries')
->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)
->withQueryString();
return view('modcp.resources', [
'items' => $items,
'title' => 'Modifications',
'singular' => 'Modification',
'storeRoute' => 'modcp.modifications.store',
'updateRoute' => 'modcp.modifications.update',
'destroyRoute' => 'modcp.modifications.destroy'
]);
}
public function store(Request $request)
{
$request->validate([
'name' => 'required|string|max:255|unique:modifications,name',
]);
Modification::create([
'name' => trim($request->name),
'slug' => EntryHelpers::uniqueSlug( $request->name, Modification::class ),
]);
return back()->with('success', 'Modification added.');
}
public function update(Request $request, Modification $Modification)
{
$request->validate([
'name' => 'required|string|max:255|unique:modifications,name,' . $Modification->id,
]);
$Modification->update([
'name' => trim($request->name),
'slug' => EntryHelpers::uniqueSlug( $request->name, Modification::class, $Modification->id ),
]);
return back()->with('success', 'Modification updated.');
}
public function destroy(Modification $Modification)
{
$Modification->delete();
return back()->with('success', 'Modification deleted.');
}
}

View File

@@ -18,7 +18,7 @@ class PlatformController extends Controller
{
$items = Platform::withCount(['games','entries'])
->orderBy('name')
->tap(fn($query) => $this->applySearch($query, ['name']))
->tap(fn($query) => $this->applySearch($query, ['id','name']))
->paginate(30)
->withQueryString();

View File

@@ -7,6 +7,8 @@ use App\Services\XenforoService;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Activitylog\Models\Concerns\LogsActivity;
use Spatie\Activitylog\Support\LogOptions;
/**
* @property int $id
@@ -32,6 +34,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
*/
class Author extends Model
{
use LogsActivity;
protected $fillable = [
'name', 'slug', 'user_id', 'website'
];
@@ -49,4 +54,14 @@ class Author extends Model
return app(XenforoService::class)->getXfUser($this->user_id);
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->useLogName('author')
->logAll()
->logOnlyDirty()
->dontLogEmptyChanges()
->setDescriptionForEvent(fn(string $eventName) => "Author {$eventName}");
}
}

View File

@@ -8,6 +8,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use Spatie\Activitylog\Models\Concerns\LogsActivity;
use Spatie\Activitylog\Support\LogOptions;
/**
* @property int $id
@@ -41,7 +43,7 @@ use League\CommonMark\GithubFlavoredMarkdownConverter;
class EntryReview extends Model
{
use HasXenforoUserId, SoftDeletes;
use HasXenforoUserId, SoftDeletes, LogsActivity;
protected $fillable = [ 'entry_id', 'title', 'rating', 'description', 'user_id' ];
@@ -60,4 +62,14 @@ class EntryReview extends Model
return $converter->convert($this->description)->getContent();
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->useLogName('review')
->logAll()
->logOnlyDirty()
->dontLogEmptyChanges()
->setDescriptionForEvent(fn(string $eventName) => "Review {$eventName}");
}
}

View File

@@ -4,6 +4,8 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\Activitylog\Models\Concerns\LogsActivity;
use Spatie\Activitylog\Support\LogOptions;
/**
* @property int $id
@@ -31,6 +33,9 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
*/
class Game extends Model
{
use LogsActivity;
/**
* @var string[]
*/
@@ -50,4 +55,14 @@ class Game extends Model
{
return $this->hasMany(Entry::class);
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->useLogName('game')
->logAll()
->logOnlyDirty()
->dontLogEmptyChanges()
->setDescriptionForEvent(fn(string $eventName) => "Game {$eventName}");
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
@@ -23,4 +24,9 @@ use Illuminate\Database\Eloquent\Model;
class Level extends Model
{
protected $fillable = ['name', 'slug'];
public function entries(): HasMany
{
return $this->hasMany(Entry::class);
}
}

View File

@@ -3,6 +3,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
* @property int $id
@@ -23,4 +25,9 @@ use Illuminate\Database\Eloquent\Model;
class Modification extends Model
{
protected $fillable = [ 'name', 'slug' ];
public function entries(): BelongsToMany
{
return $this->belongsToMany(Entry::class, 'entry_modifications');
}
}

View File

@@ -10,6 +10,8 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use League\CommonMark\GithubFlavoredMarkdownConverter;
use Spatie\Activitylog\Models\Concerns\LogsActivity;
use Spatie\Activitylog\Support\LogOptions;
/**
* @property int $id
@@ -61,7 +63,7 @@ use League\CommonMark\GithubFlavoredMarkdownConverter;
class News extends Model
{
use SoftDeletes, HasGallery, HasXenforoUserId;
use SoftDeletes, HasGallery, HasXenforoUserId, LogsActivity;
protected $table = 'news';
@@ -127,4 +129,14 @@ class News extends Model
return EntryHelpers::getYoutubeVideoId( $this->youtube_link );
}
public function getActivitylogOptions(): LogOptions
{
return LogOptions::defaults()
->useLogName('news')
->logAll()
->logOnlyDirty()
->dontLogEmptyChanges()
->setDescriptionForEvent(fn(string $eventName) => "News {$eventName}");
}
}

View File

@@ -159,7 +159,7 @@ class EntryPolicy
return $user->_can('romhackplaza', 'canModerateEntries' );
}
public function moderate(User $user, Entry $entry): bool
public function moderate(User $user, ?Entry $entry = null): bool
{
return $user->_can('romhackplaza', 'canModerateEntries' );
}

View File

@@ -199,6 +199,9 @@ class ActivityService
'post.user_id', 'post.message'
])
->get()
->reject(function($post){
return (int) $post->user_id === config('xenforo.bot_user_id');
})
->map($this->formatMessage(...))
->toArray();
});

View File

@@ -61,6 +61,10 @@ class SubmitEntryStatus extends Component
}
}
if( $this->isEdit && $this->entry->state === 'pending' && isset( $states['published'] ) ) {
unset( $states['published'] );
}
return $states;
}

View File

@@ -9,7 +9,7 @@
Click or drag'n drop files here.
</div>
<div style="font-size:0.85rem;color:var(--text2);">
Accepted: PNG, JPG or WebP
Accepted: PNG, JPG, GIF or WebP
</div>
</div>
</div>

View File

@@ -10,7 +10,7 @@
Click or drag'n drop files here.
</div>
<div style="font-size:0.85rem;color:var(--text2);">
Accepted: PNG, JPG or WebP
Accepted: PNG, JPG, GIF or WebP
</div>
</div>
<input type="hidden" name="main-image" x-model="serverFilePath">

View File

@@ -27,10 +27,12 @@
<div class="modcp-nav-group">
<span class="modcp-nav-label">Content</span>
<a href="{{ route('modcp.locked') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.locked') ? 'active' : '' }}>
<i data-lucide="lock" size="15"></i>
Locked entries
</a>
@can('moderate','\App\Models\Entry')
<a href="{{ route('modcp.locked') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.locked') ? 'active' : '' }}>
<i data-lucide="lock" size="15"></i>
Locked entries
</a>
@endcan
@can('is-admin')
<a href="{{ route('modcp.draft') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.draft') ? 'active' : '' }}>
<i data-lucide="scissors" size="15"></i>
@@ -74,6 +76,14 @@
<i data-lucide="box" size="15"></i>
Genres
</a>
<a href="{{ route('modcp.levels.index') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.levels.*') ? 'active' : '' }}">
<i data-lucide="weight" size="15"></i>
Levels
</a>
<a href="{{ route('modcp.modifications.index') }}" class="modcp-nav-item" {{ request()->routeIs('modcp.modifications.*') ? 'active' : '' }}">
<i data-lucide="pencil-ruler" size="15"></i>
Modifications
</a>
@endcan
</div>

View File

@@ -203,7 +203,7 @@
</div>
<div class="log-pagination">
{{ $logs->links() }}
{{ $logs->links('modcp.pagination') }}
</div>
@endif

View File

@@ -84,6 +84,6 @@
@endforelse
</div>
{{ $items->links() }}
{{ $items->links('modcp.pagination') }}
@endsection

View File

@@ -55,7 +55,7 @@
</div>
@endforeach
</div>
{{ $entries->links() }}
{{ $entries->links('modcp.pagination') }}
@endif
@endsection

View File

@@ -47,6 +47,6 @@
</div>
@endforeach
</div>
{{ $entries->links() }}
{{ $entries->links( 'modcp.pagination' ) }}
@endif
@endsection

View File

@@ -98,6 +98,6 @@
@endforelse
</div>
{{ $items->links() }}
{{ $items->links('modcp.pagination') }}
@endsection

View File

@@ -14,13 +14,15 @@
<span class="modcp-stat-label">In queue</span>
</div>
</a>
<a href="{{ route('modcp.locked') }}" class="modcp-stat-card">
<div class="modcp-stat-icon"><i data-lucide="lock" size="22"></i></div>
<div class="modcp-stat-info">
<span class="modcp-stat-value">{{ $stats['locked'] }}</span>
<span class="modcp-stat-label">Locked</span>
</div>
</a>
@can('moderate','\App\Models\Entry')
<a href="{{ route('modcp.locked') }}" class="modcp-stat-card">
<div class="modcp-stat-icon"><i data-lucide="lock" size="22"></i></div>
<div class="modcp-stat-info">
<span class="modcp-stat-value">{{ $stats['locked'] }}</span>
<span class="modcp-stat-label">Locked</span>
</div>
</a>
@endcan
@can('is-admin')
<a href="{{ route('modcp.draft') }}" class="modcp-stat-card">
<div class="modcp-stat-icon"><i data-lucide="scissors" size="22"></i></div>
@@ -54,37 +56,39 @@
</div>
@if($recentDeleted->isNotEmpty())
<div class="modcp-section-title" style="margin-top: 25px;">Recently deleted</div>
<div class="modcp-list">
@foreach($recentDeleted as $entry)
<div class="modcp-list-item">
<div class="modcp-list-item-info">
<span class="modcp-list-item-title">{{ $entry->complete_title ?? $entry->title }}</span>
<span class="modcp-list-item-meta">
<span class="badge {{ $entry->type }}">{{ $entry->type }}</span>
Deleted {{ $entry->deleted_at->diffForHumans() }}
</span>
@can('is-admin')
<div class="modcp-section-title" style="margin-top: 25px;">Recently deleted</div>
<div class="modcp-list">
@foreach($recentDeleted as $entry)
<div class="modcp-list-item">
<div class="modcp-list-item-info">
<span class="modcp-list-item-title">{{ $entry->complete_title ?? $entry->title }}</span>
<span class="modcp-list-item-meta">
<span class="badge {{ $entry->type }}">{{ $entry->type }}</span>
Deleted {{ $entry->deleted_at->diffForHumans() }}
</span>
</div>
<div class="modcp-list-item-actions">
<form action="{{ route('modcp.restore', $entry->id) }}" method="POST" style="display:inline">
@csrf @method('PATCH')
<button type="submit" class="btn success">
<i data-lucide="rotate-ccw" size="13"></i> Restore
</button>
</form>
<form action="{{ route('modcp.destroy', $entry->id) }}" method="POST" style="display:inline"
onsubmit="return confirm('Permanently delete?')">
@csrf @method('DELETE')
<button type="submit" class="btn danger">
<i data-lucide="trash-2" size="13"></i> Purge
</button>
</form>
</div>
</div>
<div class="modcp-list-item-actions">
<form action="{{ route('modcp.restore', $entry->id) }}" method="POST" style="display:inline">
@csrf @method('PATCH')
<button type="submit" class="btn success">
<i data-lucide="rotate-ccw" size="13"></i> Restore
</button>
</form>
<form action="{{ route('modcp.destroy', $entry->id) }}" method="POST" style="display:inline"
onsubmit="return confirm('Permanently delete?')">
@csrf @method('DELETE')
<button type="submit" class="btn danger">
<i data-lucide="trash-2" size="13"></i> Purge
</button>
</form>
</div>
</div>
@endforeach
<a href="{{ route('modcp.deleted') }}" class="modcp-list-see-all">
See all deleted entries
</a>
@endforeach
<a href="{{ route('modcp.deleted') }}" class="modcp-list-see-all">
See all deleted entries
</a>
@endcan
</div>
@endif

View File

@@ -0,0 +1,34 @@
<?php /** @var \Illuminate\Pagination\LengthAwarePaginator $paginator */ ?>
@if ($paginator->hasPages())
<div class="database-pagination">
@if ($paginator->onFirstPage())
<button class="btn" disabled>«</button>
@else
<a class="btn" href="{{ $paginator->previousPageUrl() }}">«</a>
@endif
{{-- Pages --}}
@foreach ($elements as $element)
@if (is_string($element))
<button class="btn" disabled>{{ $element }}</button>
@endif
@if (is_array($element))
@foreach ($element as $page => $url)
<a
class="btn {{ $page == $paginator->currentPage() ? 'active' : '' }}"
href="{{ $url }}"
>{{ $page }}</a>
@endforeach
@endif
@endforeach
@if ($paginator->hasMorePages())
<a class="btn" href="{{ $paginator->nextPageUrl() }}">»</a>
@else
<button class="btn" disabled>»</button>
@endif
</div>
@endif

View File

@@ -71,6 +71,6 @@
@endforelse
</div>
{{ $items->links() }}
{{ $items->links('modcp.pagination') }}
@endsection

View File

@@ -1,7 +1,6 @@
@if ($paginator->hasPages())
<div class="database-pagination">
{{-- Précédent --}}
@if ($paginator->onFirstPage())
<button class="btn" disabled>«</button>
@else
@@ -24,7 +23,6 @@
@endif
@endforeach
{{-- Suivant --}}
@if ($paginator->hasMorePages())
<button class="btn" wire:click="nextPage">»</button>
@else

View File

@@ -111,7 +111,7 @@ Route::name('tools.')->controller(\App\Http\Controllers\ToolsController::class)-
Route::name('modcp.')->prefix('/modcp')->controller(\App\Http\Controllers\ModCPController::class)->middleware(['xf.auth','can:is-mod'])->group(function () {
Route::get('/', 'index' )->name('index');
Route::get('/locked-entries', 'locked' )->name('locked');
Route::get('/locked-entries', 'locked' )->name('locked')->middleware('can:moderate,\App\Models\Entry');
Route::get('/draft-entries', 'draft' )->name('draft')->middleware('can:is-admin');
Route::get('/hidden-entries', 'hidden' )->name('hidden')->middleware('can:is-admin');
Route::get('/deleted-entries', 'deleted' )->name('deleted')->middleware('can:is-admin');
@@ -125,6 +125,8 @@ Route::name('modcp.')->prefix('/modcp')->controller(\App\Http\Controllers\ModCPC
Route::resource('authors', \App\Http\Controllers\ModCP\AuthorController::class)->only(['index', 'store','update','destroy']);
Route::resource('platforms', \App\Http\Controllers\ModCP\PlatformController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
Route::resource('genres', \App\Http\Controllers\ModCP\GenreController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
Route::resource('levels', \App\Http\Controllers\ModCP\LevelController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
Route::resource('modifications', \App\Http\Controllers\ModCP\ModificationsController::class )->middleware('can:is-admin')->only(['index', 'store','update','destroy']);
});
// RedirectController