move into sonardesk

This commit is contained in:
Mark Gregory
2026-10-12 14:50:13 +01:00
parent 5f0d2ceb72
commit 38b4a7406f
879 changed files with 24021 additions and 1 deletions
+6
View File
@@ -0,0 +1,6 @@
<?php
return [
'name' => 'Media',
'enabled' => true,
];
@@ -0,0 +1,37 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('media', function (Blueprint $table): void {
$table->id();
$table->uuid('uuid')->unique();
$table->nullableMorphs('attachable');
$table->string('collection')->default('default')->index();
$table->string('disk')->default('public');
$table->string('path');
$table->string('original_name');
$table->string('mime_type')->nullable();
$table->unsignedBigInteger('size')->default(0);
$table->string('title')->nullable();
$table->string('alt_text')->nullable();
$table->text('caption')->nullable();
$table->json('metadata')->nullable();
$table->unsignedInteger('sort_order')->default(0);
$table->foreignId('uploaded_by')->nullable()->constrained('users')->nullOnDelete();
$table->timestamps();
$table->softDeletes();
$table->index(['attachable_type', 'attachable_id', 'collection', 'sort_order'], 'media_attachable_collection_index');
});
}
public function down(): void
{
Schema::dropIfExists('media');
}
};
@@ -0,0 +1,34 @@
<?php
namespace Modules\Media\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Modules\Media\Models\Media;
final class MediaController extends Controller
{
public function index()
{
return view('media::index', ['media' => Media::query()->latest()->paginate(30)]);
}
public function store(Request $request)
{
$request->validate(['file' => ['required', 'file', 'max:20480'], 'title' => ['nullable', 'string', 'max:255'], 'alt_text' => ['nullable', 'string', 'max:255']]);
$file = $request->file('file');
$path = $file->store('media', 'public');
Media::create(['disk' => 'public', 'path' => $path, 'original_name' => $file->getClientOriginalName(), 'mime_type' => $file->getMimeType(), 'size' => $file->getSize(), 'title' => $request->string('title')->toString() ?: pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME), 'alt_text' => $request->string('alt_text')->toString() ?: null, 'uploaded_by' => $request->user()->id]);
return back()->with('status', 'Media uploaded.');
}
public function destroy(Media $medium)
{
Storage::disk($medium->disk)->delete($medium->path);
$medium->delete();
return back()->with('status', 'Media deleted.');
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Modules\Media\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;
final class Media extends Model
{
use SoftDeletes;
protected $table = 'media';
protected $guarded = [];
protected static function booted(): void
{
self::creating(fn (self $m) => $m->uuid ??= (string) Str::uuid());
}
protected function casts(): array
{
return ['metadata' => 'array', 'size' => 'integer', 'sort_order' => 'integer'];
}
}
+21
View File
@@ -0,0 +1,21 @@
<?php
namespace Modules\Media\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Media\Support\ModuleInfo;
final class MediaServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->register(RouteServiceProvider::class);
$this->mergeConfigFrom(module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower());
}
public function boot(): void
{
$this->loadMigrationsFrom(module_path(ModuleInfo::name(), 'Database/Migrations'));
$this->loadViewsFrom(module_path(ModuleInfo::name(), 'Resources/views'), ModuleInfo::nameLower());
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Modules\Media\Providers;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;
final class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
parent::boot();
$this->routes(function (): void {
Route::middleware('web')->group(module_path('Media', 'Routes/web.php'));
});
}
}
+1
View File
@@ -0,0 +1 @@
<x-layouts.app :title="__('Media library')"><div class="space-y-6"><div><h1 class="text-2xl font-semibold">Media library</h1><p class="mt-1 text-sm text-zinc-500">Upload and manage reusable files.</p></div>@if(session('status'))<div class="rounded-xl bg-emerald-50 p-3 text-sm text-emerald-800">{{ session('status') }}</div>@endif<form method="POST" enctype="multipart/form-data" action="{{ route('administration.media.store') }}" class="grid gap-4 rounded-xl border border-zinc-200 bg-white p-5 sm:grid-cols-3 dark:border-zinc-700 dark:bg-zinc-900">@csrf<flux:input type="file" name="file" label="File" required/><flux:input name="title" label="Title"/><flux:input name="alt_text" label="Alternative text"/><div class="sm:col-span-3"><flux:button type="submit" variant="primary">Upload</flux:button></div></form><div class="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">@forelse($media as $item)<article class="overflow-hidden rounded-xl border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-900">@if(str_starts_with((string)$item->mime_type,'image/'))<img src="{{ Storage::disk($item->disk)->url($item->path) }}" alt="{{ $item->alt_text }}" class="h-40 w-full object-cover">@else<div class="flex h-40 items-center justify-center bg-zinc-100 text-zinc-500 dark:bg-zinc-800"><flux:icon.document class="size-10"/></div>@endif<div class="p-4"><div class="truncate font-medium">{{ $item->title ?: $item->original_name }}</div><div class="mt-1 text-xs text-zinc-500">{{ Number::fileSize($item->size) }}</div><form method="POST" action="{{ route('administration.media.destroy',$item) }}" class="mt-3">@csrf @method('DELETE')<flux:button size="sm" variant="danger" type="submit">Delete</flux:button></form></div></article>@empty<div class="col-span-full rounded-xl border border-dashed border-zinc-300 p-10 text-center text-sm text-zinc-500">No media uploaded.</div>@endforelse</div>{{ $media->links() }}</div></x-layouts.app>
+10
View File
@@ -0,0 +1,10 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Media\Http\Controllers\MediaController;
Route::middleware(['auth', 'verified'])->prefix('administration/media')->name('administration.media.')->group(function (): void {
Route::get('/', [MediaController::class, 'index'])->name('index');
Route::post('/', [MediaController::class, 'store'])->name('store');
Route::delete('/{medium}', [MediaController::class, 'destroy'])->name('destroy');
});
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace Modules\Media\Support;
final class ModuleInfo
{
public static function name(): string
{
return 'Media';
}
public static function nameLower(): string
{
return 'media';
}
}
+5
View File
@@ -0,0 +1,5 @@
<?php
it('boots the Media module', function (): void {
expect(config('media.name'))->toBe('Media');
});
+10
View File
@@ -0,0 +1,10 @@
{
"name": "absdev/media-module",
"description": "Media foundation module for the ABSDEV Laravel application platform.",
"type": "laravel-module",
"autoload": {
"psr-4": {
"Modules\\Media\\": ""
}
}
}
+20
View File
@@ -0,0 +1,20 @@
{
"name": "Media",
"alias": "media",
"version": "1.0.0",
"description": "Media foundation module for the ABSDEV Laravel application platform.",
"keywords": [
"laravel",
"modules",
"media"
],
"priority": 10,
"providers": [
"Modules\\Media\\Providers\\MediaServiceProvider"
],
"aliases": {},
"files": [],
"requires": [
"Core"
]
}