From 14cc0b9e9ad7828102ef67eb2e1e5e83967d9762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 17:53:12 +0200 Subject: [PATCH 01/11] New translations messages.en.xlf (German) (#1028) --- translations/messages.de.xlf | 384 +++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) diff --git a/translations/messages.de.xlf b/translations/messages.de.xlf index b579d908..8515abb8 100644 --- a/translations/messages.de.xlf +++ b/translations/messages.de.xlf @@ -13056,5 +13056,389 @@ Bitte beachten Sie, dass Sie sich nicht als deaktivierter Benutzer ausgeben kön Aus Sicherheitsgründen ausgeblendet + + + project.bom_import.map_fields + Spalten zuordnen + + + + + project.bom_import.map_fields.help + Wählen Sie aus, wie CSV Spalten auf BOM Felder gemappt werden + + + + + project.bom_import.delimiter + Trennzeichen + + + + + project.bom_import.delimiter.comma + Komma (,) + + + + + project.bom_import.delimiter.semicolon + Semikolon (;) + + + + + project.bom_import.delimiter.tab + Tab + + + + + project.bom_import.field_mapping.title + Spaltenzuordnung + + + + + project.bom_import.field_mapping.csv_field + CSV Spalte + + + + + project.bom_import.field_mapping.maps_to + Mappt auf + + + + + project.bom_import.field_mapping.suggestion + Vorschlag + + + + + project.bom_import.field_mapping.priority + Priorität + + + + + project.bom_import.field_mapping.priority_help + Priorität (kleinere Nummer = höhere Priorität) + + + + + project.bom_import.field_mapping.priority_short + P + + + + + project.bom_import.field_mapping.priority_note + Prioritätstipp: Niedrigere Zahlen = höhere Priorität. Die Standardpriorität ist 10. Verwenden Sie die Prioritäten 1–9 für die wichtigsten Felder und 10+ für normale Priorität. + + + + + project.bom_import.field_mapping.summary + Zusammenfassung der Zuordnung + + + + + project.bom_import.field_mapping.select_to_see_summary + Wählen Sie Zuordnungen aus, um eine Zusammenfassung anzuzeigen. + + + + + project.bom_import.field_mapping.no_suggestion + Kein Vorschlag + + + + + project.bom_import.preview + Vorschau + + + + + project.bom_import.flash.session_expired + Die Import-Sitzung ist abgelaufen. Bitte laden Sie Ihre Datei erneut hoch. + + + + + project.bom_import.field_mapping.ignore + Ignorieren + + + + + project.bom_import.type.kicad_schematic + KiCAD Schaltplaneditor BOM (CSV Datei) + + + + + common.back + Zurück + + + + + project.bom_import.validation.errors.required_field_missing + Zeile %line%: Das Pflichtfeld „%field%“ fehlt oder ist leer. Bitte stellen Sie sicher, dass dieses Feld zugeordnet ist und Daten enthält. + + + + + project.bom_import.validation.errors.no_valid_designators + Zeile %line%: Das Bezeichnungsfeld enthält keine gültigen Komponentenreferenzen. Erwartetes Format: „R1,C2,U3“ oder „R1, C2, U3“. + + + + + project.bom_import.validation.warnings.unusual_designator_format + Zeile %line%: Einige Komponentenreferenzen haben möglicherweise ein ungewöhnliches Format: %designators%. Erwartetes Format: „R1“, „C2“, „U3“ usw. + + + + + project.bom_import.validation.errors.duplicate_designators + Zeile %line%: Doppelte Komponentenreferenzen gefunden: %designators%. Jede Komponente sollte nur einmal pro Zeile referenziert werden. + + + + + project.bom_import.validation.errors.invalid_quantity + Zeile %line%: Die Menge „%quantity%“ ist keine gültige Zahl. Bitte geben Sie einen numerischen Wert ein (z. B. 1, 2,5, 10). + + + + + project.bom_import.validation.errors.quantity_zero_or_negative + Zeile %line%: Die Menge muss größer als 0 sein, erhaltene Menge %quantity%. + + + + + project.bom_import.validation.warnings.quantity_unusually_high + Zeile %line%: Die Menge %quantity% erscheint ungewöhnlich hoch. Bitte überprüfen Sie, ob dies korrekt ist. + + + + + project.bom_import.validation.warnings.quantity_not_whole_number + Zeile %line%: Die Menge %quantity% ist keine ganze Zahl, aber Sie haben %count% Komponentenreferenzen. Dies kann auf eine Nichtübereinstimmung hindeuten. + + + + + project.bom_import.validation.errors.quantity_designator_mismatch + Zeile %line%: Diskrepanz zwischen Menge und Komponentenreferenzen. Menge: %quantity%, Referenzen: %count% (%designators%). Diese sollten übereinstimmen. Passen Sie entweder die Menge an oder überprüfen Sie Ihre Komponentenreferenzen. + + + + + project.bom_import.validation.errors.invalid_partdb_id + Zeile %line%: Part-DB ID „%id%“ ist keine gültige Zahl. Bitte geben Sie eine numerische ID ein. + + + + + project.bom_import.validation.errors.partdb_id_zero_or_negative + Zeile %line%: Die Part-DB ID muss größer als 0 sein, erhaltene ID lautet %id%. + + + + + project.bom_import.validation.warnings.partdb_id_not_found + Zeile %line%: Teil-DB-ID %id% nicht in der Datenbank gefunden. Die Komponente wird ohne Verknüpfung mit einem vorhandenen Teil importiert. + + + + + project.bom_import.validation.info.partdb_link_success + Zeile %line%: Erfolgreich mit dem Bauteil „%name%“ (ID: %id%) verknüpft. + + + + + project.bom_import.validation.warnings.no_component_name + Zeile %line%: Kein Komponentenname/keine Komponentenbezeichnung angegeben (MPN, Bezeichnung oder Wert). Die Komponente wird als „Unbekanntes Bauteil” bezeichnet. + + + + + project.bom_import.validation.warnings.package_name_too_long + Zeile %line%: Der Footprintname „%package%“ ist ungewöhnlich lang. Bitte überprüfen Sie, ob er korrekt ist. + + + + + project.bom_import.validation.info.library_prefix_detected + Zeile %line%: Das Footprint „%package%“ enthält ein Bibliothekspräfix. Dieses wird beim Import automatisch entfernt. + + + + + project.bom_import.validation.errors.non_numeric_field + Zeile %line%: Das Feld „%field%“ enthält den nicht numerischen Wert „%value%“. Bitte geben Sie eine gültige Zahl ein. + + + + + project.bom_import.validation.info.import_summary + Importübersicht: %total% Einträge insgesamt, %valid% gültig, %invalid% mit Problemen. + + + + + project.bom_import.validation.errors.summary + Es wurden %count% Validierungsfehler gefunden, die behoben werden müssen, bevor der Import fortgesetzt werden kann. + + + + + project.bom_import.validation.warnings.summary + Es wurden %count% Warnungen gefunden. Bitte überprüfen Sie diese Probleme, bevor Sie fortfahren. + + + + + project.bom_import.validation.info.all_valid + Alle Einträge haben die Validierung erfolgreich bestanden! + + + + + project.bom_import.validation.summary + Validierungsübersicht + + + + + project.bom_import.validation.total_entries + Gesamtzahl der Einträge + + + + + project.bom_import.validation.valid_entries + Gültige Einträge + + + + + project.bom_import.validation.invalid_entries + Ungültige Einträge + + + + + project.bom_import.validation.success_rate + Erfolgsquote + + + + + project.bom_import.validation.errors.title + Validierungsfehler + + + + + project.bom_import.validation.errors.description + Die folgenden Fehler müssen behoben werden, bevor der Import fortgesetzt werden kann: + + + + + project.bom_import.validation.warnings.title + Validierungswarnungen + + + + + project.bom_import.validation.warnings.description + Die folgenden Warnhinweise sollten vor dem Fortfahren gelesen werden: + + + + + project.bom_import.validation.info.title + Informationen + + + + + project.bom_import.validation.details.title + Detaillierte Validierungsergebnisse + + + + + project.bom_import.validation.details.line + Zeile + + + + + project.bom_import.validation.details.status + Status + + + + + project.bom_import.validation.details.messages + Meldungen + + + + + project.bom_import.validation.details.valid + Gültig + + + + + project.bom_import.validation.details.invalid + Ungültig + + + + + project.bom_import.validation.all_valid + Alle Einträge sind gültig und bereit zum Import! + + + + + project.bom_import.validation.fix_errors + Bitte beheben Sie die Validierungsfehler, bevor Sie mit dem Import fortfahren. + + + + + project.bom_import.type.generic_csv + Generische CSV-Datei + + + + + label_generator.update_profile + Profil mit aktuellen Einstellungen aktualisieren + + + + + label_generator.profile_updated + Labelprofil aktualisiert + + From 71629a696cef2c1ca47249fd30580996561819a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 17:55:55 +0200 Subject: [PATCH 02/11] Use updated gnu unifont --- composer.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/composer.lock b/composer.lock index 6de15830..1f67b80f 100644 --- a/composer.lock +++ b/composer.lock @@ -7513,16 +7513,16 @@ }, { "name": "part-db/label-fonts", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/Part-DB/label-fonts.git", - "reference": "77c84b70ed3bb005df15f30ff835ddec490394b9" + "reference": "c85aeb051d6492961a2c59bc291979f15ce60e88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Part-DB/label-fonts/zipball/77c84b70ed3bb005df15f30ff835ddec490394b9", - "reference": "77c84b70ed3bb005df15f30ff835ddec490394b9", + "url": "https://api.github.com/repos/Part-DB/label-fonts/zipball/c85aeb051d6492961a2c59bc291979f15ce60e88", + "reference": "c85aeb051d6492961a2c59bc291979f15ce60e88", "shasum": "" }, "type": "library", @@ -7545,9 +7545,9 @@ ], "support": { "issues": "https://github.com/Part-DB/label-fonts/issues", - "source": "https://github.com/Part-DB/label-fonts/tree/v1.1.0" + "source": "https://github.com/Part-DB/label-fonts/tree/v1.2.0" }, - "time": "2024-02-08T21:44:38+00:00" + "time": "2025-09-07T15:42:51+00:00" }, { "name": "part-db/swap", @@ -17883,16 +17883,16 @@ }, { "name": "phpstan/phpstan-doctrine", - "version": "2.0.4", + "version": "2.0.5", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-doctrine.git", - "reference": "6271e66ce37545bd2edcddbe6bcbdd3b665ab7b8" + "reference": "eeff19808f8ae3a6f7c4e43e388a2848eb2b0865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/6271e66ce37545bd2edcddbe6bcbdd3b665ab7b8", - "reference": "6271e66ce37545bd2edcddbe6bcbdd3b665ab7b8", + "url": "https://api.github.com/repos/phpstan/phpstan-doctrine/zipball/eeff19808f8ae3a6f7c4e43e388a2848eb2b0865", + "reference": "eeff19808f8ae3a6f7c4e43e388a2848eb2b0865", "shasum": "" }, "require": { @@ -17949,9 +17949,9 @@ "description": "Doctrine extensions for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-doctrine/issues", - "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.4" + "source": "https://github.com/phpstan/phpstan-doctrine/tree/2.0.5" }, - "time": "2025-07-17T11:57:55+00:00" + "time": "2025-09-07T11:52:30+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -18003,16 +18003,16 @@ }, { "name": "phpstan/phpstan-symfony", - "version": "2.0.7", + "version": "2.0.8", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-symfony.git", - "reference": "392f7ab8f52a0a776977be4e62535358c28e1b15" + "reference": "8820c22d785c235f69bb48da3d41e688bc8a1796" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/392f7ab8f52a0a776977be4e62535358c28e1b15", - "reference": "392f7ab8f52a0a776977be4e62535358c28e1b15", + "url": "https://api.github.com/repos/phpstan/phpstan-symfony/zipball/8820c22d785c235f69bb48da3d41e688bc8a1796", + "reference": "8820c22d785c235f69bb48da3d41e688bc8a1796", "shasum": "" }, "require": { @@ -18068,9 +18068,9 @@ "description": "Symfony Framework extensions and rules for PHPStan", "support": { "issues": "https://github.com/phpstan/phpstan-symfony/issues", - "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.7" + "source": "https://github.com/phpstan/phpstan-symfony/tree/2.0.8" }, - "time": "2025-07-22T09:40:57+00:00" + "time": "2025-09-07T06:55:50+00:00" }, { "name": "phpunit/php-code-coverage", From 4b00697f02346d223de342a4e30f7cfe41c2c793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 19:27:02 +0200 Subject: [PATCH 03/11] Allow to customize which items get shown on the homepage and in which order This fixes issue #470 and #894 --- .../SystemSettings/CustomizationSettings.php | 27 ++++++++-- src/Settings/SystemSettings/HomepageItems.php | 51 +++++++++++++++++++ templates/homepage.html.twig | 44 +++++++++++----- translations/messages.en.xlf | 12 +++++ 4 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 src/Settings/SystemSettings/HomepageItems.php diff --git a/src/Settings/SystemSettings/CustomizationSettings.php b/src/Settings/SystemSettings/CustomizationSettings.php index d7e92a51..a5f40cdf 100644 --- a/src/Settings/SystemSettings/CustomizationSettings.php +++ b/src/Settings/SystemSettings/CustomizationSettings.php @@ -28,10 +28,13 @@ use App\Form\Type\ThemeChoiceType; use App\Settings\SettingsIcon; use App\Validator\Constraints\ValidTheme; use Jbtronics\SettingsBundle\Metadata\EnvVarMode; +use Jbtronics\SettingsBundle\ParameterTypes\ArrayType; +use Jbtronics\SettingsBundle\ParameterTypes\EnumType; use Jbtronics\SettingsBundle\Settings\Settings; use Jbtronics\SettingsBundle\Settings\SettingsParameter; use Jbtronics\SettingsBundle\Settings\SettingsTrait; use Symfony\Component\Translation\TranslatableMessage as TM; +use Symfony\Component\Validator\Constraints as Assert; #[Settings(name: "customization", label: new TM("settings.system.customization"))] #[SettingsIcon("fa-paint-roller")] @@ -46,6 +49,13 @@ class CustomizationSettings )] public string $instanceName = "Part-DB"; + #[SettingsParameter( + label: new TM("settings.system.customization.theme"), + formType: ThemeChoiceType::class, formOptions: ['placeholder' => false] + )] + #[ValidTheme] + public string $theme = 'bootstrap'; + #[SettingsParameter( label: new TM("settings.system.customization.banner"), formType: RichTextEditorType::class, formOptions: ['mode' => 'markdown-full'], @@ -53,10 +63,17 @@ class CustomizationSettings )] public ?string $banner = null; - #[SettingsParameter( - label: new TM("settings.system.customization.theme"), - formType: ThemeChoiceType::class, formOptions: ['placeholder' => false] + /** + * @var HomepageItems[] The items to show in the sidebar. + */ + #[SettingsParameter(ArrayType::class, + label: new TM("settings.behavior.hompepage.items"), + description: new TM("settings.behavior.homepage.items.help"), + options: ['type' => EnumType::class, 'options' => ['class' => HomepageItems::class]], + formType: \Symfony\Component\Form\Extension\Core\Type\EnumType::class, + formOptions: ['class' => HomepageItems::class, 'multiple' => true, 'ordered' => true] )] - #[ValidTheme] - public string $theme = 'bootstrap'; + #[Assert\NotBlank()] + #[Assert\Unique()] + public array $homepageitems = [HomepageItems::SEARCH, HomepageItems::BANNER, HomepageItems::FIRST_STEPS, HomepageItems::LICENSE, HomepageItems::LAST_ACTIVITY]; } diff --git a/src/Settings/SystemSettings/HomepageItems.php b/src/Settings/SystemSettings/HomepageItems.php new file mode 100644 index 00000000..7366dfa2 --- /dev/null +++ b/src/Settings/SystemSettings/HomepageItems.php @@ -0,0 +1,51 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\SystemSettings; + +use Symfony\Contracts\Translation\TranslatableInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function Symfony\Component\Translation\t; + +enum HomepageItems: string implements TranslatableInterface +{ + case SEARCH = 'search'; + case BANNER = 'banner'; + case LICENSE = 'license'; + case FIRST_STEPS = 'first_steps'; + case LAST_ACTIVITY = 'last_activity'; + + public function trans(TranslatorInterface $translator, ?string $locale = null): string + { + $key = match($this) { + self::SEARCH => 'search.placeholder', + self::BANNER => 'settings.system.customization.banner', + self::LICENSE => 'homepage.license', + self::FIRST_STEPS => 'homepage.first_steps.title', + self::LAST_ACTIVITY => 'homepage.last_activity', + }; + + return $translator->trans($key, locale: $locale); + } +} diff --git a/templates/homepage.html.twig b/templates/homepage.html.twig index 3f820a53..0db7cf17 100644 --- a/templates/homepage.html.twig +++ b/templates/homepage.html.twig @@ -4,18 +4,13 @@ {% import "components/search.macro.html.twig" as search %} {% import "vars.macro.twig" as vars %} -{% block content %} - - {% if is_granted('@system.show_updates') %} - {{ nv.new_version_alert(new_version_available, new_version, new_version_url) }} - {% endif %} - +{% block item_search %} {% if is_granted('@parts.read') %} {{ search.search_form("standalone") }} -
{% endif %} +{% endblock %} - +{% block item_banner %}

{{ vars.partdb_title() }}

@@ -31,9 +26,11 @@

{% endif %} +{% endblock %} +{% block item_first_steps %} {% if show_first_steps %} -
+

{% trans %}homepage.first_steps.title{% endtrans %}

@@ -51,8 +48,10 @@
{% endif %} +{% endblock %} -
+{% block item_license %} +

{% trans %}homepage.license{% endtrans %}

@@ -68,9 +67,11 @@ {% trans %}homepage.forum.caption{% endtrans %}: {% trans with {'%href%': 'https://github.com/Part-DB/Part-DB-server/discussions'}%}homepage.forum.text{% endtrans %}
+{% endblock %} +{% block item_last_activity %} {% if datatable is not null %} -
+
{% trans %}homepage.last_activity{% endtrans %}
{% import "components/history_log_macros.html.twig" as log %} @@ -78,4 +79,23 @@
{% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} + +{% block content %} + + {% if is_granted('@system.show_updates') %} + {{ nv.new_version_alert(new_version_available, new_version, new_version_url) }} + {% endif %} + + {% for item in settings_instance('customization').homepageitems %} + {% if block('item_' ~ item.value) is defined %} + {{ block('item_' ~ item.value) }} +
+ {% else %} + + {% endif %} + {% endfor %} + +{% endblock %} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 41ad8358..7e2a816b 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -13441,5 +13441,17 @@ Please note, that you can not impersonate a disabled user. If you try you will g Label profile updated successfully. + + + settings.behavior.hompepage.items + Homepage items + + + + + settings.behavior.homepage.items.help + + + From cee6d355e8512663b1b1482720d86679230d4576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 19:43:23 +0200 Subject: [PATCH 04/11] Allow to hide the version number on homepage --- .../SystemSettings/CustomizationSettings.php | 5 +++++ templates/homepage.html.twig | 14 ++++++++------ translations/messages.en.xlf | 6 ++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Settings/SystemSettings/CustomizationSettings.php b/src/Settings/SystemSettings/CustomizationSettings.php index a5f40cdf..623e6187 100644 --- a/src/Settings/SystemSettings/CustomizationSettings.php +++ b/src/Settings/SystemSettings/CustomizationSettings.php @@ -76,4 +76,9 @@ class CustomizationSettings #[Assert\NotBlank()] #[Assert\Unique()] public array $homepageitems = [HomepageItems::SEARCH, HomepageItems::BANNER, HomepageItems::FIRST_STEPS, HomepageItems::LICENSE, HomepageItems::LAST_ACTIVITY]; + + #[SettingsParameter( + label: new TM("settings.system.customization.showVersionOnHomepage") + )] + public bool $showVersionOnHomepage = true; } diff --git a/templates/homepage.html.twig b/templates/homepage.html.twig index 0db7cf17..6e7aa360 100644 --- a/templates/homepage.html.twig +++ b/templates/homepage.html.twig @@ -13,12 +13,14 @@ {% block item_banner %}

{{ vars.partdb_title() }}

-

- {% trans %}version.caption{% endtrans %}: {{ shivas_app_version }} - {% if git_branch is not empty or git_commit is not empty %} - ({{ git_branch ?? '' }}/{{ git_commit ?? '' }}) - {% endif %} -

+ {% if settings_instance('customization').showVersionOnHomepage %} +

+ {% trans %}version.caption{% endtrans %}: {{ shivas_app_version }} + {% if git_branch is not empty or git_commit is not empty %} + ({{ git_branch ?? '' }}/{{ git_commit ?? '' }}) + {% endif %} +

+ {% endif %} {% if banner is not empty %}
diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 7e2a816b..b7710f0c 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -13453,5 +13453,11 @@ Please note, that you can not impersonate a disabled user. If you try you will g + + + settings.system.customization.showVersionOnHomepage + Show Part-DB version on homepage + + From c7ec8adc31934c0d5eef7efc0e75dabb02164612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 19:44:17 +0200 Subject: [PATCH 05/11] Disable settings caching in debug mode Otherwise we run into errors, if a settings get changed --- config/packages/settings.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/config/packages/settings.yaml b/config/packages/settings.yaml index 05e21636..c16d1804 100644 --- a/config/packages/settings.yaml +++ b/config/packages/settings.yaml @@ -5,4 +5,11 @@ jbtronics_settings: default_cacheable: true orm_storage: - default_entity_class: App\Entity\SettingsEntry \ No newline at end of file + default_entity_class: App\Entity\SettingsEntry + + +# Disable caching for development environment +when@dev: + jbtronics_settings: + cache: + default_cacheable: false From 8ff2fc5a82591a11c0c04c5ee823330538c24770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 19:55:47 +0200 Subject: [PATCH 06/11] Allow to disable the extraction of parameters out of part description and notes Fixes issue #747 --- src/Controller/PartController.php | 7 ++++--- src/Settings/BehaviorSettings/PartInfoSettings.php | 8 +++++++- translations/messages.en.xlf | 12 ++++++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/Controller/PartController.php b/src/Controller/PartController.php index b11a5c90..6708ed4c 100644 --- a/src/Controller/PartController.php +++ b/src/Controller/PartController.php @@ -46,6 +46,7 @@ use App\Services\Parameters\ParameterExtractor; use App\Services\Parts\PartLotWithdrawAddHelper; use App\Services\Parts\PricedetailHelper; use App\Services\ProjectSystem\ProjectBuildPartHelper; +use App\Settings\BehaviorSettings\PartInfoSettings; use DateTime; use Doctrine\ORM\EntityManagerInterface; use Exception; @@ -69,7 +70,7 @@ class PartController extends AbstractController protected PartPreviewGenerator $partPreviewGenerator, private readonly TranslatorInterface $translator, private readonly AttachmentSubmitHandler $attachmentSubmitHandler, private readonly EntityManagerInterface $em, - protected EventCommentHelper $commentHelper) + protected EventCommentHelper $commentHelper, private readonly PartInfoSettings $partInfoSettings) { } @@ -119,8 +120,8 @@ class PartController extends AbstractController 'pricedetail_helper' => $this->pricedetailHelper, 'pictures' => $this->partPreviewGenerator->getPreviewAttachments($part), 'timeTravel' => $timeTravel_timestamp, - 'description_params' => $parameterExtractor->extractParameters($part->getDescription()), - 'comment_params' => $parameterExtractor->extractParameters($part->getComment()), + 'description_params' => $this->partInfoSettings->extractParamsFromDescription ? $parameterExtractor->extractParameters($part->getDescription()) : [], + 'comment_params' => $this->partInfoSettings->extractParamsFromNotes ? $parameterExtractor->extractParameters($part->getComment()) : [], 'withdraw_add_helper' => $withdrawAddHelper, ] ); diff --git a/src/Settings/BehaviorSettings/PartInfoSettings.php b/src/Settings/BehaviorSettings/PartInfoSettings.php index 4c44b9bb..f017c846 100644 --- a/src/Settings/BehaviorSettings/PartInfoSettings.php +++ b/src/Settings/BehaviorSettings/PartInfoSettings.php @@ -40,4 +40,10 @@ class PartInfoSettings #[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; -} \ No newline at end of file + + #[SettingsParameter(label: new TM("settings.behavior.part_info.extract_params_from_description"))] + public bool $extractParamsFromDescription = true; + + #[SettingsParameter(label: new TM("settings.behavior.part_info.extract_params_from_notes"))] + public bool $extractParamsFromNotes = true; +} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index b7710f0c..6680521b 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -13459,5 +13459,17 @@ Please note, that you can not impersonate a disabled user. If you try you will g Show Part-DB version on homepage + + + settings.behavior.part_info.extract_params_from_description + Extract parameters from part description + + + + + settings.behavior.part_info.extract_params_from_notes + Extract parameters from part notes + + From 1f669a9c5334b1d3f8302abb3f47e6489310da01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 20:04:48 +0200 Subject: [PATCH 07/11] Readded option to show all elements in a table --- src/Controller/AttachmentFileController.php | 3 ++- src/Controller/PartListsController.php | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Controller/AttachmentFileController.php b/src/Controller/AttachmentFileController.php index 7917e97f..81369e12 100644 --- a/src/Controller/AttachmentFileController.php +++ b/src/Controller/AttachmentFileController.php @@ -24,6 +24,7 @@ namespace App\Controller; use App\DataTables\AttachmentDataTable; use App\DataTables\Filters\AttachmentFilter; +use App\DataTables\PartsDataTable; use App\Entity\Attachments\Attachment; use App\Form\Filters\AttachmentFilterType; use App\Services\Attachments\AttachmentManager; @@ -112,7 +113,7 @@ class AttachmentFileController extends AbstractController $filterForm->handleRequest($formRequest); - $table = $dataTableFactory->createFromType(AttachmentDataTable::class, ['filter' => $filter], ['pageLength' => $tableSettings->fullDefaultPageSize]) + $table = $dataTableFactory->createFromType(AttachmentDataTable::class, ['filter' => $filter], ['pageLength' => $tableSettings->fullDefaultPageSize, 'lengthMenu' => PartsDataTable::LENGTH_MENU]) ->handleRequest($request); if ($table->isCallback()) { diff --git a/src/Controller/PartListsController.php b/src/Controller/PartListsController.php index f6836ddc..b2df18c1 100644 --- a/src/Controller/PartListsController.php +++ b/src/Controller/PartListsController.php @@ -161,7 +161,9 @@ class PartListsController extends AbstractController $filterForm->handleRequest($formRequest); - $table = $this->dataTableFactory->createFromType(PartsDataTable::class, array_merge(['filter' => $filter], $additional_table_vars), ['pageLength' => $this->tableSettings->fullDefaultPageSize]) + $table = $this->dataTableFactory->createFromType(PartsDataTable::class, array_merge( + ['filter' => $filter], $additional_table_vars), + ['pageLength' => $this->tableSettings->fullDefaultPageSize, 'lengthMenu' => PartsDataTable::LENGTH_MENU]) ->handleRequest($request); if ($table->isCallback()) { From 0d1ae030be0cc2fbb0075891dc992d5ee3f757d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 20:42:33 +0200 Subject: [PATCH 08/11] Allow to select default info providers for search This fixes issue #556 --- src/Controller/InfoProviderController.php | 20 ++++++++- .../InfoProviderSystem/ProviderSelectType.php | 45 ++++++++++++++++--- .../InfoProviderGeneralSettings.php | 45 +++++++++++++++++++ .../InfoProviderSettings.php | 6 ++- translations/messages.en.xlf | 18 ++++++++ 5 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 src/Settings/InfoProviderSystem/InfoProviderGeneralSettings.php diff --git a/src/Controller/InfoProviderController.php b/src/Controller/InfoProviderController.php index a6e886e6..dae8213e 100644 --- a/src/Controller/InfoProviderController.php +++ b/src/Controller/InfoProviderController.php @@ -30,6 +30,7 @@ use App\Services\InfoProviderSystem\ExistingPartFinder; use App\Services\InfoProviderSystem\PartInfoRetriever; use App\Services\InfoProviderSystem\ProviderRegistry; use App\Settings\AppSettings; +use App\Settings\InfoProviderSystem\InfoProviderGeneralSettings; use Doctrine\ORM\EntityManagerInterface; use Jbtronics\SettingsBundle\Form\SettingsFormFactoryInterface; use Jbtronics\SettingsBundle\Manager\SettingsManagerInterface; @@ -113,7 +114,7 @@ class InfoProviderController extends AbstractController #[Route('/search', name: 'info_providers_search')] #[Route('/update/{target}', name: 'info_providers_update_part_search')] - public function search(Request $request, #[MapEntity(id: 'target')] ?Part $update_target, LoggerInterface $exceptionLogger): Response + public function search(Request $request, #[MapEntity(id: 'target')] ?Part $update_target, LoggerInterface $exceptionLogger, InfoProviderGeneralSettings $infoProviderSettings): Response { $this->denyAccessUnlessGranted('@info_providers.create_parts'); @@ -144,6 +145,23 @@ class InfoProviderController extends AbstractController } } + //If the providers form is still empty, use our default value from the settings + if (count($form->get('providers')->getData() ?? []) === 0) { + $default_providers = $infoProviderSettings->defaultSearchProviders; + $provider_objects = []; + foreach ($default_providers as $provider_key) { + try { + $tmp = $this->providerRegistry->getProviderByKey($provider_key); + if ($tmp->isActive()) { + $provider_objects[] = $tmp; + } + } catch (\InvalidArgumentException $e) { + //If the provider is not found, just ignore it + } + } + $form->get('providers')->setData($provider_objects); + } + if ($form->isSubmitted() && $form->isValid()) { $keyword = $form->get('keyword')->getData(); $providers = $form->get('providers')->getData(); diff --git a/src/Form/InfoProviderSystem/ProviderSelectType.php b/src/Form/InfoProviderSystem/ProviderSelectType.php index a9373390..95e10791 100644 --- a/src/Form/InfoProviderSystem/ProviderSelectType.php +++ b/src/Form/InfoProviderSystem/ProviderSelectType.php @@ -28,6 +28,7 @@ use App\Services\InfoProviderSystem\Providers\InfoProviderInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\ChoiceList\ChoiceList; use Symfony\Component\Form\Extension\Core\Type\ChoiceType; +use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; class ProviderSelectType extends AbstractType @@ -44,13 +45,43 @@ class ProviderSelectType extends AbstractType public function configureOptions(OptionsResolver $resolver): void { - $resolver->setDefaults([ - 'choices' => $this->providerRegistry->getActiveProviders(), - 'choice_label' => ChoiceList::label($this, static fn (?InfoProviderInterface $choice) => $choice?->getProviderInfo()['name']), - 'choice_value' => ChoiceList::value($this, static fn(?InfoProviderInterface $choice) => $choice?->getProviderKey()), + $providers = $this->providerRegistry->getActiveProviders(); - 'multiple' => true, - ]); + $resolver->setDefault('input', 'object'); + $resolver->setAllowedTypes('input', 'string'); + //Either the form returns the provider objects or their keys + $resolver->setAllowedValues('input', ['object', 'string']); + $resolver->setDefault('multiple', true); + + $resolver->setDefault('choices', function (Options $options) use ($providers) { + if ('object' === $options['input']) { + return $this->providerRegistry->getActiveProviders(); + } + + $tmp = []; + foreach ($providers as $provider) { + $name = $provider->getProviderInfo()['name']; + $tmp[$name] = $provider->getProviderKey(); + } + + return $tmp; + }); + + //The choice_label and choice_value only needs to be set if we want the objects + $resolver->setDefault('choice_label', function (Options $options){ + if ('object' === $options['input']) { + return ChoiceList::label($this, static fn (?InfoProviderInterface $choice) => $choice?->getProviderInfo()['name']); + } + + return null; + }); + $resolver->setDefault('choice_value', function (Options $options) { + if ('object' === $options['input']) { + return ChoiceList::value($this, static fn(?InfoProviderInterface $choice) => $choice?->getProviderKey()); + } + + return null; + }); } -} \ No newline at end of file +} diff --git a/src/Settings/InfoProviderSystem/InfoProviderGeneralSettings.php b/src/Settings/InfoProviderSystem/InfoProviderGeneralSettings.php new file mode 100644 index 00000000..03fff0bf --- /dev/null +++ b/src/Settings/InfoProviderSystem/InfoProviderGeneralSettings.php @@ -0,0 +1,45 @@ +. + */ + +declare(strict_types=1); + + +namespace App\Settings\InfoProviderSystem; + +use App\Form\InfoProviderSystem\ProviderSelectType; +use App\Settings\SettingsIcon; +use Jbtronics\SettingsBundle\ParameterTypes\ArrayType; +use Jbtronics\SettingsBundle\ParameterTypes\StringType; +use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsParameter; +use Symfony\Component\Translation\TranslatableMessage as TM; + +#[Settings(label: new TM("settings.ips.general"))] +#[SettingsIcon("fa-magnifying-glass")] +class InfoProviderGeneralSettings +{ + /** + * @var string[] + */ + #[SettingsParameter(type: ArrayType::class, label: new TM("settings.ips.default_providers"), + description: new TM("settings.ips.default_providers.help"), options: ['type' => StringType::class], + formType: ProviderSelectType::class, formOptions: ['input' => 'string'])] + public array $defaultSearchProviders = []; +} diff --git a/src/Settings/InfoProviderSystem/InfoProviderSettings.php b/src/Settings/InfoProviderSystem/InfoProviderSettings.php index 3c7159cb..c223bd88 100644 --- a/src/Settings/InfoProviderSystem/InfoProviderSettings.php +++ b/src/Settings/InfoProviderSystem/InfoProviderSettings.php @@ -25,6 +25,7 @@ namespace App\Settings\InfoProviderSystem; use Jbtronics\SettingsBundle\Settings\EmbeddedSettings; use Jbtronics\SettingsBundle\Settings\Settings; +use Jbtronics\SettingsBundle\Settings\SettingsParameter; use Jbtronics\SettingsBundle\Settings\SettingsTrait; #[Settings()] @@ -32,6 +33,9 @@ class InfoProviderSettings { use SettingsTrait; + #[EmbeddedSettings] + public ?InfoProviderGeneralSettings $general = null; + #[EmbeddedSettings] public ?DigikeySettings $digikey = null; @@ -58,4 +62,4 @@ class InfoProviderSettings #[EmbeddedSettings] public ?PollinSettings $pollin = null; -} \ No newline at end of file +} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 6680521b..68bbb653 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -13471,5 +13471,23 @@ Please note, that you can not impersonate a disabled user. If you try you will g Extract parameters from part notes + + + settings.ips.default_providers + Default search providers + + + + + settings.ips.general + General settings + + + + + settings.ips.default_providers.help + These providers will be preselected for searches in part providers. + + From ecd2abe00ea20ed40a9e2816300ac71e45fb4312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 21:21:08 +0200 Subject: [PATCH 09/11] Made image size of preview images in tables configurable and slightly bigger by default This makes PR #984 and #623 obsolete --- assets/css/app/images.css | 6 +++--- src/Settings/BehaviorSettings/TableSettings.php | 16 +++++++++++++++- templates/base.html.twig | 8 ++++++++ translations/messages.en.xlf | 12 ++++++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/assets/css/app/images.css b/assets/css/app/images.css index 214776e7..0212a85b 100644 --- a/assets/css/app/images.css +++ b/assets/css/app/images.css @@ -18,8 +18,8 @@ */ .hoverpic { - min-width: 10px; - max-width: 30px; + min-width: var(--table-image-preview-min-size, 20px); + max-width: var(--table-image-preview-max-size, 35px); display: block; margin-left: auto; margin-right: auto; @@ -49,7 +49,7 @@ } .part-table-image { - max-height: 40px; + max-height: calc(1.2*var(--table-image-preview-max-size, 35px)); /** Aspect ratio of maximum 1.2 */ object-fit: contain; } diff --git a/src/Settings/BehaviorSettings/TableSettings.php b/src/Settings/BehaviorSettings/TableSettings.php index 7b4e7912..b6964876 100644 --- a/src/Settings/BehaviorSettings/TableSettings.php +++ b/src/Settings/BehaviorSettings/TableSettings.php @@ -70,6 +70,20 @@ class TableSettings PartTableColumns::CATEGORY, PartTableColumns::FOOTPRINT, PartTableColumns::MANUFACTURER, PartTableColumns::LOCATION, PartTableColumns::AMOUNT]; + #[SettingsParameter(label: new TM("settings.behavior.table.preview_image_min_width"), + formOptions: ['attr' => ['min' => 1, 'max' => 100]], + envVar: "int:TABLE_IMAGE_PREVIEW_MIN_SIZE", envVarMode: EnvVarMode::OVERWRITE + )] + #[Assert\Range(min: 1, max: 100)] + public int $previewImageMinWidth = 20; + + #[SettingsParameter(label: new TM("settings.behavior.table.preview_image_max_width"), + formOptions: ['attr' => ['min' => 1, 'max' => 100]], + envVar: "int:TABLE_IMAGE_PREVIEW_MAX_SIZE", envVarMode: EnvVarMode::OVERWRITE + )] + #[Assert\Range(min: 1, max: 100)] + #[Assert\GreaterThanOrEqual(propertyPath: 'previewImageMinWidth')] + public int $previewImageMaxWidth = 35; public static function mapPartsDefaultColumnsEnv(string $columns): array { @@ -87,4 +101,4 @@ class TableSettings return $ret; } -} \ No newline at end of file +} diff --git a/templates/base.html.twig b/templates/base.html.twig index 48e45ab0..bb9844fa 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -53,6 +53,14 @@ {% endif %} {{ encore_entry_link_tags('app') }} + + {% set table_settings = settings_instance('table') %} + {% endblock %} {% block javascripts %} diff --git a/translations/messages.en.xlf b/translations/messages.en.xlf index 68bbb653..88ae764a 100644 --- a/translations/messages.en.xlf +++ b/translations/messages.en.xlf @@ -13489,5 +13489,17 @@ Please note, that you can not impersonate a disabled user. If you try you will g These providers will be preselected for searches in part providers. + + + settings.behavior.table.preview_image_max_width + Preview image max width (px) + + + + + settings.behavior.table.preview_image_min_width + Preview image min width (px) + + From e81c8470beebd8f4665a6360981248239a0178b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 21:51:58 +0200 Subject: [PATCH 10/11] Made part table action bar sticky floating Related to PR #997 --- .../elements/datatables/parts_controller.js | 2 ++ assets/css/app/tables.css | 12 +++++++++++- templates/components/datatables.macro.html.twig | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/assets/controllers/elements/datatables/parts_controller.js b/assets/controllers/elements/datatables/parts_controller.js index 1fe11a20..c43fa276 100644 --- a/assets/controllers/elements/datatables/parts_controller.js +++ b/assets/controllers/elements/datatables/parts_controller.js @@ -45,8 +45,10 @@ export default class extends DatatablesController { //Hide/Unhide panel with the selection tools if (count > 0) { selectPanel.classList.remove('d-none'); + selectPanel.classList.add('sticky-select-bar'); } else { selectPanel.classList.add('d-none'); + selectPanel.classList.remove('sticky-select-bar'); } //Update selection count text diff --git a/assets/css/app/tables.css b/assets/css/app/tables.css index ae892f50..aa72fff3 100644 --- a/assets/css/app/tables.css +++ b/assets/css/app/tables.css @@ -17,6 +17,16 @@ * along with this program. If not, see . */ +/**************************************** + * Action bar + ****************************************/ + +.sticky-select-bar { + position: sticky; + top: 120px; + z-index: 3000; /* Ensure the bar is above other content */ +} + /**************************************** * Tables ****************************************/ @@ -109,4 +119,4 @@ Classes for Datatables export #export-messageTop, .export-helper{ display: none; -} \ No newline at end of file +} diff --git a/templates/components/datatables.macro.html.twig b/templates/components/datatables.macro.html.twig index 5ce0f23f..447aa69c 100644 --- a/templates/components/datatables.macro.html.twig +++ b/templates/components/datatables.macro.html.twig @@ -29,7 +29,7 @@ -
+
{# #}
@@ -95,4 +95,4 @@
-{% endmacro %} \ No newline at end of file +{% endmacro %} From c2cbbee0df692a6787bd956e4d7eca77161cb781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 7 Sep 2025 21:59:30 +0200 Subject: [PATCH 11/11] Ensure that part table action bar dont overlap our navbar dropdowns --- assets/css/app/tables.css | 2 +- templates/components/datatables.macro.html.twig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/css/app/tables.css b/assets/css/app/tables.css index aa72fff3..8d4b200c 100644 --- a/assets/css/app/tables.css +++ b/assets/css/app/tables.css @@ -24,7 +24,7 @@ .sticky-select-bar { position: sticky; top: 120px; - z-index: 3000; /* Ensure the bar is above other content */ + z-index: 1000; /* Ensure the bar is above other content */ } /**************************************** diff --git a/templates/components/datatables.macro.html.twig b/templates/components/datatables.macro.html.twig index 447aa69c..009f815e 100644 --- a/templates/components/datatables.macro.html.twig +++ b/templates/components/datatables.macro.html.twig @@ -29,7 +29,7 @@ -
+
{# #}