diff --git a/.gitignore b/.gitignore index 3a324ad..c7cf1fa 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,3 @@ yarn-error.log /.nova /.vscode /.zed -.vite diff --git a/CHANGELOG.md b/CHANGELOG.md index 76dc870..d22dff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,4 @@ added: laravel 12 -added: AGPLv3 - -## 2026-01-02 - -added initial model and filament resource - +added: AGPLv3 \ No newline at end of file diff --git a/LICENSE b/LICENSE index aaddaf1..8c8c48f 100644 --- a/LICENSE +++ b/LICENSE @@ -219,7 +219,7 @@ If you develop a new program, and you want it to be of the greatest possible use To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. - share-lt + test Copyright (C) 2026 jon This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. diff --git a/app/Console/Commands/MoveMediaToPublic.php b/app/Console/Commands/MoveMediaToPublic.php deleted file mode 100644 index bce2727..0000000 --- a/app/Console/Commands/MoveMediaToPublic.php +++ /dev/null @@ -1,112 +0,0 @@ -option('dry-run'); - - if ($dryRun) { - $this->warn('DRY RUN MODE - No files will actually be moved'); - } - - // Get all media records using local disk - $mediaRecords = Media::where('disk', 'local')->get(); - - if ($mediaRecords->isEmpty()) { - $this->info('No media records found using local disk.'); - return self::SUCCESS; - } - - $this->info("Found {$mediaRecords->count()} media records to migrate."); - - $progressBar = $this->output->createProgressBar($mediaRecords->count()); - $progressBar->start(); - - $moved = 0; - $errors = 0; - - foreach ($mediaRecords as $media) { - // Use relative path: {id}/{filename} - $relativePath = $media->id . '/' . $media->file_name; - - // Check if source file exists - if (!Storage::disk('local')->exists($relativePath)) { - $this->newLine(); - $this->error("Source file not found: {$relativePath}"); - $errors++; - $progressBar->advance(); - continue; - } - - try { - if (!$dryRun) { - // Copy file from local to public disk - $fileContent = Storage::disk('local')->get($relativePath); - Storage::disk('public')->put($relativePath, $fileContent); - - // Verify the file was copied successfully - if (Storage::disk('public')->exists($relativePath)) { - // Update the database record - $media->update([ - 'disk' => 'public', - 'conversions_disk' => 'public', - ]); - - // Delete the old file from local disk - Storage::disk('local')->delete($relativePath); - - $moved++; - } else { - throw new \Exception("Failed to copy file to public disk"); - } - } else { - $this->newLine(); - $this->line("Would move: local:{$relativePath} -> public:{$relativePath}"); - $moved++; - } - } catch (\Exception $e) { - $this->newLine(); - $this->error("Error moving {$relativePath}: {$e->getMessage()}"); - $errors++; - } - - $progressBar->advance(); - } - - $progressBar->finish(); - $this->newLine(2); - - if ($dryRun) { - $this->info("DRY RUN: Would move {$moved} files, {$errors} errors encountered."); - $this->info("Run without --dry-run to actually perform the migration."); - } else { - $this->info("Successfully moved {$moved} files, {$errors} errors encountered."); - } - - return self::SUCCESS; - } -} diff --git a/app/Filament/Resources/Entries/EntryResource.php b/app/Filament/Resources/Entries/EntryResource.php deleted file mode 100644 index 9b15701..0000000 --- a/app/Filament/Resources/Entries/EntryResource.php +++ /dev/null @@ -1,58 +0,0 @@ - ListEntries::route('/'), - 'create' => CreateEntry::route('/create'), - 'view' => ViewEntry::route('/{record}'), - 'edit' => EditEntry::route('/{record}/edit'), - ]; - } -} diff --git a/app/Filament/Resources/Entries/Pages/CreateEntry.php b/app/Filament/Resources/Entries/Pages/CreateEntry.php deleted file mode 100644 index 222b29b..0000000 --- a/app/Filament/Resources/Entries/Pages/CreateEntry.php +++ /dev/null @@ -1,11 +0,0 @@ -components([ - TextInput::make('title') - ->required() - ->live(onBlur: true) - ->afterStateUpdated(function ($state, $set): void { - $set('slug', Str::slug((string) $state)); - }), - TextInput::make('slug') - ->required() - ->dehydrated() - ->readOnly(), - Textarea::make('description') - ->columnSpanFull(), - SpatieMediaLibraryFileUpload::make('featured_image') - ->collection('featured-image') - ->image() - ->imageEditor() - ->disk('public') - ->visibility('public') - ->columnSpanFull() - ->dehydrated(false) - ->hintAction( - Action::make('featured_picker') - ->label('Pick from Gallery') - ->icon('heroicon-m-photo') - ->schema([ - Select::make('image_id') - ->label('Select an existing image') - ->allowHtml() - ->options(function () { - return Media::where('model_type', 'temp') - ->where('model_id', 0) - ->where('disk', 'public') - ->latest() - ->limit(30) - ->get(['id', 'file_name', 'name', 'disk']) - ->mapWithKeys(function (Media $item) { - try { - $url = $item->getUrl(); - $fileName = e($item->file_name); - $name = e($item->name ?? ''); - - $html = "
" . - "{$fileName}" . - "
" . - "{$name}" . - "{$fileName}" . - "
"; - - return [$item->id => $html]; - } catch (\Exception $e) { - return []; - } - })->toArray(); - }) - ->searchable() - ->preload() - ->required(), - ]) - ->action(function (array $data, SpatieMediaLibraryFileUpload $component): void { - $record = $component->getRecord(); - - if (!$record) { - \Filament\Notifications\Notification::make() - ->warning() - ->title('Save the entry first') - ->send(); - return; - } - - if (!$data['image_id']) { - return; - } - - $sourceMedia = Media::find($data['image_id']); - if (!$sourceMedia || !file_exists($sourceMedia->getPath())) { - \Filament\Notifications\Notification::make() - ->danger() - ->title('Image file not found') - ->send(); - return; - } - - $sourceFile = $sourceMedia->getPath(); - $tempCopy = sys_get_temp_dir() . '/' . uniqid() . '_' . $sourceMedia->file_name; - copy($sourceFile, $tempCopy); - - try { - // Verify record has ID - if (!$record->id) { - \Filament\Notifications\Notification::make() - ->danger() - ->title('Entry must be saved first') - ->send(); - return; - } - - // Add the copy to the entry's featured-image collection - $newMedia = $record->addMedia($tempCopy) - ->usingName($sourceMedia->name ?: pathinfo($sourceMedia->file_name, PATHINFO_FILENAME)) - ->usingFileName($sourceMedia->file_name) - ->toMediaCollection('featured-image', 'public'); - - // Dispatch event for app.js to handle - $component->getLivewire()->dispatch('featured-image-added', ['mediaId' => $newMedia->id]); - - \Filament\Notifications\Notification::make() - ->success() - ->title('Image added to featured image') - ->send(); - } catch (\Exception $e) { - \Filament\Notifications\Notification::make() - ->danger() - ->title('Error: ' . $e->getMessage()) - ->send(); - } finally { - if (file_exists($tempCopy)) { - unlink($tempCopy); - } - } - }) - ), - Toggle::make('is_published') - ->required(), - Toggle::make('is_featured') - ->required(), - DatePicker::make('published_at'), - RichEditor::make('content') - ->columnSpanFull() - ->hintAction( - Action::make('picker') - ->label('Gallery Picker') - ->icon('heroicon-m-photo') - ->schema([ - Select::make('image_url') - ->label('Select an existing image') - ->allowHtml() - ->options(function () { - return Media::latest() - ->limit(30) // Limit to 30 most recent items for performance - ->get(['id', 'file_name', 'name', 'uuid', 'collection_name', 'model_type', 'model_id', 'disk']) - ->filter(function (Media $item) { - // Only include media items that have a valid disk - return $item->disk !== null; - }) - ->mapWithKeys(function (Media $item) { - try { - $url = $item->getUrl(); - } catch (\Exception $e) { - // Skip items that can't generate URLs - return []; - } - - $fileName = e($item->file_name); - $name = e($item->name ?? ''); - - // Smaller image preview for better performance - $html = "
" . - "{$fileName}" . - "
" . - "{$name}" . - "{$fileName}" . - "
"; - - return [$url => $html]; - })->toArray(); - }) - ->searchable() - ->preload() - ->required(), - ]) - ->action(function (array $data, RichEditor $component) { - // We dispatch the URL to the browser to be inserted into TipTap - $component->getLivewire()->dispatch('insert-editor-content', [ - 'statePath' => $component->getStatePath(), - 'html' => "", - ]); - }) - ), - - ]); - } -} diff --git a/app/Filament/Resources/Entries/Schemas/EntryInfolist.php b/app/Filament/Resources/Entries/Schemas/EntryInfolist.php deleted file mode 100644 index be741bc..0000000 --- a/app/Filament/Resources/Entries/Schemas/EntryInfolist.php +++ /dev/null @@ -1,42 +0,0 @@ -components([ - TextEntry::make('title'), - TextEntry::make('slug'), - TextEntry::make('description') - ->placeholder('-') - ->columnSpanFull(), - SpatieMediaLibraryImageEntry::make('featured_image') - ->collection('featured-image') - ->columnSpanFull(), - IconEntry::make('is_published') - ->boolean(), - IconEntry::make('is_featured') - ->boolean(), - TextEntry::make('published_at') - ->date() - ->placeholder('-'), - TextEntry::make('content') - ->placeholder('-') - ->columnSpanFull(), - TextEntry::make('created_at') - ->dateTime() - ->placeholder('-'), - TextEntry::make('updated_at') - ->dateTime() - ->placeholder('-'), - ]); - } -} diff --git a/app/Filament/Resources/Entries/Tables/EntriesTable.php b/app/Filament/Resources/Entries/Tables/EntriesTable.php deleted file mode 100644 index eb89fd4..0000000 --- a/app/Filament/Resources/Entries/Tables/EntriesTable.php +++ /dev/null @@ -1,58 +0,0 @@ -columns([ - SpatieMediaLibraryImageColumn::make('featured_image') - ->collection('featured-image') - ->circular() - ->stacked() - ->limit(3), - TextColumn::make('title') - ->searchable(), - TextColumn::make('slug') - ->searchable(), - IconColumn::make('is_published') - ->boolean(), - IconColumn::make('is_featured') - ->boolean(), - TextColumn::make('published_at') - ->date() - ->sortable(), - TextColumn::make('created_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - TextColumn::make('updated_at') - ->dateTime() - ->sortable() - ->toggleable(isToggledHiddenByDefault: true), - ]) - ->filters([ - // - ]) - ->recordActions([ - ViewAction::make(), - EditAction::make(), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make(), - ]), - ]); - } -} diff --git a/app/Filament/Resources/Media/MediaResource.php b/app/Filament/Resources/Media/MediaResource.php deleted file mode 100644 index 2ac19fe..0000000 --- a/app/Filament/Resources/Media/MediaResource.php +++ /dev/null @@ -1,58 +0,0 @@ - ListMedia::route('/'), - 'create' => CreateMedia::route('/create'), - 'view' => ViewMedia::route('/{record}'), - 'edit' => EditMedia::route('/{record}/edit'), - ]; - } -} diff --git a/app/Filament/Resources/Media/Pages/CreateMedia.php b/app/Filament/Resources/Media/Pages/CreateMedia.php deleted file mode 100644 index c652a96..0000000 --- a/app/Filament/Resources/Media/Pages/CreateMedia.php +++ /dev/null @@ -1,61 +0,0 @@ -uploadedFile = $file; - - // Set required fields for Media model - $data['model_type'] = $data['model_type'] ?? 'temp'; - $data['model_id'] = $data['model_id'] ?? 0; - $data['collection_name'] = $data['collection_name'] ?? 'default'; - $data['disk'] = $data['disk'] ?? 'public'; - $data['file_name'] = $file ? basename($file) : ''; - $data['mime_type'] = $file && Storage::disk('public')->exists($file) - ? Storage::disk('public')->mimeType($file) - : 'application/octet-stream'; - $data['size'] = $file && Storage::disk('public')->exists($file) - ? Storage::disk('public')->size($file) - : 0; - $data['manipulations'] = []; - $data['custom_properties'] = []; - $data['generated_conversions'] = []; - $data['responsive_images'] = []; - - return $data; - } - - protected function afterCreate(): void - { - if ($this->uploadedFile && $this->record) { - $disk = Storage::disk('public'); - - // Create the directory for this media ID (Spatie structure: {id}/{filename}) - $mediaDirectory = (string) $this->record->id; - $disk->makeDirectory($mediaDirectory); - - // Move file from temporary upload location to Spatie's expected location - if ($disk->exists($this->uploadedFile)) { - $newPath = $mediaDirectory.'/'.$this->record->file_name; - $disk->move($this->uploadedFile, $newPath); - } - } - } -} diff --git a/app/Filament/Resources/Media/Pages/EditMedia.php b/app/Filament/Resources/Media/Pages/EditMedia.php deleted file mode 100644 index b6f4cdb..0000000 --- a/app/Filament/Resources/Media/Pages/EditMedia.php +++ /dev/null @@ -1,72 +0,0 @@ -record->getPathRelativeToRoot()) { - $this->uploadedFile = $file; - - // Keep the original file_name to prevent breaking existing references - // $data['file_name'] is not updated - we preserve the original filename - $data['mime_type'] = Storage::disk('public')->exists($file) - ? Storage::disk('public')->mimeType($file) - : 'application/octet-stream'; - $data['size'] = Storage::disk('public')->exists($file) - ? Storage::disk('public')->size($file) - : 0; - } - - return $data; - } - - protected function afterSave(): void - { - if ($this->uploadedFile && $this->record) { - $disk = Storage::disk('public'); - $mediaDirectory = (string) $this->record->id; - - // Delete old file if it exists - $oldPath = $mediaDirectory.'/'.$this->record->getOriginal('file_name'); - if ($disk->exists($oldPath)) { - $disk->delete($oldPath); - } - - // Move new file to Spatie's expected location using the original filename - if ($disk->exists($this->uploadedFile)) { - $disk->makeDirectory($mediaDirectory); - // Use the original file_name to preserve existing references - $newPath = $mediaDirectory.'/'.$this->record->file_name; - $disk->move($this->uploadedFile, $newPath); - } - - // Redirect to the same page to refresh the form state - $this->redirect(static::getUrl(['record' => $this->record]), navigate: true); - } - } -} diff --git a/app/Filament/Resources/Media/Pages/ListMedia.php b/app/Filament/Resources/Media/Pages/ListMedia.php deleted file mode 100644 index 2e6b63d..0000000 --- a/app/Filament/Resources/Media/Pages/ListMedia.php +++ /dev/null @@ -1,19 +0,0 @@ -components([ - TextInput::make('name') - ->required() - ->maxLength(255), - TextInput::make('collection_name') - ->default('default') - ->required() - ->maxLength(255), - Hidden::make('disk') - ->default('public'), - FileUpload::make('file') - ->label('File') - ->imageEditor() - ->imageEditorAspectRatios([ - '16:9', - '4:3', - '1:1', - ]) - ->columnSpanFull() - ->disk('public') - ->directory('media') - ->visibility('public') - ->acceptedFileTypes(['image/*', 'application/pdf']) - ->maxSize(10240) - ->required(fn ($context) => $context === 'create') - ->afterStateHydrated(function (FileUpload $component, $state, $record): void { - if (! $record) { - return; - } - - $media = $record; - - if (! $media instanceof SpatieMedia) { - return; - } - - // Construct the correct path: {media_id}/{filename} - $path = $media->id.'/'.$media->file_name; - - $component->state($path); - }), - ]); - } -} diff --git a/app/Filament/Resources/Media/Schemas/MediaInfolist.php b/app/Filament/Resources/Media/Schemas/MediaInfolist.php deleted file mode 100644 index dfe4801..0000000 --- a/app/Filament/Resources/Media/Schemas/MediaInfolist.php +++ /dev/null @@ -1,36 +0,0 @@ -components([ - ImageEntry::make('file_name') - ->label('Preview') - ->getStateUsing(fn ($record) => $record->getUrl()) - ->visible(fn ($record) => $record->mime_type && str_starts_with($record->mime_type, 'image/')), - TextEntry::make('name'), - TextEntry::make('file_name'), - TextEntry::make('mime_type'), - TextEntry::make('collection_name'), - TextEntry::make('size') - ->formatStateUsing(fn ($state) => number_format($state / 1024, 2).' KB'), - TextEntry::make('model_type') - ->label('Attached to Model'), - TextEntry::make('model_id'), - TextEntry::make('custom_properties') - ->formatStateUsing(fn ($state) => json_encode($state, JSON_PRETTY_PRINT)), - TextEntry::make('created_at') - ->dateTime(), - TextEntry::make('updated_at') - ->dateTime(), - ]); - } -} diff --git a/app/Filament/Resources/Media/Tables/MediaTable.php b/app/Filament/Resources/Media/Tables/MediaTable.php deleted file mode 100644 index dba76b0..0000000 --- a/app/Filament/Resources/Media/Tables/MediaTable.php +++ /dev/null @@ -1,86 +0,0 @@ -modifyQueryUsing(fn ($query) => $query->where('collection_name', '!=', 'avatars')) - ->columns([ - ImageColumn::make('url') - ->label('Preview') - ->getStateUsing(fn ($record) => - // Prefer the stored path produced by Filament's FileUpload (saved in custom_properties), - // fall back to Spatie's getUrl() when no stored_path exists. - ($record->getCustomProperty('stored_path')) - ? Storage::url($record->getCustomProperty('stored_path')) - : $record->getUrl() - ) - ->height(40) - ->width(40), - TextColumn::make('name') - ->searchable(), - TextColumn::make('file_name') - ->searchable(), - TextColumn::make('collection_name') - ->badge(), - TextColumn::make('mime_type'), - TextColumn::make('size') - ->formatStateUsing(fn ($state) => number_format($state / 1024, 2).' KB'), - TextColumn::make('created_at') - ->dateTime(), - ]) - ->filters([ - SelectFilter::make('collection_name') - ->options([ - 'images' => 'Images', - 'documents' => 'Documents', - ]), - ]) - ->recordActions([ - EditAction::make(), - DeleteAction::make() - ->action(function (Media $record) { - // Delete the actual stored file path if we saved one, otherwise fall back to the Spatie path. - $stored = $record->getCustomProperty('stored_path'); - if ($stored) { - Storage::disk($record->disk)->delete($stored); - } else { - Storage::disk($record->disk)->delete($record->getPath()); - } - - $record->delete(); - }), - ]) - ->toolbarActions([ - BulkActionGroup::make([ - DeleteBulkAction::make() - ->action(function (Collection $records) { - $records->each(function (Media $record) { - $stored = $record->getCustomProperty('stored_path'); - if ($stored) { - Storage::disk($record->disk)->delete($stored); - } else { - Storage::disk($record->disk)->delete($record->getPath()); - } - $record->delete(); - }); - }), - ]), - ]); - } -} diff --git a/app/Livewire/GalleryPicker.php b/app/Livewire/GalleryPicker.php deleted file mode 100644 index c1da485..0000000 --- a/app/Livewire/GalleryPicker.php +++ /dev/null @@ -1,108 +0,0 @@ -entryId = $entryId; - $this->loadMediaItems(); - $this->showModal = true; - } - - public function loadMediaItems(): void - { - $this->mediaItems = Media::where('model_type', 'temp') - ->where('model_id', 0) - ->where('disk', 'public') - ->latest() - ->limit(30) - ->get(['id', 'file_name', 'name', 'disk']) - ->toArray(); - } - - public function selectMedia($mediaId): void - { - $this->selectedMediaId = $mediaId; - } - - public function copyToEntry(): void - { - if (!$this->selectedMediaId || !$this->entryId) { - $this->dispatch('notify-error', ['message' => 'Please select an image']); - return; - } - - $sourceMedia = Media::find($this->selectedMediaId); - if (!$sourceMedia) { - $this->dispatch('notify-error', ['message' => 'Media not found']); - return; - } - - try { - // Get the entry - $entry = \App\Models\Entry::find($this->entryId); - if (!$entry) { - $this->dispatch('notify-error', ['message' => 'Entry not found']); - return; - } - - // Get source file - $sourceFile = $sourceMedia->getPath(); - if (!file_exists($sourceFile)) { - $this->dispatch('notify-error', ['message' => 'Source file not found']); - return; - } - - // Create temp copy - $tempCopy = sys_get_temp_dir() . '/' . uniqid() . '_' . $sourceMedia->file_name; - copy($sourceFile, $tempCopy); - - try { - // Clear existing featured image - $entry->clearMediaCollection('featured-image'); - - // Add to entry - $newMedia = $entry->addMedia($tempCopy) - ->usingName($sourceMedia->name ?: pathinfo($sourceMedia->file_name, PATHINFO_FILENAME)) - ->usingFileName($sourceMedia->file_name) - ->toMediaCollection('featured-image', 'public'); - - // Close modal and notify - $this->showModal = false; - $this->selectedMediaId = null; - $this->dispatch('media-selected', ['mediaId' => $newMedia->id, 'fileName' => $newMedia->file_name]); - $this->dispatch('notify-success', ['message' => 'Image added to entry']); - } finally { - if (file_exists($tempCopy)) { - unlink($tempCopy); - } - } - } catch (\Exception $e) { - $this->dispatch('notify-error', ['message' => 'Error: ' . $e->getMessage()]); - } - } - - public function closePicker(): void - { - $this->showModal = false; - $this->selectedMediaId = null; - } - - public function render() - { - return view('livewire.gallery-picker'); - } -} diff --git a/app/Models/Entry.php b/app/Models/Entry.php deleted file mode 100644 index d0036af..0000000 --- a/app/Models/Entry.php +++ /dev/null @@ -1,42 +0,0 @@ -registerRichContent('content') - ->fileAttachmentProvider( - SpatieMediaLibraryFileAttachmentProvider::make() - ->collection('content-attachments') - ->preserveFilenames() - ); - } - -} diff --git a/app/Providers/Filament/AdminPanelProvider.php b/app/Providers/Filament/AdminPanelProvider.php index 81aa43d..8ce4eb0 100644 --- a/app/Providers/Filament/AdminPanelProvider.php +++ b/app/Providers/Filament/AdminPanelProvider.php @@ -10,8 +10,6 @@ use Filament\Pages\Dashboard; use Filament\Panel; use Filament\PanelProvider; use Filament\Support\Colors\Color; -use Filament\Support\Facades\FilamentView; -use Filament\View\PanelsRenderHook; use Filament\Widgets\AccountWidget; use Filament\Widgets\FilamentInfoWidget; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; @@ -31,7 +29,7 @@ class AdminPanelProvider extends PanelProvider ->path('admin') ->login() ->colors([ - 'primary' => Color::Blue, + 'primary' => Color::Amber, ]) ->discoverResources(in: app_path('Filament/Resources'), for: 'App\Filament\Resources') ->discoverPages(in: app_path('Filament/Pages'), for: 'App\Filament\Pages') @@ -58,12 +56,4 @@ class AdminPanelProvider extends PanelProvider Authenticate::class, ]); } - - public function boot(): void - { - FilamentView::registerRenderHook( - PanelsRenderHook::BODY_END, - fn (): string => \Illuminate\Support\Facades\Blade::render('@vite("resources/js/app.js")'), - ); - } } diff --git a/composer.json b/composer.json index c67e0d1..f49e35f 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,7 @@ "laravel/fortify": "^1.30", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", - "livewire/flux": "^2.9.0", - "spatie/laravel-medialibrary": "^11.17" + "livewire/flux": "^2.9.0" }, "require-dev": { "fakerphp/faker": "^1.23", diff --git a/composer.lock b/composer.lock index 010c9b8..1b34af6 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bec347dcc3a450fc682920a766cbe019", + "content-hash": "ed91eaf8381afba35eea6bfdd94e4e18", "packages": [ { "name": "anourvalar/eloquent-serialize", @@ -565,83 +565,6 @@ ], "time": "2024-07-16T11:13:48+00:00" }, - { - "name": "composer/semver", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+00:00" - }, { "name": "danharrin/date-format-converter", "version": "v0.3.1", @@ -1509,43 +1432,6 @@ }, "time": "2025-12-30T13:02:44+00:00" }, - { - "name": "filament/spatie-laravel-media-library-plugin", - "version": "v4.4.0", - "source": { - "type": "git", - "url": "https://github.com/filamentphp/spatie-laravel-media-library-plugin.git", - "reference": "73748df28a9c2e8c34d2c02f9314c330602e1830" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/filamentphp/spatie-laravel-media-library-plugin/zipball/73748df28a9c2e8c34d2c02f9314c330602e1830", - "reference": "73748df28a9c2e8c34d2c02f9314c330602e1830", - "shasum": "" - }, - "require": { - "filament/support": "self.version", - "php": "^8.2", - "spatie/laravel-medialibrary": "^11.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Filament\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Filament support for `spatie/laravel-medialibrary`.", - "homepage": "https://github.com/filamentphp/filament", - "support": { - "issues": "https://github.com/filamentphp/filament/issues", - "source": "https://github.com/filamentphp/filament" - }, - "time": "2025-12-09T09:54:02+00:00" - }, { "name": "filament/support", "version": "v4.4.0", @@ -3649,84 +3535,6 @@ ], "time": "2025-12-19T02:00:29+00:00" }, - { - "name": "maennchen/zipstream-php", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/maennchen/ZipStream-PHP.git", - "reference": "682f1098a8fddbaf43edac2306a691c7ad508ec5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/maennchen/ZipStream-PHP/zipball/682f1098a8fddbaf43edac2306a691c7ad508ec5", - "reference": "682f1098a8fddbaf43edac2306a691c7ad508ec5", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "ext-zlib": "*", - "php-64bit": "^8.3" - }, - "require-dev": { - "brianium/paratest": "^7.7", - "ext-zip": "*", - "friendsofphp/php-cs-fixer": "^3.86", - "guzzlehttp/guzzle": "^7.5", - "mikey179/vfsstream": "^1.6", - "php-coveralls/php-coveralls": "^2.5", - "phpunit/phpunit": "^12.0", - "vimeo/psalm": "^6.0" - }, - "suggest": { - "guzzlehttp/psr7": "^2.4", - "psr/http-message": "^2.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "ZipStream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Paul Duncan", - "email": "pabs@pablotron.org" - }, - { - "name": "Jonatan Männchen", - "email": "jonatan@maennchen.ch" - }, - { - "name": "Jesse Donat", - "email": "donatj@gmail.com" - }, - { - "name": "András Kolesár", - "email": "kolesar@kolesar.hu" - } - ], - "description": "ZipStream is a library for dynamically streaming dynamic zip files from PHP without writing to the disk at all on the server.", - "keywords": [ - "stream", - "zip" - ], - "support": { - "issues": "https://github.com/maennchen/ZipStream-PHP/issues", - "source": "https://github.com/maennchen/ZipStream-PHP/tree/3.2.1" - }, - "funding": [ - { - "url": "https://github.com/maennchen", - "type": "github" - } - ], - "time": "2025-12-10T09:58:31+00:00" - }, { "name": "masterminds/html5", "version": "2.10.0", @@ -3796,16 +3604,16 @@ }, { "name": "monolog/monolog", - "version": "3.10.0", + "version": "3.9.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0" + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/b321dd6749f0bf7189444158a3ce785cc16d69b0", - "reference": "b321dd6749f0bf7189444158a3ce785cc16d69b0", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/10d85740180ecba7896c87e06a166e0c95a0e3b6", + "reference": "10d85740180ecba7896c87e06a166e0c95a0e3b6", "shasum": "" }, "require": { @@ -3823,7 +3631,7 @@ "graylog2/gelf-php": "^1.4.2 || ^2.0", "guzzlehttp/guzzle": "^7.4.5", "guzzlehttp/psr7": "^2.2", - "mongodb/mongodb": "^1.8 || ^2.0", + "mongodb/mongodb": "^1.8", "php-amqplib/php-amqplib": "~2.4 || ^3", "php-console/php-console": "^3.1.8", "phpstan/phpstan": "^2", @@ -3883,7 +3691,7 @@ ], "support": { "issues": "https://github.com/Seldaek/monolog/issues", - "source": "https://github.com/Seldaek/monolog/tree/3.10.0" + "source": "https://github.com/Seldaek/monolog/tree/3.9.0" }, "funding": [ { @@ -3895,7 +3703,7 @@ "type": "tidelift" } ], - "time": "2026-01-02T08:56:05+00:00" + "time": "2025-03-24T10:02:05+00:00" }, { "name": "nesbot/carbon", @@ -5574,134 +5382,6 @@ ], "time": "2022-12-17T21:53:22+00:00" }, - { - "name": "spatie/image", - "version": "3.8.7", - "source": { - "type": "git", - "url": "https://github.com/spatie/image.git", - "reference": "4d35db207c4b317bc221d02ab7ba94aa78b44c24" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/image/zipball/4d35db207c4b317bc221d02ab7ba94aa78b44c24", - "reference": "4d35db207c4b317bc221d02ab7ba94aa78b44c24", - "shasum": "" - }, - "require": { - "ext-exif": "*", - "ext-json": "*", - "ext-mbstring": "*", - "php": "^8.2", - "spatie/image-optimizer": "^1.7.5", - "spatie/temporary-directory": "^2.2", - "symfony/process": "^6.4|^7.0|^8.0" - }, - "require-dev": { - "ext-gd": "*", - "ext-imagick": "*", - "laravel/sail": "^1.34", - "pestphp/pest": "^3.0|^4.0", - "phpstan/phpstan": "^1.10.50", - "spatie/pest-plugin-snapshots": "^2.1", - "spatie/pixelmatch-php": "^1.0", - "spatie/ray": "^1.40.1", - "symfony/var-dumper": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\Image\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Manipulate images with an expressive API", - "homepage": "https://github.com/spatie/image", - "keywords": [ - "image", - "spatie" - ], - "support": { - "source": "https://github.com/spatie/image/tree/3.8.7" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2025-11-24T15:10:50+00:00" - }, - { - "name": "spatie/image-optimizer", - "version": "1.8.1", - "source": { - "type": "git", - "url": "https://github.com/spatie/image-optimizer.git", - "reference": "2ad9ac7c19501739183359ae64ea6c15869c23d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/image-optimizer/zipball/2ad9ac7c19501739183359ae64ea6c15869c23d9", - "reference": "2ad9ac7c19501739183359ae64ea6c15869c23d9", - "shasum": "" - }, - "require": { - "ext-fileinfo": "*", - "php": "^7.3|^8.0", - "psr/log": "^1.0 | ^2.0 | ^3.0", - "symfony/process": "^4.2|^5.0|^6.0|^7.0|^8.0" - }, - "require-dev": { - "pestphp/pest": "^1.21|^2.0|^3.0|^4.0", - "phpunit/phpunit": "^8.5.21|^9.4.4|^10.0|^11.0|^12.0", - "symfony/var-dumper": "^4.2|^5.0|^6.0|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\ImageOptimizer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Easily optimize images using PHP", - "homepage": "https://github.com/spatie/image-optimizer", - "keywords": [ - "image-optimizer", - "spatie" - ], - "support": { - "issues": "https://github.com/spatie/image-optimizer/issues", - "source": "https://github.com/spatie/image-optimizer/tree/1.8.1" - }, - "time": "2025-11-26T10:57:19+00:00" - }, { "name": "spatie/invade", "version": "2.1.0", @@ -5761,116 +5441,6 @@ ], "time": "2024-05-17T09:06:10+00:00" }, - { - "name": "spatie/laravel-medialibrary", - "version": "11.17.7", - "source": { - "type": "git", - "url": "https://github.com/spatie/laravel-medialibrary.git", - "reference": "237f34f70ae97523c1a99cad7176e229b8d6f0b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-medialibrary/zipball/237f34f70ae97523c1a99cad7176e229b8d6f0b6", - "reference": "237f34f70ae97523c1a99cad7176e229b8d6f0b6", - "shasum": "" - }, - "require": { - "composer/semver": "^3.4", - "ext-exif": "*", - "ext-fileinfo": "*", - "ext-json": "*", - "illuminate/bus": "^10.2|^11.0|^12.0", - "illuminate/conditionable": "^10.2|^11.0|^12.0", - "illuminate/console": "^10.2|^11.0|^12.0", - "illuminate/database": "^10.2|^11.0|^12.0", - "illuminate/pipeline": "^10.2|^11.0|^12.0", - "illuminate/support": "^10.2|^11.0|^12.0", - "maennchen/zipstream-php": "^3.1", - "php": "^8.2", - "spatie/image": "^3.3.2", - "spatie/laravel-package-tools": "^1.16.1", - "spatie/temporary-directory": "^2.2", - "symfony/console": "^6.4.1|^7.0|^8.0" - }, - "conflict": { - "php-ffmpeg/php-ffmpeg": "<0.6.1" - }, - "require-dev": { - "aws/aws-sdk-php": "^3.293.10", - "ext-imagick": "*", - "ext-pdo_sqlite": "*", - "ext-zip": "*", - "guzzlehttp/guzzle": "^7.8.1", - "larastan/larastan": "^2.7|^3.0", - "league/flysystem-aws-s3-v3": "^3.22", - "mockery/mockery": "^1.6.7", - "orchestra/testbench": "^8.36|^9.15|^10.8", - "pestphp/pest": "^2.36|^3.0|^4.0", - "phpstan/extension-installer": "^1.3.1", - "spatie/laravel-ray": "^1.33", - "spatie/pdf-to-image": "^2.2|^3.0", - "spatie/pest-expectations": "^1.13", - "spatie/pest-plugin-snapshots": "^2.1" - }, - "suggest": { - "league/flysystem-aws-s3-v3": "Required to use AWS S3 file storage", - "php-ffmpeg/php-ffmpeg": "Required for generating video thumbnails", - "spatie/pdf-to-image": "Required for generating thumbnails of PDFs and SVGs" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Spatie\\MediaLibrary\\MediaLibraryServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Spatie\\MediaLibrary\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Freek Van der Herten", - "email": "freek@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Associate files with Eloquent models", - "homepage": "https://github.com/spatie/laravel-medialibrary", - "keywords": [ - "cms", - "conversion", - "downloads", - "images", - "laravel", - "laravel-medialibrary", - "media", - "spatie" - ], - "support": { - "issues": "https://github.com/spatie/laravel-medialibrary/issues", - "source": "https://github.com/spatie/laravel-medialibrary/tree/11.17.7" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2025-12-15T08:51:55+00:00" - }, { "name": "spatie/laravel-package-tools", "version": "1.92.7", @@ -5997,67 +5567,6 @@ ], "time": "2025-02-21T14:16:57+00:00" }, - { - "name": "spatie/temporary-directory", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/spatie/temporary-directory.git", - "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/spatie/temporary-directory/zipball/580eddfe9a0a41a902cac6eeb8f066b42e65a32b", - "reference": "580eddfe9a0a41a902cac6eeb8f066b42e65a32b", - "shasum": "" - }, - "require": { - "php": "^8.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Spatie\\TemporaryDirectory\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alex Vanderbist", - "email": "alex@spatie.be", - "homepage": "https://spatie.be", - "role": "Developer" - } - ], - "description": "Easily create, use and destroy temporary directories", - "homepage": "https://github.com/spatie/temporary-directory", - "keywords": [ - "php", - "spatie", - "temporary-directory" - ], - "support": { - "issues": "https://github.com/spatie/temporary-directory/issues", - "source": "https://github.com/spatie/temporary-directory/tree/2.3.0" - }, - "funding": [ - { - "url": "https://spatie.be/open-source/support-us", - "type": "custom" - }, - { - "url": "https://github.com/spatie", - "type": "github" - } - ], - "time": "2025-01-13T13:04:43+00:00" - }, { "name": "symfony/clock", "version": "v7.4.0", diff --git a/config/media-library.php b/config/media-library.php deleted file mode 100644 index 33d558a..0000000 --- a/config/media-library.php +++ /dev/null @@ -1,303 +0,0 @@ - env('MEDIA_DISK', 'public'), - - /* - * The maximum file size of an item in bytes. - * Adding a larger file will result in an exception. - */ - 'max_file_size' => 1024 * 1024 * 10, // 10MB - - /* - * This queue connection will be used to generate derived and responsive images. - * Leave empty to use the default queue connection. - */ - 'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'), - - /* - * This queue will be used to generate derived and responsive images. - * Leave empty to use the default queue. - */ - 'queue_name' => env('MEDIA_QUEUE', ''), - - /* - * By default all conversions will be performed on a queue. - */ - 'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true), - - /* - * Should database transactions be run after database commits? - */ - 'queue_conversions_after_database_commit' => env('QUEUE_CONVERSIONS_AFTER_DB_COMMIT', true), - - /* - * The fully qualified class name of the media model. - */ - 'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class, - - /* - * The fully qualified class name of the media observer. - */ - 'media_observer' => Spatie\MediaLibrary\MediaCollections\Models\Observers\MediaObserver::class, - - /* - * When enabled, media collections will be serialised using the default - * laravel model serialization behaviour. - * - * Keep this option disabled if using Media Library Pro components (https://medialibrary.pro) - */ - 'use_default_collection_serialization' => false, - - /* - * The fully qualified class name of the model used for temporary uploads. - * - * This model is only used in Media Library Pro (https://medialibrary.pro) - */ - 'temporary_upload_model' => Spatie\MediaLibraryPro\Models\TemporaryUpload::class, - - /* - * When enabled, Media Library Pro will only process temporary uploads that were uploaded - * in the same session. You can opt to disable this for stateless usage of - * the pro components. - */ - 'enable_temporary_uploads_session_affinity' => true, - - /* - * When enabled, Media Library pro will generate thumbnails for uploaded file. - */ - 'generate_thumbnails_for_temporary_uploads' => true, - - /* - * This is the class that is responsible for naming generated files. - */ - 'file_namer' => Spatie\MediaLibrary\Support\FileNamer\DefaultFileNamer::class, - - /* - * The class that contains the strategy for determining a media file's path. - */ - 'path_generator' => Spatie\MediaLibrary\Support\PathGenerator\DefaultPathGenerator::class, - - /* - * The class that contains the strategy for determining how to remove files. - */ - 'file_remover_class' => Spatie\MediaLibrary\Support\FileRemover\DefaultFileRemover::class, - - /* - * Here you can specify which path generator should be used for the given class. - */ - 'custom_path_generators' => [ - // Model::class => PathGenerator::class - // or - // 'model_morph_alias' => PathGenerator::class - ], - - /* - * When urls to files get generated, this class will be called. Use the default - * if your files are stored locally above the site root or on s3. - */ - 'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class, - - /* - * Moves media on updating to keep path consistent. Enable it only with a custom - * PathGenerator that uses, for example, the media UUID. - */ - 'moves_media_on_update' => false, - - /* - * Whether to activate versioning when urls to files get generated. - * When activated, this attaches a ?v=xx query string to the URL. - */ - 'version_urls' => false, - - /* - * The media library will try to optimize all converted images by removing - * metadata and applying a little bit of compression. These are - * the optimizers that will be used by default. - */ - 'image_optimizers' => [ - Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [ - '-m85', // set maximum quality to 85% - '--force', // ensure that progressive generation is always done also if a little bigger - '--strip-all', // this strips out all text information such as comments and EXIF data - '--all-progressive', // this will make sure the resulting image is a progressive one - ], - Spatie\ImageOptimizer\Optimizers\Pngquant::class => [ - '--force', // required parameter for this package - ], - Spatie\ImageOptimizer\Optimizers\Optipng::class => [ - '-i0', // this will result in a non-interlaced, progressive scanned image - '-o2', // this set the optimization level to two (multiple IDAT compression trials) - '-quiet', // required parameter for this package - ], - Spatie\ImageOptimizer\Optimizers\Svgo::class => [ - '--disable=cleanupIDs', // disabling because it is known to cause troubles - ], - Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [ - '-b', // required parameter for this package - '-O3', // this produces the slowest but best results - ], - Spatie\ImageOptimizer\Optimizers\Cwebp::class => [ - '-m 6', // for the slowest compression method in order to get the best compression. - '-pass 10', // for maximizing the amount of analysis pass. - '-mt', // multithreading for some speed improvements. - '-q 90', // quality factor that brings the least noticeable changes. - ], - Spatie\ImageOptimizer\Optimizers\Avifenc::class => [ - '-a cq-level=23', // constant quality level, lower values mean better quality and greater file size (0-63). - '-j all', // number of jobs (worker threads, "all" uses all available cores). - '--min 0', // min quantizer for color (0-63). - '--max 63', // max quantizer for color (0-63). - '--minalpha 0', // min quantizer for alpha (0-63). - '--maxalpha 63', // max quantizer for alpha (0-63). - '-a end-usage=q', // rate control mode set to Constant Quality mode. - '-a tune=ssim', // SSIM as tune the encoder for distortion metric. - ], - ], - - /* - * These generators will be used to create an image of media files. - */ - 'image_generators' => [ - Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class, - Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class, - Spatie\MediaLibrary\Conversions\ImageGenerators\Avif::class, - Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class, - Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class, - Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class, - ], - - /* - * The path where to store temporary files while performing image conversions. - * If set to null, storage_path('media-library/temp') will be used. - */ - 'temporary_directory_path' => null, - - /* - * The engine that should perform the image conversions. - * Should be either `gd` or `imagick`. - */ - 'image_driver' => env('IMAGE_DRIVER', 'gd'), - - /* - * FFMPEG & FFProbe binaries paths, only used if you try to generate video - * thumbnails and have installed the php-ffmpeg/php-ffmpeg composer - * dependency. - */ - 'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'), - 'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'), - - /* - * The timeout (in seconds) that will be used when generating video - * thumbnails via FFMPEG. - */ - 'ffmpeg_timeout' => env('FFMPEG_TIMEOUT', 900), - - /* - * The number of threads that FFMPEG should use. 0 means that FFMPEG - * may decide itself. - */ - 'ffmpeg_threads' => env('FFMPEG_THREADS', 0), - - /* - * Here you can override the class names of the jobs used by this package. Make sure - * your custom jobs extend the ones provided by the package. - */ - 'jobs' => [ - 'perform_conversions' => Spatie\MediaLibrary\Conversions\Jobs\PerformConversionsJob::class, - 'generate_responsive_images' => Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class, - ], - - /* - * When using the addMediaFromUrl method you may want to replace the default downloader. - * This is particularly useful when the url of the image is behind a firewall and - * need to add additional flags, possibly using curl. - */ - 'media_downloader' => Spatie\MediaLibrary\Downloaders\DefaultDownloader::class, - - /* - * When using the addMediaFromUrl method the SSL is verified by default. - * This is option disables SSL verification when downloading remote media. - * Please note that this is a security risk and should only be false in a local environment. - */ - 'media_downloader_ssl' => env('MEDIA_DOWNLOADER_SSL', true), - - /* - * The default lifetime in minutes for temporary urls. - * This is used when you call the `getLastTemporaryUrl` or `getLastTemporaryUrl` method on a media item. - */ - 'temporary_url_default_lifetime' => env('MEDIA_TEMPORARY_URL_DEFAULT_LIFETIME', 5), - - 'remote' => [ - /* - * Any extra headers that should be included when uploading media to - * a remote disk. Even though supported headers may vary between - * different drivers, a sensible default has been provided. - * - * Supported by S3: CacheControl, Expires, StorageClass, - * ServerSideEncryption, Metadata, ACL, ContentEncoding - */ - 'extra_headers' => [ - 'CacheControl' => 'max-age=604800', - ], - ], - - 'responsive_images' => [ - /* - * This class is responsible for calculating the target widths of the responsive - * images. By default we optimize for filesize and create variations that each are 30% - * smaller than the previous one. More info in the documentation. - * - * https://docs.spatie.be/laravel-medialibrary/v9/advanced-usage/generating-responsive-images - */ - 'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class, - - /* - * By default rendering media to a responsive image will add some javascript and a tiny placeholder. - * This ensures that the browser can already determine the correct layout. - * When disabled, no tiny placeholder is generated. - */ - 'use_tiny_placeholders' => true, - - /* - * This class will generate the tiny placeholder used for progressive image loading. By default - * the media library will use a tiny blurred jpg image. - */ - 'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class, - ], - - /* - * When enabling this option, a route will be registered that will enable - * the Media Library Pro Vue and React components to move uploaded files - * in a S3 bucket to their right place. - */ - 'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false), - - /* - * When converting Media instances to response the media library will add - * a `loading` attribute to the `img` tag. Here you can set the default - * value of that attribute. - * - * Possible values: 'lazy', 'eager', 'auto' or null if you don't want to set any loading instruction. - * - * More info: https://css-tricks.com/native-lazy-loading/ - */ - 'default_loading_attribute_value' => null, - - /* - * You can specify a prefix for that is used for storing all media. - * If you set this to `/my-subdir`, all your media will be stored in a `/my-subdir` directory. - */ - 'prefix' => env('MEDIA_PREFIX', ''), - - /* - * When forcing lazy loading, media will be loaded even if you don't eager load media and you have - * disabled lazy loading globally in the service provider. - */ - 'force_lazy_loading' => env('FORCE_MEDIA_LIBRARY_LAZY_LOADING', true), -]; diff --git a/database/migrations/2026_01_02_134204_create_entries_table.php b/database/migrations/2026_01_02_134204_create_entries_table.php deleted file mode 100644 index 337002b..0000000 --- a/database/migrations/2026_01_02_134204_create_entries_table.php +++ /dev/null @@ -1,34 +0,0 @@ -id(); - $table->string('title'); - $table->string('slug')->unique(); - $table->text('description')->nullable(); - $table->boolean('is_published')->default(false); - $table->boolean('is_featured')->default(false); - $table->date('published_at')->nullable(); - $table->mediumText('content')->nullable(); - $table->timestamps(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('entries'); - } -}; diff --git a/database/migrations/2026_01_02_160151_create_media_table.php b/database/migrations/2026_01_02_160151_create_media_table.php deleted file mode 100644 index 47a4be9..0000000 --- a/database/migrations/2026_01_02_160151_create_media_table.php +++ /dev/null @@ -1,32 +0,0 @@ -id(); - - $table->morphs('model'); - $table->uuid()->nullable()->unique(); - $table->string('collection_name'); - $table->string('name'); - $table->string('file_name'); - $table->string('mime_type')->nullable(); - $table->string('disk'); - $table->string('conversions_disk')->nullable(); - $table->unsignedBigInteger('size'); - $table->json('manipulations'); - $table->json('custom_properties'); - $table->json('generated_conversions'); - $table->json('responsive_images'); - $table->unsignedInteger('order_column')->nullable()->index(); - - $table->nullableTimestamps(); - }); - } -}; diff --git a/docs/decisions/002-AGPLv3-and-filament.md b/docs/decisions/002-AGPLv3-and-filament.md index 7bed142..d19b605 100644 --- a/docs/decisions/002-AGPLv3-and-filament.md +++ b/docs/decisions/002-AGPLv3-and-filament.md @@ -12,3 +12,4 @@ It is a way to create apps that have all the features of an advanced app with ma There are so many things to do, we don't have the time to re-write or re-invent what is already achieved by filament and likely, better than we could create ourselves or likely dream of. + diff --git a/docs/decisions/003-initial-model-and-filament-resource.md b/docs/decisions/003-initial-model-and-filament-resource.md deleted file mode 100644 index 90e58bd..0000000 --- a/docs/decisions/003-initial-model-and-filament-resource.md +++ /dev/null @@ -1,86 +0,0 @@ -# 2026-01-02 - -## Entry model - -```bash -php artisan make:model -m Entry -``` - -creates skeleton files for a model and migration - -we edit the migration to hold the fields we desire to be present in each record - -just the minimal are added for now, paying attention to the amount of data that these may need to hold - -* string (varchar): ~255 chars default. -* text: ~65 KB. -* mediumText: ~16 MB. - -```bash -php artisan migrate -``` - -runs the migration to create the table - -## Entry filament resource - -```bash -❯ php artisan filament:resource Entry - - The "title attribute" is used to label each record in the UI. - - You can leave this blank if records do not have a title. - - ┌ What is the title attribute for this model? ─────────────────┐ - │ title │ - └──────────────────────────────────────────────────────────────┘ - - ┌ Would you like to generate a read-only view page for the resource? ┐ - │ Yes │ - └────────────────────────────────────────────────────────────────────┘ - - ┌ Should the configuration be generated from the current database columns? ┐ - │ Yes │ - └──────────────────────────────────────────────────────────────────────────┘ - - INFO Filament resource [App\Filament\Resources\Entries\EntryResource] created successfully. -``` - -There is more work to do on this model but for now, a new resource is added to the admin panel by which records can be added, edited, listed. - -Full CRUD is already possible. This is amazing if you think how long this would have taken to create all this manually. - - - -### slugify - -```php - TextInput::make('title') - ->required() - ->reactive() - ->afterStateUpdated(function ($state, $set): void { - $set('slug', Str::slug((string) $state)); - }), - TextInput::make('slug') - ->required(), -``` - -adding reactive and afterStateUpdted to title automatically creates a safe slug - -I class things like this creature comforts of the framework and it shows how simple it can be to make fields active and updated by previous entries - -For the user, this reduces error and confusion over what a url should be - -### rich editor - -just changing the TextInput for content - -```php - RichEditor::make('content') - ->columnSpanFull(), -``` - -to `RichEditr` gives us a 'tiptap' rich text editor - -already, this is feeling more like a mini CMS, with relatively little effort - diff --git a/docs/decisions/004-spatie-media-library b/docs/decisions/004-spatie-media-library deleted file mode 100644 index e1dc122..0000000 --- a/docs/decisions/004-spatie-media-library +++ /dev/null @@ -1,10 +0,0 @@ -# 2026-01-02 - -```bash -php artisan storage:link -composer require "spatie/laravel-medialibrary" -composer require filament/spatie-laravel-media-library-plugin:"^4.0" -W -php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-config" -php artisan migrate -``` - diff --git a/resources/js/app.js b/resources/js/app.js index 22e1c9c..e69de29 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -1,48 +0,0 @@ -console.log('App.js is loaded'); - -document.addEventListener('livewire:init', () => { - Livewire.on('insert-editor-content', (data) => { - console.log('Received insert-editor-content data:', data); - // Handle if data is an array - const payload = Array.isArray(data) ? data[0] : data; - const statePath = payload.statePath; - const html = payload.html; - console.log('Extracted statePath:', statePath, 'html:', html); - // 1. Find the editor by its statePath - const container = document.querySelector(`[wire\\:model="${statePath}"]`) || document.querySelector(`[statepath="${statePath}"]`); - console.log('Container found:', container); - const editorElement = container ? container.querySelector('.tiptap') : null; - console.log('Editor element found:', editorElement); - - if (editorElement && editorElement.editor) { - console.log('Inserting content:', html); - // 2. Insert the HTML (the tag) into the editor - setTimeout(() => { - editorElement.editor.chain().focus().insertContent(html).run(); - }, 500); - } else { - console.log('Editor not found or not initialized'); - // Fallback: try to find any .tiptap on the page - const anyTiptap = document.querySelector('.tiptap'); - console.log('Any tiptap found:', anyTiptap); - if (anyTiptap && anyTiptap.editor) { - console.log('Inserting to any tiptap'); - setTimeout(() => { - anyTiptap.editor.chain().focus().insertContent(html).run(); - }, 500); - } - } - }); - - Livewire.on('featured-image-added', (data) => { - console.log('Received featured-image-added event:', data); - const payload = Array.isArray(data) ? data[0] : data; - - // Reload the page to show the updated featured image - setTimeout(() => { - window.location.reload(); - }, 500); - }); -}); - -console.log('Testing if app.js is still running'); \ No newline at end of file diff --git a/resources/views/components/featured-image-display.blade.php b/resources/views/components/featured-image-display.blade.php deleted file mode 100644 index 437552f..0000000 --- a/resources/views/components/featured-image-display.blade.php +++ /dev/null @@ -1,32 +0,0 @@ -
- @php - $record = $this->data; - if ($record && isset($record['id'])) { - $entry = \App\Models\Entry::find($record['id']); - $media = $entry ? $entry->getMedia('featured-image') : []; - } else { - $media = []; - } - @endphp - -
- - @if(count($media) > 0) -
- @foreach($media as $item) -
-
- -
-

{{ $item->name }}

-

{{ $item->file_name }}

-
-
-
- @endforeach -
- @else -

No featured image selected

- @endif -
-
diff --git a/resources/views/livewire/gallery-picker.blade.php b/resources/views/livewire/gallery-picker.blade.php deleted file mode 100644 index 103b66b..0000000 --- a/resources/views/livewire/gallery-picker.blade.php +++ /dev/null @@ -1,72 +0,0 @@ -
- @if($showModal ?? false) -
-
- -
- - -
-
-
-

- Select Image from Gallery -

- -
- - @if(empty($mediaItems ?? [])) -

No images in gallery

- @else -
- @foreach($mediaItems ?? [] as $item) - - @endforeach -
- @endif -
- -
- - -
-
-
-
- @endif -
diff --git a/tests/Pest.php b/tests/Pest.php index b3a25b7..40d096b 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -13,7 +13,7 @@ pest()->extend(Tests\TestCase::class) ->use(Illuminate\Foundation\Testing\RefreshDatabase::class) - ->in('Feature', 'Unit'); + ->in('Feature'); /* |-------------------------------------------------------------------------- diff --git a/tests/TestCase.php b/tests/TestCase.php index 65c00a0..fe1ffc2 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -6,15 +6,5 @@ use Illuminate\Foundation\Testing\TestCase as BaseTestCase; abstract class TestCase extends BaseTestCase { - /** - * Creates the application. - */ - public function createApplication(): \Illuminate\Foundation\Application - { - $app = require __DIR__ . '/../bootstrap/app.php'; - - $app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap(); - - return $app; - } + // } diff --git a/tests/Unit/EntryModelTest.php b/tests/Unit/EntryModelTest.php deleted file mode 100644 index e339278..0000000 --- a/tests/Unit/EntryModelTest.php +++ /dev/null @@ -1,19 +0,0 @@ -getFillable())->toEqual($expected); -}); diff --git a/tests/Unit/EntryResourceTest.php b/tests/Unit/EntryResourceTest.php deleted file mode 100644 index 3757f62..0000000 --- a/tests/Unit/EntryResourceTest.php +++ /dev/null @@ -1,53 +0,0 @@ -getDefaultProperties(); - - expect($defaults['model'] ?? null)->toBe(Entry::class); - expect($defaults['recordTitleAttribute'] ?? null)->toBe('title'); -}); - -it('defines the expected pages', function () { - $pages = EntryResource::getPages(); - - expect(array_keys($pages))->toEqual(['index', 'create', 'view', 'edit']); -}); - -it('accepts new record when entered via the form schema', function () { - - $data = [ - 'title' => 'Test Entry', - 'slug' => 'test-entry', - 'description' => 'This is a test entry.', - 'is_published' => true, - 'is_featured' => false, - 'published_at' => now()->toDateString(), - 'content' => '

This is the content of the test entry.

', - ]; - - $entry = new Entry(); - $entry->fill($data); - - expect($entry->slug)->toBe('test-entry'); -}); - -it('deletes a record correctly', function () { - $entry = Entry::create([ - 'title' => 'Test Entry to Delete', - 'slug' => 'test-entry-to-delete', - 'description' => 'This is a test entry.', - 'is_published' => false, - 'is_featured' => false, - 'published_at' => null, - 'content' => '

This is the content of the test entry.

', - ]); - - $entryId = $entry->id; - $entry->delete(); - - $deletedEntry = Entry::find($entryId); - expect($deletedEntry)->toBeNull(); -}); \ No newline at end of file