Compare commits

..

No commits in common. "18bf68cb6abadb765e28e738abbd950ebee39ce6" and "b2946aee0d444391b8e3ea45cc7facee08894809" have entirely different histories.

40 changed files with 393 additions and 903 deletions

View file

@ -47,7 +47,6 @@
PassEnv PROVIDER_REICHELT_ENABLED PROVIDER_REICHELT_CURRENCY PROVIDER_REICHELT_COUNTRY PROVIDER_REICHELT_LANGUAGE PROVIDER_REICHELT_INCLUDE_VAT
PassEnv PROVIDER_POLLIN_ENABLED
PassEnv EDA_KICAD_CATEGORY_DEPTH
PassEnv SHOW_PART_IMAGE_OVERLAY
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to

31
.env
View file

@ -67,6 +67,34 @@ ERROR_PAGE_ADMIN_EMAIL=''
# If this is set to true, solutions to common problems are shown on error pages. Disable this, if you do not want your users to see them...
ERROR_PAGE_SHOW_HELP=1
##################################################################################
# Info provider settings
##################################################################################
# Digikey Provider:
# You can get your client id and secret from https://developer.digikey.com/
PROVIDER_DIGIKEY_CLIENT_ID=
PROVIDER_DIGIKEY_SECRET=
# The currency to get prices in
PROVIDER_DIGIKEY_CURRENCY=EUR
# The language to get results in (en, de, fr, it, es, zh, ja, ko)
PROVIDER_DIGIKEY_LANGUAGE=en
# The country to get results for
PROVIDER_DIGIKEY_COUNTRY=DE
# Octopart / Nexar Provider:
# You can get your API key from https://nexar.com/api
PROVIDER_OCTOPART_CLIENT_ID=
PROVIDER_OCTOPART_SECRET=
# The currency and country to get prices for (you have to set both to get meaningful results)
# 3 letter ISO currency code (e.g. EUR, USD, GBP)
PROVIDER_OCTOPART_CURRENCY=EUR
# 2 letter ISO country code (e.g. DE, US, GB)
PROVIDER_OCTOPART_COUNTRY=DE
# The number of results to get from Octopart while searching (please note that this counts towards your API limits)
PROVIDER_OCTOPART_SEARCH_LIMIT=10
# Set to false to include non authorized offers in the results
PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS=1
##################################################################################
# EDA integration related settings
@ -129,6 +157,9 @@ NO_URL_REWRITE_AVAILABLE=0
# Set to 1, if Part-DB should redirect all HTTP requests to HTTPS. You dont need to configure this, if your webserver already does this.
REDIRECT_TO_HTTPS=0
# If you want to use fixer.io for currency conversion, you have to set this to your API key
FIXER_API_KEY=CHANGEME
# Override value if you want to show to show a given text on homepage.
# When this is empty the content of config/banner.md is used as banner
BANNER=""

View file

