diff --git a/composer.json b/composer.json index 20d02876..23a6858c 100644 --- a/composer.json +++ b/composer.json @@ -29,6 +29,7 @@ "hshn/base64-encoded-file": "^5.0", "jbtronics/2fa-webauthn": "^v2.2.0", "jbtronics/dompdf-font-loader-bundle": "^1.0.0", + "jbtronics/settings-bundle": "^2.0", "jfcherng/php-diff": "^6.14", "knpuniversity/oauth2-client-bundle": "^2.15", "league/csv": "^9.8.0", diff --git a/composer.lock b/composer.lock index f74031a6..6a501621 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": "19c280b6d5021cb69af33476174dfc1e", + "content-hash": "047dbea97d20f32d8b1fd175e3891941", "packages": [ { "name": "api-platform/core", @@ -2127,6 +2127,74 @@ ], "time": "2023-10-06T06:47:41+00:00" }, + { + "name": "ergebnis/classy", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/classy.git", + "reference": "e8da19634d7d7d7e293e916b1419395224658d5a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/classy/zipball/e8da19634d7d7d7e293e916b1419395224658d5a", + "reference": "e8da19634d7d7d7e293e916b1419395224658d5a", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.39.0", + "ergebnis/license": "^2.3.0", + "ergebnis/php-cs-fixer-config": "~6.12.0", + "ergebnis/phpunit-slow-test-detector": "^2.4.0", + "fakerphp/faker": "^1.23.0", + "infection/infection": "~0.27.8", + "phpunit/phpunit": "^10.4.2", + "psalm/plugin-phpunit": "~0.18.4", + "rector/rector": "~0.18.11", + "vimeo/psalm": "^5.16.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.0-dev" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\Classy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides a finder for classy constructs (classes, enums, interfaces, and traits).", + "homepage": "https://github.com/ergebnis/classy", + "keywords": [ + "classes", + "classy", + "constructs", + "finder", + "interfaces", + "traits" + ], + "support": { + "issues": "https://github.com/ergebnis/classy/issues", + "source": "https://github.com/ergebnis/classy" + }, + "time": "2023-11-27T16:46:22+00:00" + }, { "name": "erusev/parsedown", "version": "1.7.4", @@ -3147,6 +3215,91 @@ }, "time": "2023-12-24T14:11:31+00:00" }, + { + "name": "jbtronics/settings-bundle", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/jbtronics/settings-bundle.git", + "reference": "c8a54ab5517e42e22c00b65f04ffe308a2b0e69b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jbtronics/settings-bundle/zipball/c8a54ab5517e42e22c00b65f04ffe308a2b0e69b", + "reference": "c8a54ab5517e42e22c00b65f04ffe308a2b0e69b", + "shasum": "" + }, + "require": { + "ergebnis/classy": "^1.6", + "ext-json": "*", + "php": "^8.1", + "symfony/deprecation-contracts": "^3.4", + "symfony/form": "^6.4|^7.0", + "symfony/framework-bundle": "^6.4|^7.0", + "symfony/translation": "^7.0|^6.4", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/validator": "^6.4|^7.0", + "symfony/var-exporter": "^6.4|^7.0" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^2.11", + "doctrine/doctrine-fixtures-bundle": "^3.5", + "doctrine/orm": "^3.0", + "ekino/phpstan-banned-code": "^1.0", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-strict-rules": "^1.5", + "phpstan/phpstan-symfony": "^1.3", + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-latest", + "symfony/phpunit-bridge": "^6.4|^7.0", + "symfony/security-csrf": "^7.0|^6.4", + "symfony/twig-bridge": "^6.4|^7.0" + }, + "suggest": { + "doctrine/doctrine-bundle": "To use the doctrine ORM storage", + "symfony/twig-bridge": "Allows to access settings in twig templates" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Jbtronics\\SettingsBundle\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Böhmer", + "email": "mail@jan-boehmer.de" + } + ], + "description": "A symfony bundle to easily create typesafe, user-configurable settings for symfony applications", + "keywords": [ + "Settings", + "config", + "symfony", + "symfony-bundle", + "user-configurable" + ], + "support": { + "issues": "https://github.com/jbtronics/settings-bundle/issues", + "source": "https://github.com/jbtronics/settings-bundle/tree/2.0.1" + }, + "funding": [ + { + "url": "https://www.paypal.me/do9jhb", + "type": "custom" + }, + { + "url": "https://github.com/jbtronics", + "type": "github" + } + ], + "time": "2024-04-28T20:49:45+00:00" + }, { "name": "jfcherng/php-color-output", "version": "3.0.0", diff --git a/config/bundles.php b/config/bundles.php index b78bbc22..3166f6ab 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -32,4 +32,5 @@ return [ KnpU\OAuth2ClientBundle\KnpUOAuth2ClientBundle::class => ['all' => true], Nelmio\CorsBundle\NelmioCorsBundle::class => ['all' => true], ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true], + Jbtronics\SettingsBundle\JbtronicsSettingsBundle::class => ['all' => true], ]; diff --git a/config/packages/settings.yaml b/config/packages/settings.yaml new file mode 100644 index 00000000..6b47cba3 --- /dev/null +++ b/config/packages/settings.yaml @@ -0,0 +1,2 @@ +jbtronics_settings: + default_storage_adapter: Jbtronics\SettingsBundle\Storage\PHPFileStorageAdapter \ No newline at end of file diff --git a/config/services.yaml b/config/services.yaml index 0e30ab14..e31356c5 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -307,11 +307,6 @@ services: $options: '%env(string:PROVIDER_MOUSER_SEARCH_OPTION)%' $search_limit: '%env(int:PROVIDER_MOUSER_SEARCH_LIMIT)%' - App\Services\InfoProviderSystem\Providers\LCSCProvider: - arguments: - $enabled: '%env(bool:PROVIDER_LCSC_ENABLED)%' - $currency: '%env(string:PROVIDER_LCSC_CURRENCY)%' - #################################################################################################################### # API system #################################################################################################################### diff --git a/src/Controller/SettingsController.php b/src/Controller/SettingsController.php new file mode 100644 index 00000000..b48b66ff --- /dev/null +++ b/src/Controller/SettingsController.php @@ -0,0 +1,66 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Controller; + +use App\Settings\AppSettings; +use Jbtronics\SettingsBundle\Form\SettingsFormFactoryInterface; +use Jbtronics\SettingsBundle\Manager\SettingsManagerInterface; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; + +class SettingsController extends AbstractController +{ + public function __construct(private readonly SettingsManagerInterface $settingsManager, private readonly SettingsFormFactoryInterface $settingsFormFactory) + {} + + #[Route("/settings", name: "system_settings")] + public function systemSettings(Request $request): Response + { + //Create a clone of the settings object + $settings = $this->settingsManager->createTemporaryCopy(AppSettings::class); + + //Create a form builder for the settings object + $builder = $this->settingsFormFactory->createSettingsFormBuilder($settings); + + //Add a submit button to the form + $builder->add('submit', \Symfony\Component\Form\Extension\Core\Type\SubmitType::class, ['label' => 'save']); + + //Create the form + $form = $builder->getForm(); + $form->handleRequest($request); + + //If the form was submitted and is valid, save the settings + if ($form->isSubmitted() && $form->isValid()) { + $this->settingsManager->mergeTemporaryCopy($settings); + $this->settingsManager->save($settings); + } + + //Render the form + return $this->render('settings/settings.html.twig', [ + 'form' => $form + ]); + } +} \ No newline at end of file diff --git a/src/Services/InfoProviderSystem/Providers/LCSCProvider.php b/src/Services/InfoProviderSystem/Providers/LCSCProvider.php index 84424f44..7ccfe7df 100755 --- a/src/Services/InfoProviderSystem/Providers/LCSCProvider.php +++ b/src/Services/InfoProviderSystem/Providers/LCSCProvider.php @@ -29,6 +29,7 @@ use App\Services\InfoProviderSystem\DTOs\ParameterDTO; use App\Services\InfoProviderSystem\DTOs\PartDetailDTO; use App\Services\InfoProviderSystem\DTOs\PriceDTO; use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO; +use App\Settings\InfoProviderSystem\LCSCSettings; use Symfony\Component\HttpFoundation\Cookie; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -39,7 +40,7 @@ class LCSCProvider implements InfoProviderInterface public const DISTRIBUTOR_NAME = 'LCSC'; - public function __construct(private readonly HttpClientInterface $lcscClient, private readonly string $currency, private readonly bool $enabled = true) + public function __construct(private readonly HttpClientInterface $lcscClient, private readonly LCSCSettings $settings) { } @@ -62,7 +63,7 @@ class LCSCProvider implements InfoProviderInterface // This provider is always active public function isActive(): bool { - return $this->enabled; + return $this->settings->enabled; } /** @@ -73,7 +74,7 @@ class LCSCProvider implements InfoProviderInterface { $response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/product/detail", [ 'headers' => [ - 'Cookie' => new Cookie('currencyCode', $this->currency) + 'Cookie' => new Cookie('currencyCode', $this->settings->currency) ], 'query' => [ 'productCode' => $id, @@ -120,7 +121,7 @@ class LCSCProvider implements InfoProviderInterface { $response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/search/global", [ 'headers' => [ - 'Cookie' => new Cookie('currencyCode', $this->currency) + 'Cookie' => new Cookie('currencyCode', $this->settings->currency) ], 'query' => [ 'keyword' => $term, @@ -270,7 +271,7 @@ class LCSCProvider implements InfoProviderInterface 'kr.' => 'DKK', '₹' => 'INR', //Fallback to the configured currency - default => $this->currency, + default => $this->settings->currency, }; } diff --git a/src/Settings/AppSettings.php b/src/Settings/AppSettings.php new file mode 100644 index 00000000..1b067939 --- /dev/null +++ b/src/Settings/AppSettings.php @@ -0,0 +1,38 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings; + +use App\Settings\InfoProviderSystem\InfoProviderSettings; +use Jbtronics\SettingsBundle\Settings\EmbeddedSettings; +use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsTrait; + +#[Settings] +class AppSettings +{ + use SettingsTrait; + + #[EmbeddedSettings] + public InfoProviderSettings $infoProviders; +} \ No newline at end of file diff --git a/src/Settings/InfoProviderSystem/InfoProviderSettings.php b/src/Settings/InfoProviderSystem/InfoProviderSettings.php new file mode 100644 index 00000000..3c2f3678 --- /dev/null +++ b/src/Settings/InfoProviderSystem/InfoProviderSettings.php @@ -0,0 +1,37 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\InfoProviderSystem; + +use Jbtronics\SettingsBundle\Settings\EmbeddedSettings; +use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsTrait; + +#[Settings] +class InfoProviderSettings +{ + use SettingsTrait; + + #[EmbeddedSettings] + public LCSCSettings $lcsc; +} \ No newline at end of file diff --git a/src/Settings/InfoProviderSystem/LCSCSettings.php b/src/Settings/InfoProviderSystem/LCSCSettings.php new file mode 100644 index 00000000..c9474407 --- /dev/null +++ b/src/Settings/InfoProviderSystem/LCSCSettings.php @@ -0,0 +1,43 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\InfoProviderSystem; + +use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsParameter; +use Jbtronics\SettingsBundle\Settings\SettingsTrait; +use Symfony\Component\Form\Extension\Core\Type\CurrencyType; +use Symfony\Component\Validator\Constraints as Assert; + +#[Settings] +class LCSCSettings +{ + use SettingsTrait; + + #[SettingsParameter(label: "Enable LCSC provider", envVar: "bool:PROVIDER_LCSC_ENABLED")] + public bool $enabled = false; + + #[SettingsParameter(label: "LCSC Currency", description: "The currency to retrieve prices from LCSC", formType: CurrencyType::class, envVar: "string:PROVIDER_LCSC_CURRENCY")] + #[Assert\Currency()] + public string $currency = 'EUR'; +} \ No newline at end of file diff --git a/symfony.lock b/symfony.lock index 1e04eedd..09bf42e3 100644 --- a/symfony.lock +++ b/symfony.lock @@ -194,6 +194,9 @@ "jbtronics/dompdf-font-loader-bundle": { "version": "v1.1.1" }, + "jbtronics/settings-bundle": { + "version": "2.0.1" + }, "knpuniversity/oauth2-client-bundle": { "version": "2.15", "recipe": { diff --git a/templates/settings/settings.html.twig b/templates/settings/settings.html.twig new file mode 100644 index 00000000..2b292b14 --- /dev/null +++ b/templates/settings/settings.html.twig @@ -0,0 +1,7 @@ +{% extends "main_card.html.twig" %} + +{% block card_content %} + {{ form_start(form) }} + {{ form_widget(form.infoProviders) }} + {{ form_end(form) }} +{% endblock %} \ No newline at end of file