diff --git a/app/Liquid/Filters/StandardFilters.php b/app/Liquid/Filters/StandardFilters.php new file mode 100644 index 0000000..4db86a0 --- /dev/null +++ b/app/Liquid/Filters/StandardFilters.php @@ -0,0 +1,20 @@ +filterRegistry->register(StandardFilters::class); $liquidTemplate = $environment->parseString($template); $context = $environment->newRenderContext(data: $variables); @@ -285,7 +288,7 @@ class Plugin extends Model $inlineFileSystem = new InlineTemplatesFileSystem(); $environment = new \Keepsuit\Liquid\Environment( fileSystem: $inlineFileSystem, - extensions: [new StandardExtension()] + extensions: [new StandardExtension(), new LaravelLiquidExtension()] ); // Register all custom filters diff --git a/resources/views/livewire/catalog/index.blade.php b/resources/views/livewire/catalog/index.blade.php index 4725e68..92bd5a9 100644 --- a/resources/views/livewire/catalog/index.blade.php +++ b/resources/views/livewire/catalog/index.blade.php @@ -2,6 +2,7 @@ use App\Services\PluginImportService; use Livewire\Volt\Component; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; @@ -26,21 +27,40 @@ new class extends Component { $catalogContent = $response->body(); $catalog = Yaml::parse($catalogContent); - return collect($catalog)->map(function ($plugin, $key) { - return [ - 'id' => $key, - 'name' => $plugin['name'] ?? 'Unknown Plugin', - 'description' => $plugin['author_bio']['description'] ?? '', - 'author' => $plugin['author']['name'] ?? 'Unknown Author', - 'github' => $plugin['author']['github'] ?? null, - 'license' => $plugin['license'] ?? null, - 'zip_url' => $plugin['trmnlp']['zip_url'] ?? null, - 'repo_url' => $plugin['trmnlp']['repo'] ?? null, - 'logo_url' => $plugin['logo_url'] ?? null, - 'screenshot_url' => $plugin['screenshot_url'] ?? null, - 'learn_more_url' => $plugin['author_bio']['learn_more_url'] ?? null, - ]; - })->toArray(); + $currentVersion = config('app.version'); + + return collect($catalog) + ->filter(function ($plugin) use ($currentVersion) { + // Check if Laravel compatibility is true + if (!Arr::get($plugin, 'byos.byos_laravel.compatibility', false)) { + return false; + } + + // Check minimum version if specified + $minVersion = Arr::get($plugin, 'byos.byos_laravel.min_version'); + if ($minVersion && $currentVersion && version_compare($currentVersion, $minVersion, '<')) { + return false; + } + + return true; + }) + ->map(function ($plugin, $key) { + return [ + 'id' => $key, + 'name' => Arr::get($plugin, 'name', 'Unknown Plugin'), + 'description' => Arr::get($plugin, 'author_bio.description', ''), + 'author' => Arr::get($plugin, 'author.name', 'Unknown Author'), + 'github' => Arr::get($plugin, 'author.github'), + 'license' => Arr::get($plugin, 'license'), + 'zip_url' => Arr::get($plugin, 'trmnlp.zip_url'), + 'repo_url' => Arr::get($plugin, 'trmnlp.repo'), + 'logo_url' => Arr::get($plugin, 'logo_url'), + 'screenshot_url' => Arr::get($plugin, 'screenshot_url'), + 'learn_more_url' => Arr::get($plugin, 'author_bio.learn_more_url'), + ]; + }) + ->sortBy('name') + ->toArray(); } catch (\Exception $e) { Log::error('Failed to load catalog from URL: ' . $e->getMessage()); return []; diff --git a/resources/views/livewire/plugins/index.blade.php b/resources/views/livewire/plugins/index.blade.php index 828e051..bcecfc9 100644 --- a/resources/views/livewire/plugins/index.blade.php +++ b/resources/views/livewire/plugins/index.blade.php @@ -38,10 +38,7 @@ new class extends Component { public function refreshPlugins(): void { - $userPlugins = auth()->user()?->plugins?->map(function ($plugin) { - return $plugin->toArray(); - })->toArray(); - + $userPlugins = auth()->user()?->plugins?->makeHidden(['render_markup', 'data_payload'])->toArray(); $this->plugins = array_merge($this->native_plugins, $userPlugins ?? []); } diff --git a/resources/views/livewire/plugins/recipe.blade.php b/resources/views/livewire/plugins/recipe.blade.php index 9226af6..3a8e7cc 100644 --- a/resources/views/livewire/plugins/recipe.blade.php +++ b/resources/views/livewire/plugins/recipe.blade.php @@ -133,7 +133,7 @@ new class extends Component { $this->addError('polling_url', 'The polling URL must be a valid URL after resolving configuration variables.'); } } catch (\Exception $e) { - $this->addError('polling_url', 'Error resolving Liquid variables: ' . $e->getMessage()); + $this->addError('polling_url', 'Error resolving Liquid variables: ' . $e->getMessage() . $e->getPrevious()?->getMessage()); } } } @@ -148,7 +148,7 @@ new class extends Component { $this->data_payload_updated_at = $this->plugin->data_payload_updated_at; } catch (\Exception $e) { - $this->dispatch('data-update-error', message: $e->getMessage()); + $this->dispatch('data-update-error', message: $e->getMessage() . $e->getPrevious()?->getMessage()); } } } @@ -690,7 +690,10 @@ HTML; @endforeach @else - + @php + $key = mb_strtolower(str_replace(' ', '_', $option)); + @endphp + @endif @endforeach @endif diff --git a/tests/Feature/Livewire/Catalog/IndexTest.php b/tests/Feature/Livewire/Catalog/IndexTest.php index 7defd78..5964588 100644 --- a/tests/Feature/Livewire/Catalog/IndexTest.php +++ b/tests/Feature/Livewire/Catalog/IndexTest.php @@ -38,6 +38,11 @@ it('loads plugins from catalog URL', function () { 'trmnlp' => [ 'zip_url' => 'https://example.com/plugin.zip', ], + 'byos' => [ + 'byos_laravel' => [ + 'compatibility' => true, + ] + ], 'logo_url' => 'https://example.com/logo.png', ], ]; diff --git a/tests/Feature/PluginLiquidWhereFilterTest.php b/tests/Feature/PluginLiquidFilterTest.php similarity index 76% rename from tests/Feature/PluginLiquidWhereFilterTest.php rename to tests/Feature/PluginLiquidFilterTest.php index c165109..fb429ae 100644 --- a/tests/Feature/PluginLiquidWhereFilterTest.php +++ b/tests/Feature/PluginLiquidFilterTest.php @@ -2,7 +2,9 @@ declare(strict_types=1); +use App\Liquid\Filters\StandardFilters; use App\Models\Plugin; +use Keepsuit\Liquid\Environment; /** * Tests for the Liquid where filter functionality @@ -92,3 +94,31 @@ LIQUID // Should not contain the low tide data $this->assertStringNotContainsString('"type":"L"', $result); }); + +it('encodes arrays for url_encode as JSON with spaces after commas and then percent-encodes', function () { + /** @var Environment $env */ + $env = app('liquid.environment'); + $env->filterRegistry->register(StandardFilters::class); + + $template = $env->parseString('{{ categories | url_encode }}'); + + $output = $template->render($env->newRenderContext([ + 'categories' => ['common', 'obscure'], + ])); + + expect($output)->toBe('%5B%22common%22%2C%22obscure%22%5D'); +}); + +it('keeps scalar url_encode behavior intact', function () { + /** @var Environment $env */ + $env = app('liquid.environment'); + $env->filterRegistry->register(StandardFilters::class); + + $template = $env->parseString('{{ text | url_encode }}'); + + $output = $template->render($env->newRenderContext([ + 'text' => 'hello world', + ])); + + expect($output)->toBe('hello+world'); +});