@ -33,10 +33,7 @@ export default class extends Controller {
{
let value = "";
if (this.unitValue) {
//Escape percentage signs
value = this.inputTarget.value.replace(/%/g, '\\%');
value = "\\mathrm{" + value + "}";
value = "\\mathrm{" + this.inputTarget.value + "}";
} else {
value = this.inputTarget.value;
}

View file

@ -85,9 +85,7 @@ export default class extends Controller
tmp += '<span>' + katex.renderToString(data.symbol) + '</span>'
}
if (data.unit) {
let unit = data.unit.replace(/%/g, '\\%');
unit = "\\mathrm{" + unit + "}";
tmp += '<span class="ms-2">' + katex.renderToString('[' + unit + ']') + '</span>'
tmp += '<span class="ms-2">' + katex.renderToString('[' + data.unit + ']') + '</span>'
}

View file

@ -30,7 +30,7 @@
"hshn/base64-encoded-file": "^5.0",
"jbtronics/2fa-webauthn": "^v2.2.0",
"jbtronics/dompdf-font-loader-bundle": "^1.0.0",
"jbtronics/settings-bundle": "^v2.6.0",
"jbtronics/settings-bundle": "dev-master",
"jfcherng/php-diff": "^6.14",
"knpuniversity/oauth2-client-bundle": "^2.15",
"league/csv": "^9.8.0",

541
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,8 +6,8 @@ knpu_oauth2_client:
type: generic
provider_class: '\League\OAuth2\Client\Provider\GenericProvider'
client_id: '%env(settings:digikey:clientId)%'
client_secret: '%env(settings:digikey:secret)%'
client_id: '%env(PROVIDER_DIGIKEY_CLIENT_ID)%'
client_secret: '%env(PROVIDER_DIGIKEY_SECRET)%'
redirect_route: 'oauth_client_check'
redirect_params: {name: 'ip_digikey_oauth'}
@ -26,8 +26,8 @@ knpu_oauth2_client:
type: generic
provider_class: '\League\OAuth2\Client\Provider\GenericProvider'
client_id: '%env(settings:octopart:clientId)%'
client_secret: '%env(settings:octopart:secret)%'
client_id: '%env(PROVIDER_OCTOPART_CLIENT_ID)%'
client_secret: '%env(PROVIDER_OCTOPART_SECRET)%'
redirect_route: 'oauth_client_check'
redirect_params: { name: 'ip_octopart_oauth' }

View file

@ -1,8 +1,2 @@
jbtronics_settings:
default_storage_adapter: Jbtronics\SettingsBundle\Storage\ORMStorageAdapter
cache:
default_cacheable: true
orm_storage:
default_entity_class: App\Entity\SettingsEntry
default_storage_adapter: Jbtronics\SettingsBundle\Storage\PHPFileStorageAdapter

View file

@ -6,5 +6,5 @@ florianv_swap:
providers:
european_central_bank: ~ # European Central Bank (only works for EUR base currency)
fixer: # Fixer.io (needs an API key)
access_key: "%env(string:default:settings:exchange_rate:fixerApiKey:INVALID)%"
access_key: "%env(FIXER_API_KEY)%"
#exchange_rates_api: ~

View file

@ -265,13 +265,17 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
# label: "perm.database.write_db_settings"
# alsoSet: ['read_db_settings', 'see_status']
config:
label: "perm.config"
group: "system"
operations:
change_system_settings:
label: "perm.config.change_system_settings"
apiTokenRole: ROLE_API_ADMIN
#config:
# label: "perm.config"
# group: "system"
# operations:
# read_config:
# label: "perm.config.read_config"
# edit_config:
# label: "perm.config.edit_config"
# alsoSet: 'read_config'
# server_info:
# label: "perm.config.server_info"
system:
label: "perm.system"

View file

@ -199,6 +199,23 @@ services:
arguments:
$providers: !tagged_iterator 'app.info_provider'
App\Services\InfoProviderSystem\Providers\DigikeyProvider:
arguments:
$clientId: '%env(string:PROVIDER_DIGIKEY_CLIENT_ID)%'
$currency: '%env(string:PROVIDER_DIGIKEY_CURRENCY)%'
$language: '%env(string:PROVIDER_DIGIKEY_LANGUAGE)%'
$country: '%env(string:PROVIDER_DIGIKEY_COUNTRY)%'
App\Services\InfoProviderSystem\Providers\OctopartProvider:
arguments:
$clientId: '&env(string:PROVIDER_OCTOPART_CLIENT_ID)%'
$secret: '%env(string:PROVIDER_OCTOPART_SECRET)%'
$country: '%env(string:PROVIDER_OCTOPART_COUNTRY)%'
$currency: '%env(string:PROVIDER_OCTOPART_CURRENCY)%'
$search_limit: '%env(int:PROVIDER_OCTOPART_SEARCH_LIMIT)%'
$onlyAuthorizedSellers: '%env(bool:PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS)%'
####################################################################################################################
# API system
####################################################################################################################

View file

@ -95,8 +95,6 @@ bundled with Part-DB. Set `DATABASE_MYSQL_SSL_VERIFY_CERT` if you want to accept
particularly for securing and protecting various aspects of your application. It's a secret key that is used for
cryptographic operations and security measures (session management, CSRF protection, etc..). Therefore this
value should be handled as confidential data and not shared publicly.
* `SHOW_PART_IMAGE_OVERLAY`: Set to 0 to disable the part image overlay, which appears if you hover over an image in the
part image gallery
### E-Mail settings

View file

@ -1,49 +0,0 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use App\Migration\AbstractMultiPlatformMigration;
use Doctrine\DBAL\Schema\Schema;
final class Version20250706201121 extends AbstractMultiPlatformMigration
{
public function getDescription(): string
{
return 'Add settings_entry table for storing settings';
}
public function mySQLUp(Schema $schema): void
{
$this->addSql('CREATE TABLE settings_entry (`key` VARCHAR(255) NOT NULL, `data` JSON DEFAULT NULL, id INT AUTO_INCREMENT NOT NULL, UNIQUE INDEX UNIQ_93F8DB394E645A7E (`key`), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci`');
}
public function mySQLDown(Schema $schema): void
{
$this->addSql('DROP TABLE settings_entry');
}
public function sqLiteUp(Schema $schema): void
{
$this->addSql('CREATE TABLE settings_entry ("key" VARCHAR(255) NOT NULL, "data" CLOB DEFAULT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)');
$this->addSql('CREATE UNIQUE INDEX UNIQ_93F8DB39F48571EB ON settings_entry ("key")');
}
public function sqLiteDown(Schema $schema): void
{
$this->addSql('DROP TABLE settings_entry');
}
public function postgreSQLUp(Schema $schema): void
{
$this->addSql('CREATE TABLE settings_entry ("key" VARCHAR(255) NOT NULL, "data" JSON DEFAULT NULL, id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL, PRIMARY KEY(id))');
$this->addSql('CREATE UNIQUE INDEX UNIQ_93F8DB39F48571EB ON settings_entry ("key")');
}
public function postgreSQLDown(Schema $schema): void
{
$this->addSql('DROP TABLE settings_entry');
}
}

View file

@ -40,8 +40,6 @@ class SettingsController extends AbstractController
#[Route("/settings", name: "system_settings")]
public function systemSettings(Request $request, TagAwareCacheInterface $cache): Response
{
$this->denyAccessUnlessGranted('@config.change_system_settings');
//Create a clone of the settings object
$settings = $this->settingsManager->createTemporaryCopy(AppSettings::class);
@ -64,6 +62,9 @@ class SettingsController extends AbstractController
$cache->invalidateTags(['tree_treeview', 'sidebar_tree_update']);
}
//Render the form
return $this->render('settings/settings.html.twig', [
'form' => $form

View file

@ -217,7 +217,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement implements Uniqu
$str = '';
$bracket_opened = false;
if ($this->value_typical !== null) {
if ($this->value_typical) {
$str .= $this->getValueTypicalWithUnit($latex_formatted);
if ($this->value_min || $this->value_max) {
$bracket_opened = true;
@ -225,11 +225,11 @@ abstract class AbstractParameter extends AbstractNamedDBElement implements Uniqu
}
}
if ($this->value_max !== null && $this->value_min !== null) {
if ($this->value_max && $this->value_min) {
$str .= $this->getValueMinWithUnit($latex_formatted).' ... '.$this->getValueMaxWithUnit($latex_formatted);
} elseif ($this->value_max !== null) {
} elseif ($this->value_max) {
$str .= 'max. '.$this->getValueMaxWithUnit($latex_formatted);
} elseif ($this->value_min !== null) {
} elseif ($this->value_min) {
$str .= 'min. '.$this->getValueMinWithUnit($latex_formatted);
}
@ -449,10 +449,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement implements Uniqu
if (!$with_latex) {
$unit = $this->unit;
} else {
//Escape the percentage sign for convenience (as latex uses it as comment and it is often used in units)
$escaped = preg_replace('/\\\\?%/', "\\\\%", $this->unit);
$unit = '$\mathrm{'.$escaped.'}$';
$unit = '$\mathrm{'.$this->unit.'}$';
}
return $str.' '.$unit;
@ -460,7 +457,7 @@ abstract class AbstractParameter extends AbstractNamedDBElement implements Uniqu
return $str;
}
/**
* Returns the class of the element that is allowed to be associated with this attachment.
* @return string

View file

@ -1,35 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Entity;
use Doctrine\DBAL\Types\Types;
use Jbtronics\SettingsBundle\Entity\AbstractSettingsORMEntry;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class SettingsEntry extends AbstractSettingsORMEntry
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column(type: Types::INTEGER)]
protected int $id;
}

View file

@ -241,49 +241,6 @@ class KiCadHelper
$result["fields"]["Part-DB IPN"] = $this->createField($part->getIpn());
}
// Add supplier information from orderdetails (include obsolete orderdetails)
if ($part->getOrderdetails(false)->count() > 0) {
$supplierCounts = [];
foreach ($part->getOrderdetails(false) as $orderdetail) {
if ($orderdetail->getSupplier() !== null && $orderdetail->getSupplierPartNr() !== '') {
$supplierName = $orderdetail->getSupplier()->getName();
$supplierName .= " SPN"; // Append "SPN" to the supplier name to indicate Supplier Part Number
if (!isset($supplierCounts[$supplierName])) {
$supplierCounts[$supplierName] = 0;
}
$supplierCounts[$supplierName]++;
// Create field name with sequential number if more than one from same supplier (e.g. "Mouser", "Mouser 2", etc.)
$fieldName = $supplierCounts[$supplierName] > 1
? $supplierName . ' ' . $supplierCounts[$supplierName]
: $supplierName;
$result["fields"][$fieldName] = $this->createField($orderdetail->getSupplierPartNr());
}
}
}
//Add fields for KiCost:
if ($part->getManufacturer() !== null) {
$result["fields"]["manf"] = $this->createField($part->getManufacturer()->getName());
}
if ($part->getManufacturerProductNumber() !== "") {
$result['fields']['manf#'] = $this->createField($part->getManufacturerProductNumber());
}
//For each supplier, add a field with the supplier name and the supplier part number for KiCost
if ($part->getOrderdetails(false)->count() > 0) {
foreach ($part->getOrderdetails(false) as $orderdetail) {
if ($orderdetail->getSupplier() !== null && $orderdetail->getSupplierPartNr() !== '') {
$fieldName = mb_strtolower($orderdetail->getSupplier()->getName()) . '#';
$result["fields"][$fieldName] = $this->createField($orderdetail->getSupplierPartNr());
}
}
}
return $result;
}

View file

@ -31,7 +31,6 @@ use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
use App\Services\OAuth\OAuthTokenManager;
use App\Settings\InfoProviderSystem\DigikeySettings;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class DigikeyProvider implements InfoProviderInterface
@ -56,16 +55,17 @@ class DigikeyProvider implements InfoProviderInterface
];
public function __construct(HttpClientInterface $httpClient, private readonly OAuthTokenManager $authTokenManager,
private readonly DigikeySettings $settings,)
private readonly string $currency, private readonly string $clientId,
private readonly string $language, private readonly string $country)
{
//Create the HTTP client with some default options
$this->digikeyClient = $httpClient->withOptions([
"base_uri" => self::BASE_URI,
"headers" => [
"X-DIGIKEY-Client-Id" => $this->settings->clientId,
"X-DIGIKEY-Locale-Site" => $this->settings->country,
"X-DIGIKEY-Locale-Language" => $this->settings->language,
"X-DIGIKEY-Locale-Currency" => $this->settings->currency,
"X-DIGIKEY-Client-Id" => $clientId,
"X-DIGIKEY-Locale-Site" => $this->country,
"X-DIGIKEY-Locale-Language" => $this->language,
"X-DIGIKEY-Locale-Currency" => $this->currency,
"X-DIGIKEY-Customer-Id" => 0,
]
]);
@ -101,7 +101,7 @@ class DigikeyProvider implements InfoProviderInterface
public function isActive(): bool
{
//The client ID has to be set and a token has to be available (user clicked connect)
return $this->settings->clientId !== '' && $this->authTokenManager->hasToken(self::OAUTH_APP_NAME);
return $this->clientId !== '' && $this->authTokenManager->hasToken(self::OAUTH_APP_NAME);
}
public function searchByKeyword(string $keyword): array
@ -268,7 +268,7 @@ class DigikeyProvider implements InfoProviderInterface
$prices = [];
foreach ($price_breaks as $price_break) {
$prices[] = new PriceDTO(minimum_discount_amount: $price_break['BreakQuantity'], price: (string) $price_break['UnitPrice'], currency_iso_code: $this->settings->currency);
$prices[] = new PriceDTO(minimum_discount_amount: $price_break['BreakQuantity'], price: (string) $price_break['UnitPrice'], currency_iso_code: $this->currency);
}
return [

View file

@ -30,7 +30,6 @@ use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Services\OAuth\OAuthTokenManager;
use App\Settings\InfoProviderSystem\OctopartSettings;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\HttpClient\HttpOptions;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@ -115,8 +114,9 @@ class OctopartProvider implements InfoProviderInterface
public function __construct(private readonly HttpClientInterface $httpClient,
private readonly OAuthTokenManager $authTokenManager, private readonly CacheItemPoolInterface $partInfoCache,
private readonly OctopartSettings $settings,
)
private readonly string $clientId, private readonly string $secret,
private readonly string $currency, private readonly string $country,
private readonly int $search_limit, private readonly bool $onlyAuthorizedSellers)
{
}
@ -183,7 +183,7 @@ class OctopartProvider implements InfoProviderInterface
{
//The client ID has to be set and a token has to be available (user clicked connect)
//return /*!empty($this->clientId) && */ $this->authTokenManager->hasToken(self::OAUTH_APP_NAME);
return $this->settings->clientId !== '' && $this->settings->secret !== '';
return $this->clientId !== '' && $this->secret !== '';
}
private function mapLifeCycleStatus(?string $value): ?ManufacturingStatus
@ -347,10 +347,10 @@ class OctopartProvider implements InfoProviderInterface
$result = $this->makeGraphQLCall($graphQL, [
'keyword' => $keyword,
'limit' => $this->settings->searchLimit,
'currency' => $this->settings->currency,
'country' => $this->settings->country,
'authorizedOnly' => $this->settings->onlyAuthorizedSellers,
'limit' => $this->search_limit,
'currency' => $this->currency,
'country' => $this->country,
'authorizedOnly' => $this->onlyAuthorizedSellers,
]);
$tmp = [];
@ -383,9 +383,9 @@ class OctopartProvider implements InfoProviderInterface
$result = $this->makeGraphQLCall($graphql, [
'ids' => [$id],
'currency' => $this->settings->currency,
'country' => $this->settings->country,
'authorizedOnly' => $this->settings->onlyAuthorizedSellers,
'currency' => $this->currency,
'country' => $this->country,
'authorizedOnly' => $this->onlyAuthorizedSellers,
]);
$tmp = $this->partResultToDTO($result['data']['supParts'][0]);

View file

@ -289,13 +289,6 @@ class ToolsTreeBuilder
))->setIcon('fa-fw fa-treeview fa-solid fa-database');
}
if ($this->security->isGranted('@config.change_system_settings')) {
$nodes[] = (new TreeViewNode(
$this->translator->trans('tree.tools.system.settings'),
$this->urlGenerator->generate('system_settings')
))->setIcon('fa fa-fw fa-gears fa-solid');
}
return $nodes;
}
}

