move into sonardesk
This commit is contained in:
@@ -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.');
|
||||
}
|
||||
}
|
||||
@@ -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'];
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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');
|
||||
});
|
||||
@@ -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';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
it('boots the Media module', function (): void {
|
||||
expect(config('media.name'))->toBe('Media');
|
||||
});
|
||||
@@ -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\\": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user