View file

@ -179,7 +179,10 @@ class TreeViewGenerator
}
if (($mode === 'list_parts_root' || $mode === 'devices') && $this->rootNodeEnabled) {
$root_node = new TreeViewNode($this->entityClassToRootNodeString($class), $this->entityClassToRootNodeHref($class), $generic);
//We show the root node as a link to the list of all parts
$show_all_parts_url = $this->router->generate('parts_show_all');
$root_node = new TreeViewNode($this->entityClassToRootNodeString($class), $show_all_parts_url, $generic);
$root_node->setExpanded($this->rootNodeExpandedByDefault);
$root_node->setIcon($this->entityClassToRootNodeIcon($class));
@ -189,27 +192,6 @@ class TreeViewGenerator
return array_merge($head, $generic);
}
protected function entityClassToRootNodeHref(string $class): ?string
{
//If the root node should redirect to the new entity page, we return the URL for the new entity.
if ($this->sidebarSettings->rootNodeRedirectsToNewEntity) {
return match ($class) {
Category::class => $this->router->generate('category_new'),
StorageLocation::class => $this->router->generate('store_location_new'),
Footprint::class => $this->router->generate('footprint_new'),
Manufacturer::class => $this->router->generate('manufacturer_new'),
Supplier::class => $this->router->generate('supplier_new'),
Project::class => $this->router->generate('project_new'),
default => null,
};
}
return match ($class) {
Project::class => $this->router->generate('project_new'),
default => $this->router->generate('parts_show_all')
};
}
protected function entityClassToRootNodeString(string $class): string
{
return match ($class) {

View file

@ -105,9 +105,6 @@ class PermissionPresetsHelper
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'suppliers', PermissionData::ALLOW);
$this->permissionResolver->setAllOperationsOfPermission($perm_holder, 'projects', PermissionData::ALLOW);
//Allow to change system settings
$this->permissionResolver->setPermission($perm_holder, 'config', 'change_system_settings', PermissionData::ALLOW);
//Allow to manage Oauth tokens
$this->permissionResolver->setPermission($perm_holder, 'system', 'manage_oauth_tokens', PermissionData::ALLOW);
//Allow to show updates

View file

@ -37,7 +37,4 @@ class BehaviorSettings
#[EmbeddedSettings]
public ?TableSettings $table = null;
#[EmbeddedSettings]
public ?PartInfoSettings $partInfo = null;
}

View file

@ -1,43 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Settings\BehaviorSettings;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(name: "part_info", label: new TM("settings.behavior.part_info"))]
#[SettingsIcon('fa-circle-info')]
class PartInfoSettings
{
/**
* Whether to show the part image overlays in the part info view
* @var bool
*/
#[SettingsParameter(label: new TM("settings.behavior.part_info.show_part_image_overlay"), description: new TM("settings.behavior.part_info.show_part_image_overlay.help"),
envVar: "bool:SHOW_PART_IMAGE_OVERLAY", envVarMode: EnvVarMode::OVERWRITE)]
public bool $showPartImageOverlay = true;
}

View file

@ -67,10 +67,4 @@ class SidebarSettings
*/
#[SettingsParameter(label: new TM("settings.behavior.sidebar.rootNodeExpanded"))]
public bool $rootNodeExpanded = true;
/**
* @var bool Whether the root node should redirect to a new entity creation page when clicked.
*/
#[SettingsParameter(label: new TM("settings.behavior.sidebar.rootNodeRedirectsToNewEntity"))]
public bool $rootNodeRedirectsToNewEntity = false;
}

View file

@ -1,70 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(label: new TM("settings.ips.digikey"))]
#[SettingsIcon("fa-plug")]
class DigikeySettings
{
use SettingsTrait;
#[SettingsParameter(
label: new TM("settings.ips.digikey.client_id"),
envVar: "PROVIDER_DIGIKEY_CLIENT_ID", envVarMode: EnvVarMode::OVERWRITE
)]
public ?string $clientId = null;
#[SettingsParameter(
label: new TM("settings.ips.digikey.secret"),
envVar: "PROVIDER_DIGIKEY_SECRET", envVarMode: EnvVarMode::OVERWRITE
)]
public ?string $secret = null;
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class,
formOptions: ["preferred_choices" => ["EUR", "USD", "CHF", "GBP"]],
envVar: "PROVIDER_DIGIKEY_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Currency()]
public string $currency = "EUR";
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class,
envVar: "PROVIDER_DIGIKEY_COUNTRY", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Country]
public string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.tme.language"), formType: LanguageType::class,
envVar: "PROVIDER_DIGIKEY_LANGUAGE", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Language]
public string $language = "en";
}

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
@ -36,11 +35,9 @@ class Element14Settings
{
use SettingsTrait;
#[SettingsParameter(label: new TM("settings.ips.element14.apiKey"), description: new TM("settings.ips.element14.apiKey.help"),
formOptions: ["help_html" => true], envVar: "PROVIDER_ELEMENT14_KEY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.element14.apiKey"), description: new TM("settings.ips.element14.apiKey.help"), formOptions: ["help_html" => true], envVar: "PROVIDER_ELEMENT14_KEY")]
public ?string $apiKey = null;
#[SettingsParameter(label: new TM("settings.ips.element14.storeId"), description: new TM("settings.ips.element14.storeId.help"),
formOptions: ["help_html" => true], envVar: "PROVIDER_ELEMENT14_STORE_ID", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.element14.storeId"), description: new TM("settings.ips.element14.storeId.help"), formOptions: ["help_html" => true], envVar: "PROVIDER_ELEMENT14_STORE_ID")]
public string $storeId = "de.farnell.com";
}

View file

@ -32,9 +32,6 @@ class InfoProviderSettings
{
use SettingsTrait;
#[EmbeddedSettings]
public ?DigikeySettings $digikey = null;
#[EmbeddedSettings]
public ?MouserSettings $mouser = null;
@ -44,9 +41,6 @@ class InfoProviderSettings
#[EmbeddedSettings]
public ?Element14Settings $element14 = null;
#[EmbeddedSettings]
public ?OctopartSettings $octopartSettings = null;
#[EmbeddedSettings]
public ?LCSCSettings $lcsc = null;

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
@ -38,12 +37,10 @@ class LCSCSettings
{
use SettingsTrait;
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"),
envVar: "bool:PROVIDER_LCSC_ENABLED", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"), envVar: "bool:PROVIDER_LCSC_ENABLED")]
public bool $enabled = false;
#[SettingsParameter(label: new TM("settings.ips.lcsc.currency"), formType: CurrencyType::class,
envVar: "string:PROVIDER_LCSC_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.lcsc.currency"), formType: CurrencyType::class, envVar: "string:PROVIDER_LCSC_CURRENCY")]
#[Assert\Currency()]
public string $currency = 'EUR';
}

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Symfony\Component\Validator\Constraints as Assert;
@ -34,25 +33,22 @@ use Symfony\Component\Translation\TranslatableMessage as TM;
#[SettingsIcon("fa-plug")]
class MouserSettings
{
#[SettingsParameter(label: new TM("settings.ips.mouser.apiKey"), description: new TM("settings.ips.mouser.apiKey.help"),
formOptions: ["help_html" => true], envVar: "PROVIDER_MOUSER_KEY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.mouser.apiKey"), description: new TM("settings.ips.mouser.apiKey.help"), formOptions: ["help_html" => true], envVar: "PROVIDER_MOUSER_KEY")]
public ?string $apiKey = null;
/** @var int The number of results to get from Mouser while searching (please note that this value is max 50) */
#[SettingsParameter(label: new TM("settings.ips.mouser.searchLimit"), description: new TM("settings.ips.mouser.searchLimit.help"),
envVar: "int:PROVIDER_MOUSER_SEARCH_LIMIT", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.mouser.searchLimit"), description: new TM("settings.ips.mouser.searchLimit.help"), envVar: "int:PROVIDER_MOUSER_SEARCH_LIMIT")]
#[Assert\Range(min: 1, max: 50)]
public int $searchLimit = 50;
/** @var MouserSearchOptions Filter search results by RoHS compliance and stock availability */
#[SettingsParameter(label: new TM("settings.ips.mouser.searchOptions"), description: new TM("settings.ips.mouser.searchOptions.help"),
envVar: "PROVIDER_MOUSER_SEARCH_OPTION", envVarMode: EnvVarMode::OVERWRITE, envVarMapper: [self::class, "mapSearchOptionEnvVar"])]
#[SettingsParameter(label: new TM("settings.ips.mouser.searchOptions"), description: new TM("settings.ips.mouser.searchOptions.help"), envVar: "PROVIDER_MOUSER_SEARCH_OPTION", envVarMapper: [self::class, "mapSearchOptionEnvVar"])]
public MouserSearchOptions $searchOption = MouserSearchOptions::NONE;
/** @var bool It is recommended to leave this set to 'true'. The option is not really documented by Mouser:
* Used when searching for keywords in the language specified when you signed up for Search API. */
//TODO: Put this into some expert mode only
//#[SettingsParameter(envVar: "bool:PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE")]
#[SettingsParameter(envVar: "bool:PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE")]
public bool $searchWithSignUpLanguage = true;
public static function mapSearchOptionEnvVar(?string $value): MouserSearchOptions

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
@ -42,17 +41,14 @@ class OEMSecretsSettings
public const SUPPORTED_CURRENCIES = ["AUD", "CAD", "CHF", "CNY", "DKK", "EUR", "GBP", "HKD", "ILS", "INR", "JPY", "KRW", "NOK",
"NZD", "RUB", "SEK", "SGD", "TWD", "USD"];
#[SettingsParameter(label: new TM("settings.ips.element14.apiKey"),
envVar: "PROVIDER_OEMSECRETS_KEY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.element14.apiKey"), envVar: "PROVIDER_OEMSECRETS_KEY")]
public ?string $apiKey = null;
#[Assert\Country]
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class, formOptions: ["preferred_choices" => ["DE", "PL", "GB", "FR", "US"]],
envVar: "PROVIDER_OEMSECRETS_COUNTRY_CODE", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class, formOptions: ["preferred_choices" => ["DE", "PL", "GB", "FR", "US"]], envVar: "PROVIDER_OEMSECRETS_COUNTRY_CODE")]
public ?string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class, formOptions: ["preferred_choices" => self::SUPPORTED_CURRENCIES],
envVar: "PROVIDER_OEMSECRETS_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class, formOptions: ["preferred_choices" => self::SUPPORTED_CURRENCIES], envVar: "PROVIDER_OEMSECRETS_CURRENCY")]
#[Assert\Choice(choices: self::SUPPORTED_CURRENCIES)]
public string $currency = "EUR";
@ -60,8 +56,7 @@ class OEMSecretsSettings
* @var bool If this is enabled, distributors with zero prices
* will be discarded from the creation of a new part
*/
#[SettingsParameter(label: new TM("settings.ips.oemsecrets.keepZeroPrices"), description: new TM("settings.ips.oemsecrets.keepZeroPrices.help"),
envVar: "bool:PROVIDER_OEMSECRETS_ZERO_PRICE", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.oemsecrets.keepZeroPrices"), description: new TM("settings.ips.oemsecrets.keepZeroPrices.help"), envVar: "bool:PROVIDER_OEMSECRETS_ZERO_PRICE")]
public bool $keepZeroPrices = false;
/**
@ -69,8 +64,7 @@ class OEMSecretsSettings
* # from the description transforming unstructured descriptions into structured parameters;
* # each parameter in description should have the form: "...;name1:value1;name2:value2"
*/
#[SettingsParameter(label: new TM("settings.ips.oemsecrets.parseParams"), description: new TM("settings.ips.oemsecrets.parseParams.help"),
envVar: "bool:PROVIDER_OEMSECRETS_SET_PARAM", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.oemsecrets.parseParams"), description: new TM("settings.ips.oemsecrets.parseParams.help"), envVar: "bool:PROVIDER_OEMSECRETS_SET_PARAM")]
public bool $parseParams = true;
#[SettingsParameter(label: new TM("settings.ips.oemsecrets.sortMode"), envVar: "PROVIDER_OEMSECRETS_SORT_CRITERIA", envVarMapper: [self::class, "mapSortModeEnvVar"])]

View file

@ -1,78 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Form\Extension\Core\Type\CountryType;
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(label: new TM("settings.ips.octopart"))]
#[SettingsIcon("fa-plug")]
class OctopartSettings
{
use SettingsTrait;
#[SettingsParameter(
label: new TM("settings.ips.digikey.client_id"),
envVar: "PROVIDER_OCTOPART_CLIENT_ID", envVarMode: EnvVarMode::OVERWRITE
)]
public ?string $clientId = null;
#[SettingsParameter(
label: new TM("settings.ips.digikey.secret"),
envVar: "PROVIDER_OCTOPART_SECRET", envVarMode: EnvVarMode::OVERWRITE
)]
public ?string $secret = null;
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class,
formOptions: ["preferred_choices" => ["EUR", "USD", "CHF", "GBP"]],
envVar: "PROVIDER_OCTOPART_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Currency()]
public string $currency = "EUR";
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class,
envVar: "PROVIDER_OCTOPART_COUNTRY", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Country]
public string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.octopart.searchLimit"), description: new TM("settings.ips.octopart.searchLimit.help"),
formType: NumberType::class, formOptions: ["attr" => ["min" => 1, "max" => 100]],
envVar: "int:PROVIDER_OCTOPART_SEARCH_LIMIT", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Range(min: 1, max: 100)]
public int $searchLimit = 10;
#[SettingsParameter(label: new TM("settings.ips.octopart.onlyAuthorizedSellers"),
description: new TM("settings.ips.octopart.onlyAuthorizedSellers.help"),
envVar: "bool:PROVIDER_OCTOPART_ONLY_AUTHORIZED_SELLERS", envVarMode: EnvVarMode::OVERWRITE
)]
public bool $onlyAuthorizedSellers = true;
}

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Symfony\Component\Translation\TranslatableMessage as TM;
@ -33,7 +32,6 @@ use Symfony\Component\Translation\TranslatableMessage as TM;
#[SettingsIcon("fa-plug")]
class PollinSettings
{
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"),
envVar: "bool:PROVIDER_POLLIN_ENABLED", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"), envVar: "bool:PROVIDER_POLLIN_ENABLED")]
public bool $enabled = false;
}

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
@ -42,27 +41,22 @@ class ReicheltSettings
public const SUPPORTED_LANGUAGE = ["en", "de", "fr", "nl", "pl", "it", "es"];
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"),
envVar: "bool:PROVIDER_REICHELT_ENABLED", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"), envVar: "bool:PROVIDER_REICHELT_ENABLED")]
public bool $enabled = false;
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class, formOptions: ["preferred_choices" => ["EUR"]],
envVar: "PROVIDER_REICHELT_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class, formOptions: ["preferred_choices" => ["EUR"]], envVar: "PROVIDER_REICHELT_CURRENCY")]
public string $currency = "EUR";
#[SettingsParameter(label: new TM("settings.ips.tme.language"), formType: LanguageType::class, formOptions: ["preferred_choices" => self::SUPPORTED_LANGUAGE],
envVar: "PROVIDER_REICHELT_LANGUAGE", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.language"), formType: LanguageType::class, formOptions: ["preferred_choices" => self::SUPPORTED_LANGUAGE], envVar: "PROVIDER_REICHELT_LANGUAGE")]
#[Assert\Language()]
#[Assert\Choice(choices: self::SUPPORTED_LANGUAGE)]
public string $language = "en";
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class, formOptions: ["preferred_choices" => ["DE", "PL", "GB", "FR"]],
envVar: "PROVIDER_REICHELT_COUNTRY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.country"), envVar: "PROVIDER_REICHELT_COUNTRY", formType: CountryType::class, formOptions: ["preferred_choices" => ["DE", "PL", "GB", "FR"]])]
#[Assert\Country]
public string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.reichelt.include_vat"),
envVar: "bool:PROVIDER_REICHELT_INCLUDE_VAT", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.reichelt.include_vat"), envVar: "bool:PROVIDER_REICHELT_INCLUDE_VAT")]
public bool $includeVAT = true;
}

View file

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace App\Settings\InfoProviderSystem;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
@ -43,30 +42,24 @@ class TMESettings
private const SUPPORTED_CURRENCIES = ["EUR", "USD", "PLN", "GBP"];
#[SettingsParameter(label: new TM("settings.ips.tme.token"),
description: new TM("settings.ips.tme.token.help"), formOptions: ["help_html" => true],
envVar: "PROVIDER_TME_KEY", envVarMode: EnvVarMode::OVERWRITE)]
description: new TM("settings.ips.tme.token.help"), formOptions: ["help_html" => true], envVar: "PROVIDER_TME_KEY")]
public ?string $apiToken = null;
#[SettingsParameter(label: new TM("settings.ips.tme.secret"),
envVar: "PROVIDER_TME_SECRET", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.secret"), envVar: "PROVIDER_TME_SECRET")]
public ?string $apiSecret = null;
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class, formOptions: ["preferred_choices" => self::SUPPORTED_CURRENCIES],
envVar: "PROVIDER_TME_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.currency"), formType: CurrencyType::class, formOptions: ["preferred_choices" => self::SUPPORTED_CURRENCIES], envVar: "PROVIDER_TME_CURRENCY")]
#[Assert\Choice(choices: self::SUPPORTED_CURRENCIES)]
public string $currency = "EUR";
#[SettingsParameter(label: new TM("settings.ips.tme.language"), formType: LanguageType::class, formOptions: ["preferred_choices" => ["en", "de", "fr", "pl"]],
envVar: "PROVIDER_TME_LANGUAGE", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.language"), formType: LanguageType::class, formOptions: ["preferred_choices" => ["en", "de", "fr", "pl"]], envVar: "PROVIDER_TME_LANGUAGE")]
#[Assert\Language]
public string $language = "en";
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class, formOptions: ["preferred_choices" => ["DE", "PL", "GB", "FR"]],
envVar: "PROVIDER_TME_COUNTRY", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.country"), formType: CountryType::class, formOptions: ["preferred_choices" => ["DE", "PL", "GB", "FR"]], envVar: "PROVIDER_TME_COUNTRY")]
#[Assert\Country]
public string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.tme.grossPrices"),
envVar: "bool:PROVIDER_TME_GET_GROSS_PRICES", envVarMode: EnvVarMode::OVERWRITE)]
#[SettingsParameter(label: new TM("settings.ips.tme.grossPrices"), envVar: "bool:PROVIDER_TME_GET_GROSS_PRICES")]
public bool $grossPrices = true;
}

View file

@ -1,41 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2025 Jan Böhmer (https://github.com/jbtronics)
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace App\Settings\MiscSettings;
use App\Settings\SettingsIcon;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(name: "exchange_rate", label: new TM("settings.misc.exchange_rate"))]
#[SettingsIcon("fa-money-bill-transfer")]
class ExchangeRateSettings
{
#[SettingsParameter(label: new TM("settings.misc.exchange_rate.fixer_api_key"),
description: new TM("settings.misc.exchange_rate.fixer_api_key.help"),
envVar: "FIXER_API_KEY", envVarMode: EnvVarMode::OVERWRITE,
)]
public ?string $fixerApiKey = null;
}

View file

@ -31,7 +31,4 @@ class MiscSettings
{
#[EmbeddedSettings]
public ?KiCadEDASettings $kicadEDA = null;
#[EmbeddedSettings]
public ?ExchangeRateSettings $exchangeRate = null;
}

View file

@ -70,7 +70,6 @@ class HistorySettings
description: new TM("settings.system.history.enforceComments.description"),
options: ['type' => EnumType::class, 'nullable' => false, 'options' => ['class' => EventCommentType::class]],
formType: EnforceEventCommentTypesType::class,
formOptions: ['required' => false, "empty_data" => []],
envVar: "ENFORCE_CHANGE_COMMENTS_FOR", envVarMode: EnvVarMode::OVERWRITE, envVarMapper: [self::class, 'mapEnforceComments']
)]
public array $enforceComments = [];

View file

@ -13,7 +13,6 @@
<div class="carousel-item {% if loop.first %}active{% endif %}">
<a href="{{ entity_url(pic, 'file_view') }}" data-turbo="false" target="_blank" rel="noopener">
<img class="d-block w-100 img-fluid img-thumbnail bg-light part-info-image" src="{{ entity_url(pic, 'file_view') }}" alt="">
{% if settings_instance("part_info").showPartImageOverlay %}
<div class="mask"></div>
<div class="carousel-caption-hover">
<div class="carousel-caption text-white">
@ -22,7 +21,6 @@
<div>{{ entity_type_label(pic.element) }}</div>
</div>
</div>
{% endif %}
</a>
</div>
{% endfor %}

View file

@ -12922,107 +12922,5 @@ Please note, that you can not impersonate a disabled user. If you try you will g
<target>Pollin.de offers no official API, so this info provider webscrapes the website to extract info. It could break at any time, use it at your own risk.</target>
</segment>
</unit>
<unit id="TEm7uIg" name="settings.behavior.sidebar.rootNodeRedirectsToNewEntity">
<segment>
<source>settings.behavior.sidebar.rootNodeRedirectsToNewEntity</source>
<target>Root nodes redirect to new entity pages</target>
</segment>
</unit>
<unit id="j7HiQ80" name="settings.ips.digikey">
<segment>
<source>settings.ips.digikey</source>
<target>Digikey</target>
</segment>
</unit>
<unit id="_ViyVdh" name="settings.ips.digikey.client_id">
<segment>
<source>settings.ips.digikey.client_id</source>
<target>Client ID</target>
</segment>
</unit>
<unit id="eB9dDyp" name="settings.ips.digikey.secret">
<segment>
<source>settings.ips.digikey.secret</source>
<target>Secret</target>
</segment>
</unit>
<unit id="5xjmMzf" name="settings.ips.octopart">
<segment>
<source>settings.ips.octopart</source>
<target>Octopart / Nexar</target>
</segment>
</unit>
<unit id="vGv90iO" name="settings.ips.octopart.searchLimit">
<segment>
<source>settings.ips.octopart.searchLimit</source>
<target>Number of results</target>
</segment>
</unit>
<unit id="au4Yeps" name="settings.ips.octopart.searchLimit.help">
<segment>
<source>settings.ips.octopart.searchLimit.help</source>
<target>The number of results to get from Octopart while searching (please note that this counts towards your API limits)</target>
</segment>
</unit>
<unit id="Tiqmk.8" name="settings.ips.octopart.onlyAuthorizedSellers">
<segment>
<source>settings.ips.octopart.onlyAuthorizedSellers</source>
<target>Only authorized sellers</target>
</segment>
</unit>
<unit id="ECQkeJy" name="settings.ips.octopart.onlyAuthorizedSellers.help">
<segment>
<source>settings.ips.octopart.onlyAuthorizedSellers.help</source>
<target>Set to false to include non-authorized offers in the results</target>
</segment>
</unit>
<unit id="iRDDtdU" name="settings.misc.exchange_rate">
<segment>
<source>settings.misc.exchange_rate</source>
<target>Money exchange rates</target>
</segment>
</unit>
<unit id="0REngfi" name="settings.misc.exchange_rate.fixer_api_key">
<segment>
<source>settings.misc.exchange_rate.fixer_api_key</source>
<target>Fixer.io API Key</target>
</segment>
</unit>
<unit id="COLhoWD" name="settings.misc.exchange_rate.fixer_api_key.help">
<segment>
<source>settings.misc.exchange_rate.fixer_api_key.help</source>
<target>If you need exchange rates between non-euro currencies, you can input an API key from fixer.io here.</target>
</segment>
</unit>
<unit id="Ffr5xYM" name="settings.behavior.part_info">
<segment>
<source>settings.behavior.part_info</source>
<target>Part info page</target>
</segment>
</unit>
<unit id="weH3j.a" name="settings.behavior.part_info.show_part_image_overlay">
<segment>
<source>settings.behavior.part_info.show_part_image_overlay</source>
<target>Show image overlay</target>
</segment>
</unit>
<unit id="SCUs3WS" name="settings.behavior.part_info.show_part_image_overlay.help">
<segment>
<source>settings.behavior.part_info.show_part_image_overlay.help</source>
<target>Show the image overlay with attachment details on hovering over the part image gallery.</target>
</segment>
</unit>
<unit id="ALfPkeR" name="perm.config.change_system_settings">
<segment>
<source>perm.config.change_system_settings</source>
<target>Change system settings</target>
</segment>
</unit>
<unit id="TlHeIjk" name="tree.tools.system.settings">
<segment>
<source>tree.tools.system.settings</source>
<target>System settings</target>
</segment>
</unit>
</file>
</xliff>