Compare commits

..

7 Commits

Author SHA1 Message Date
Jan Böhmer
eb7aefb8c0 Bumped to version 1.17.3 2025-08-13 15:34:29 +02:00
Jan Böhmer
475cfe60f9 Fixed phpstan issue 2025-08-13 15:34:12 +02:00
Jan Böhmer
1e9a2e5382 Revert yarn dependencies update 2025-08-13 15:26:56 +02:00
Jan Böhmer
9eaf5042ec Downgraded ckeditor-dev-utils to be compatible with node 18 2025-08-13 14:49:20 +02:00
Jan Böhmer
128b428644 Allow to use arrow style full-paths for mass creation of entities
This fixes issue #993
2025-08-13 14:37:13 +02:00
Jan Böhmer
23cd51c1ca Updated dependencies 2025-08-12 23:34:06 +02:00
Jan Böhmer
d370f976a7 Return null instead of throwing an exception that could lead to a denial of service when trying to generate a thumbnail for a non-image picture 2025-08-12 23:29:24 +02:00
125 changed files with 1781 additions and 4216 deletions

185
.env
View File

@@ -32,16 +32,36 @@ DATABASE_EMULATE_NATURAL_SORT=0
###################################################################################
# The language to use serverwide as default (en, de, ru, etc.)
#DEFAULT_LANG="en"
DEFAULT_LANG="en"
# The default timezone to use serverwide (e.g. Europe/Berlin)
#DEFAULT_TIMEZONE="Europe/Berlin"
DEFAULT_TIMEZONE="Europe/Berlin"
# The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country
#BASE_CURRENCY="EUR"
BASE_CURRENCY="EUR"
# The name of this installation. This will be shown as title in the browser and in the header of the website
INSTANCE_NAME="Part-DB"
# Allow users to download attachments to the server by providing an URL
# This could be a potential security issue, as the user can retrieve any file the server has access to (via internet)
ALLOW_ATTACHMENT_DOWNLOADS=0
# Set this to 1, if the "download external files" checkbox should be checked by default for new attachments
ATTACHMENT_DOWNLOAD_BY_DEFAULT=0
# Use gravatars for user avatars, when user has no own avatar defined
USE_GRAVATAR=0
# The maximum allowed size for attachment files in bytes (you can use M for megabytes and G for gigabytes)
# Please note that the php.ini setting upload_max_filesize also limits the maximum size of uploaded files
MAX_ATTACHMENT_FILE_SIZE="100M"
# The public reachable URL of this Part-DB installation. This is used for generating links in SAML and email templates
# This must end with a slash!
DEFAULT_URI="https://partdb.changeme.invalid/"
# With this option you can configure, where users are enforced to give a change reason, which will be logged
# This is a comma separated list of values, see documentation for available values
# Leave this empty, to make all change reasons optional
ENFORCE_CHANGE_COMMENTS_FOR=""
# Disable that if you do not want that Part-DB connects to GitHub to check for available updates, or if your server can not connect to the internet
CHECK_FOR_UPDATES=1
###################################################################################
# Email settings
###################################################################################
@@ -58,6 +78,21 @@ EMAIL_SENDER_NAME="Part-DB Mailer"
# Set this to 1 to allow reset of a password per email
ALLOW_EMAIL_PW_RESET=0
######################################################################################
# History/Eventlog settings
######################################################################################
# If you want to use full timetrave functionality all values below have to be set to 1
# Save which fields were changed in a ElementEdited log entry
HISTORY_SAVE_CHANGED_FIELDS=1
# Save the old data in the ElementEdited log entry (warning this could increase the database size in short time)
HISTORY_SAVE_CHANGED_DATA=1
# Save the data of an element that gets removed into log entry. This allows to undelete an element
HISTORY_SAVE_REMOVED_DATA=1
# Save the new data of an element that gets changed or added. This allows an easy comparison of the old and new data on the detail page
# This option only becomes active when HISTORY_SAVE_CHANGED_DATA is set to 1
HISTORY_SAVE_NEW_DATA=1
###################################################################################
# Error pages settings
###################################################################################
@@ -67,6 +102,140 @@ 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
##################################################################################
# Part table settings
##################################################################################
# The default page size for the part table (set to -1 to show all parts on one page)
TABLE_DEFAULT_PAGE_SIZE=50
# Configure which columns will be visible by default in the parts table (and in which order).
# This is a comma separated list of column names. See documentation for available values.
TABLE_PARTS_DEFAULT_COLUMNS=name,description,category,footprint,manufacturer,storage_location,amount
##################################################################################
# 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
# Farnell Provider:
# You can get your API key from https://partner.element14.com/
PROVIDER_ELEMENT14_KEY=
# Configure the store domain you want to use. This decides the language and currency of results. You can get a list of available stores from https://partner.element14.com/docs/Product_Search_API_REST__Description
PROVIDER_ELEMENT14_STORE_ID=de.farnell.com
# TME Provider:
# You can get your API key from https://developers.tme.eu/en/
PROVIDER_TME_KEY=
PROVIDER_TME_SECRET=
# The currency to get prices in
PROVIDER_TME_CURRENCY=EUR
# The language to get results in (en, de, pl)
PROVIDER_TME_LANGUAGE=en
# The country to get results for
PROVIDER_TME_COUNTRY=DE
# [DEPRECATED] Set this to 1 to get gross prices (including VAT) instead of net prices
# With private API keys, this option cannot be used anymore is ignored by Part-DB. The VAT inclusion depends on your TME account settings.
PROVIDER_TME_GET_GROSS_PRICES=1
# 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
# Mouser Provider API V2:
# You can get your API key from https://www.mouser.it/api-hub/
PROVIDER_MOUSER_KEY=
# Filter search results by RoHS compliance and stock availability:
# Available options: None | Rohs | InStock | RohsAndInStock
PROVIDER_MOUSER_SEARCH_OPTION='None'
# The number of results to get from Mouser while searching (please note that this value is max 50)
PROVIDER_MOUSER_SEARCH_LIMIT=50
# It is recommended to leave this set to 'true'. The option is not really good doumented by Mouser:
# Used when searching for keywords in the language specified when you signed up for Search API.
PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE='true'
# LCSC Provider:
# LCSC does not provide an offical API, so this used the API LCSC uses to render their webshop.
# LCSC did not intended the use of this API and it could break any time, so use it at your own risk.
# We dont require an API key for LCSC, just set this to 1 to enable LCSC support
PROVIDER_LCSC_ENABLED=0
# The currency to get prices in (e.g. EUR, USD, etc.)
PROVIDER_LCSC_CURRENCY=EUR
# Oemsecrets Provider API 3.0.1:
# You can get your API key from https://www.oemsecrets.com/api
PROVIDER_OEMSECRETS_KEY=
# The country you want the output for
PROVIDER_OEMSECRETS_COUNTRY_CODE=DE
# Available country code are:
# AD, AE, AQ, AR, AT, AU, BE, BO, BR, BV, BY, CA, CH, CL, CN, CO, CZ, DE, DK, EC, EE, EH,
# ES, FI, FK, FO, FR, GB, GE, GF, GG, GI, GL, GR, GS, GY, HK, HM, HR, HU, IE, IM, IN, IS,
# IT, JM, JP, KP, KR, KZ, LI, LK, LT, LU, MC, MD, ME, MK, MT, NL, NO, NZ, PE, PH, PL, PT,
# PY, RO, RS, RU, SB, SD, SE, SG, SI, SJ, SK, SM, SO, SR, SY, SZ, TC, TF, TG, TH, TJ, TK,
# TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VE, VG, VI, VN, VU, WF, YE,
# ZA, ZM, ZW
#
# The currency you want the prices to be displayed in
PROVIDER_OEMSECRETS_CURRENCY=EUR
# Available currency are:AUD, CAD, CHF, CNY, DKK, EUR, GBP, HKD, ILS, INR, JPY, KRW, NOK,
# NZD, RUB, SEK, SGD, TWD, USD
#
# If PROVIDER_OEMSECRETS_ZERO_PRICE is set to 0, distributors with zero prices
# will be discarded from the creation of a new part (set to 1 otherwise)
PROVIDER_OEMSECRETS_ZERO_PRICE=0
#
# When PROVIDER_OEMSECRETS_SET_PARAM is set to 1 the parameters for the part are generated
# from the description transforming unstructured descriptions into structured parameters;
# each parameter in description should have the form: "...;name1:value1;name2:value2"
PROVIDER_OEMSECRETS_SET_PARAM=1
#
# This environment variable determines the sorting criteria for product results.
# The sorting process first arranges items based on the provided keyword.
# Then, if set to 'C', it further sorts by completeness (prioritizing items with the most
# detailed information). If set to 'M', it further sorts by manufacturer name.
#If unset or set to any other value, no sorting is performed.
PROVIDER_OEMSECRETS_SORT_CRITERIA=C
# Reichelt provider:
# Reichelt.com 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
# We dont require an API key for Reichelt, just set this to 1 to enable Reichelt support
PROVIDER_REICHELT_ENABLED=0
# The country to get prices for
PROVIDER_REICHELT_COUNTRY=DE
# The language to get results in (en, de, fr, nl, pl, it, es)
PROVIDER_REICHELT_LANGUAGE=en
# Include VAT in prices (set to 1 to include VAT, 0 to exclude VAT)
PROVIDER_REICHELT_INCLUDE_VAT=1
# The currency to get prices in (only for countries with countries other than EUR)
PROVIDER_REICHELT_CURRENCY=EUR
# Pollin provider:
# 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
# We dont require an API key for Pollin, just set this to 1 to enable Pollin support
PROVIDER_POLLIN_ENABLED=0
##################################################################################
# EDA integration related settings
@@ -74,8 +243,8 @@ ERROR_PAGE_SHOW_HELP=1
# This value determines the depth of the category tree, that is visible inside KiCad
# 0 means that only the top level categories are visible. Set to a value > 0 to show more levels.
# Set to -1, to show all parts of Part-DB inside a sigle cnategory in KiCad
#EDA_KICAD_CATEGORY_DEPTH=0
# Set to -1, to show all parts of Part-DB inside a single category in KiCad
EDA_KICAD_CATEGORY_DEPTH=0
###################################################################################
# SAML Single sign on-settings
@@ -129,10 +298,16 @@ 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=""
# Enable the part image overlay which shows name and filename of the picture
SHOW_PART_IMAGE_OVERLAY=1
APP_ENV=prod
APP_SECRET=a03498528f5a5fc089273ec9ae5b2849

View File

@@ -10,6 +10,4 @@ DATABASE_URL="sqlite:///%kernel.project_dir%/var/app_test.db"
#DATABASE_URL=mysql://root:@127.0.0.1:3306/part-db
# Disable update checks, as tests would fail, when github is not reachable
CHECK_FOR_UPDATES=0
INSTANCE_NAME="Part-DB"
CHECK_FOR_UPDATES=0

1
.gitignore vendored
View File

@@ -2,7 +2,6 @@
/.env.local
/.env.local.php
/.env.*.local
/.env.local.bak
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/

View File

@@ -1 +1 @@
2.0.0-dev
1.17.3

View File

@@ -40,7 +40,6 @@ export default class extends Controller {
let settings = {
plugins: ["clear_button"],
allowEmptyOption: true,
selectOnTab: true,
maxOptions: null,
@@ -51,24 +50,7 @@ export default class extends Controller {
}
};
//Load the drag_drop plugin if the select is ordered
if (this.element.dataset.orderedValue) {
settings.plugins.push('drag_drop');
settings.plugins.push("caret_position");
}
//If multiple items can be selected, enable the remove_button plugin
if (this.element.multiple) {
settings.plugins.push('remove_button');
}
this._tomSelect = new TomSelect(this.element, settings);
//If the select is ordered, we need to update the value field (with the decoded value from the orderedValue field)
if (this.element.dataset.orderedValue) {
const data = JSON.parse(this.element.dataset.orderedValue);
this._tomSelect.setValue(data);
}
}
getTomSelect() {

View File

@@ -20,8 +20,6 @@
import {Controller} from "@hotwired/stimulus";
import TomSelect from "tom-select";
// TODO: Merge with select_controller.js
export default class extends Controller {
_tomSelect;

View File

@@ -30,7 +30,6 @@
"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",
"jfcherng/php-diff": "^6.14",
"knpuniversity/oauth2-client-bundle": "^2.15",
"league/csv": "^9.8.0",
@@ -75,7 +74,7 @@
"symfony/runtime": "6.4.*",
"symfony/security-bundle": "6.4.*",
"symfony/serializer": "6.4.*",
"symfony/string": "6.4.8",
"symfony/string": "6.4.*",
"symfony/translation": "6.4.*",
"symfony/twig-bundle": "6.4.*",
"symfony/ux-translator": "^2.10",

1529
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -31,6 +31,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],
Jbtronics\TranslationEditorBundle\JbtronicsTranslationEditorBundle::class => ['dev' => true],
];

View File

@@ -9,8 +9,7 @@ datatables:
# Set options, as documented at https://datatables.net/reference/option/
options:
lengthMenu : [[10, 25, 50, 100], [10, 25, 50, 100]] # We add the "All" option, when part tables are generated
#pageLength: '%partdb.table.default_page_size%' # Set to -1 to disable pagination (i.e. show all rows) by default
pageLength: 50 #TODO
pageLength: '%partdb.table.default_page_size%' # Set to -1 to disable pagination (i.e. show all rows) by default
dom: " <'row' <'col mb-2 input-group flex-nowrap' B l > <'col-auto mb-2' < p >>>
<'card'
rt

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 +0,0 @@
jbtronics_settings:
default_storage_adapter: Jbtronics\SettingsBundle\Storage\ORMStorageAdapter
cache:
default_cacheable: true
orm_storage:
default_entity_class: App\Entity\SettingsEntry

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

@@ -1,10 +1,11 @@
framework:
default_locale: 'en'
default_locale: '%partdb.locale%'
# Just enable the locales we need for performance reasons.
enabled_locale: '%partdb.locale_menu%'
translator:
default_path: '%kernel.project_dir%/translations'
fallbacks:
- '%partdb.locale%'
- 'en'
providers:
# crowdin:

View File

@@ -6,17 +6,22 @@ twig:
'%kernel.project_dir%/assets/css': css
globals:
partdb_title: '%partdb.title%'
default_currency: '%partdb.default_currency%'
global_theme: '%partdb.global_theme%'
allow_email_pw_reset: '%partdb.users.email_pw_reset%'
locale_menu: '%partdb.locale_menu%'
attachment_manager: '@App\Services\Attachments\AttachmentManager'
label_profile_dropdown_helper: '@App\Services\LabelSystem\LabelProfileDropdownHelper'
error_page_admin_email: '%partdb.error_pages.admin_email%'
error_page_show_help: '%partdb.error_pages.show_help%'
sidebar_items: '%partdb.sidebar.items%'
sidebar_tree_updater: '@App\Services\Trees\SidebarTreeUpdater'
avatar_helper: '@App\Services\UserSystem\UserAvatarHelper'
available_themes: '%partdb.available_themes%'
saml_enabled: '%partdb.saml.enabled%'
part_preview_generator: '@App\Services\Attachments\PartPreviewGenerator'
img_overlay: '%partdb.show_part_image_overlay%'
when@test:
twig:

View File

@@ -5,10 +5,14 @@ parameters:
######################################################################################################################
# Common
######################################################################################################################
# This is used as workaround for places where we can not access the settings directly (like the 2FA application names)
partdb.title: '%env(string:settings:customization:instanceName)%' # The title shown inside of Part-DB (e.g. in the navbar and on homepage)
partdb.locale: '%env(string:DEFAULT_LANG)%' # The default language to use serverwide
partdb.timezone: '%env(string:DEFAULT_TIMEZONE)%' # The default timezone
partdb.title: '%env(trim:string:INSTANCE_NAME)%' # The title shown inside of Part-DB (e.g. in the navbar and on homepage)
partdb.banner: '%env(trim:string:BANNER)%' # The info text shown in the homepage, if empty config/banner.md is used
partdb.default_currency: '%env(string:BASE_CURRENCY)%' # The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country
partdb.global_theme: '' # The theme to use globally (see public/build/themes/ for choices, use name without .css). Set to '' for default bootstrap theme
partdb.locale_menu: ['en', 'de', 'it', 'fr', 'ru', 'ja', 'cs', 'da', 'zh', 'pl'] # The languages that are shown in user drop down menu
partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all.
partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails
@@ -18,8 +22,11 @@ parameters:
# Users and Privacy
######################################################################################################################
partdb.gdpr_compliance: true # If this option is activated, IP addresses are anonymized to be GDPR compliant
partdb.users.use_gravatar: '%env(bool:USE_GRAVATAR)%' # Set to false, if no Gravatar images should be used for user profiles.
partdb.users.email_pw_reset: '%env(bool:ALLOW_EMAIL_PW_RESET)%' # Config if users are able, to reset their password by email. By default this enabled, when a mail server is configured.
partdb.check_for_updates: '%env(bool:CHECK_FOR_UPDATES)' # Set to false, if Part-DB should not contact the GitHub API to check for updates
######################################################################################################################
# Mail settings
######################################################################################################################
@@ -29,8 +36,11 @@ parameters:
######################################################################################################################
# Attachments and files
######################################################################################################################
partdb.attachments.allow_downloads: '%env(bool:ALLOW_ATTACHMENT_DOWNLOADS)%' # Allow users to download attachments to server. Warning: This can be dangerous, because via that feature attackers maybe can access ressources on your intranet!
partdb.attachments.download_by_default: '%env(bool:ATTACHMENT_DOWNLOAD_BY_DEFAULT)%' # If this is set the 'download external files' checkbox is set by default for new attachments (only if allow_downloads is set to true)
partdb.attachments.dir.media: 'public/media/' # The folder where uploaded attachment files are saved (must be in public folder)
partdb.attachments.dir.secure: 'uploads/' # The folder where secured attachment files are saved (must not be in public/)
partdb.attachments.max_file_size: '%env(string:MAX_ATTACHMENT_FILE_SIZE)%' # The maximum size of an attachment file (in bytes, you can use M for megabytes and G for gigabytes)
######################################################################################################################
# Error pages
@@ -43,11 +53,28 @@ parameters:
######################################################################################################################
partdb.saml.enabled: '%env(bool:SAML_ENABLED)%' # If this is set to true, SAML authentication is enabled
######################################################################################################################
# Table settings
######################################################################################################################
partdb.table.default_page_size: '%env(int:TABLE_DEFAULT_PAGE_SIZE)%' # The default number of entries shown per page in tables
partdb.table.parts.default_columns: '%env(trim:string:TABLE_PARTS_DEFAULT_COLUMNS)%' # The default columns in part tables and their order
######################################################################################################################
# Sidebar
######################################################################################################################
# You can configures the default shown tree items in the sidebar here. You can add or remove entries here, to change the number of trees in the sidebar. The possible entries are: categories, locations, footprints, manufacturers, suppliers, devices, tools
partdb.sidebar.items:
- categories
- devices
- tools
partdb.sidebar.root_expanded: true # If this is set to true, the root node of the sidebar is expanded by default
partdb.sidebar.root_node_enable: true # Put all entities below a root node in the sidebar
######################################################################################################################
# Miscellaneous
######################################################################################################################
partdb.demo_mode: '%env(bool:DEMO_MODE)%' # If set to true, all potentially dangerous things are disabled (like changing passwords of the own user)
partdb.show_part_image_overlay: '%env(bool:SHOW_PART_IMAGE_OVERLAY)%' # If set to false, the filename overlay of the part image will be disabled
# Set the themes from which the user can choose from in the settings.
# Themes commented here by default, are not really usable, because of display problems. Enable them at your own risk!
@@ -84,8 +111,17 @@ parameters:
# Env default values
######################################################################################################################
env(DEFAULT_LANG): 'en'
env(DEFAULT_TIMEZONE): 'Europe/Berlin'
env(INSTANCE_NAME): 'Part-DB'
env(BASE_CURRENCY): 'EUR'
env(USE_GRAVATAR): '0'
env(MAX_ATTACHMENT_FILE_SIZE): '100M'
env(REDIRECT_TO_HTTPS): 0
env(ENFORCE_CHANGE_COMMENTS_FOR): ''
env(ERROR_PAGE_ADMIN_EMAIL): ''
env(ERROR_PAGE_SHOW_HELP): 1
@@ -97,6 +133,8 @@ parameters:
env(EMAIL_SENDER_NAME): 'Part-DB Mailer'
env(ALLOW_EMAIL_PW_RESET): 0
env(TABLE_DEFAULT_PAGE_SIZE): 50
env(TRUSTED_PROXIES): '127.0.0.1' #By default trust only our own server
env(TRUSTED_HOSTS): '' # Trust all host names by default
@@ -104,4 +142,11 @@ parameters:
env(SAML_ROLE_MAPPING): '{}'
env(HISTORY_SAVE_CHANGED_DATA): 1
env(HISTORY_SAVE_CHANGED_FIELDS): 1
env(HISTORY_SAVE_REMOVED_DATA): 1
env(HISTORY_SAVE_NEW_DATA): 1
env(EDA_KICAD_CATEGORY_DEPTH): 0
env(DATABASE_EMULATE_NATURAL_SORT): 0

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

@@ -17,6 +17,8 @@ services:
bool $gdpr_compliance: '%partdb.gdpr_compliance%'
bool $kernel_debug_enabled: '%kernel.debug%'
string $kernel_cache_dir: '%kernel.cache_dir%'
string $partdb_title: '%partdb.title%'
string $base_currency: '%partdb.default_currency%'
_instanceof:
App\Services\LabelSystem\PlaceholderProviders\PlaceholderProviderInterface:
@@ -74,10 +76,28 @@ services:
# Only the event classes specified here are saved to DB (set to []) to log all events
$whitelist: []
App\EventListener\LogSystem\EventLoggerListener:
arguments:
$save_changed_fields: '%env(bool:HISTORY_SAVE_CHANGED_FIELDS)%'
$save_changed_data: '%env(bool:HISTORY_SAVE_CHANGED_DATA)%'
$save_removed_data: '%env(bool:HISTORY_SAVE_REMOVED_DATA)%'
$save_new_data: '%env(bool:HISTORY_SAVE_NEW_DATA)%'
App\Form\AttachmentFormType:
arguments:
$allow_attachments_download: '%partdb.attachments.allow_downloads%'
$max_file_size: '%partdb.attachments.max_file_size%'
$download_by_default: '%partdb.attachments.download_by_default%'
App\Services\Attachments\AttachmentSubmitHandler:
arguments:
$allow_attachments_downloads: '%partdb.attachments.allow_downloads%'
$mimeTypes: '@mime_types'
$max_upload_size: '%partdb.attachments.max_file_size%'
App\Services\LogSystem\EventCommentNeededHelper:
arguments:
$enforce_change_comments_for: '%partdb.enforce_change_comments_for%'
####################################################################################################################
# Attachment system
@@ -136,6 +156,29 @@ services:
tags:
- { name: doctrine.orm.entity_listener }
####################################################################################################################
# Price system
####################################################################################################################
App\Command\Currencies\UpdateExchangeRatesCommand:
arguments:
$base_current: '%partdb.default_currency%'
App\Form\Type\CurrencyEntityType:
arguments:
$base_currency: '%partdb.default_currency%'
App\Services\Parts\PricedetailHelper:
arguments:
$base_currency: '%partdb.default_currency%'
App\Services\Formatters\MoneyFormatter:
arguments:
$base_currency: '%partdb.default_currency%'
App\Services\Tools\ExchangeRateUpdater:
arguments:
$base_currency: '%partdb.default_currency%'
###################################################################################################################
# User system
####################################################################################################################
@@ -143,6 +186,10 @@ services:
arguments:
$demo_mode: '%partdb.demo_mode%'
App\EventSubscriber\UserSystem\SetUserTimezoneSubscriber:
arguments:
$default_timezone: '%partdb.timezone%'
App\Controller\SecurityController:
arguments:
$allow_email_pw_reset: '%partdb.users.email_pw_reset%'
@@ -156,6 +203,10 @@ services:
tags:
- { name: 'translation.extractor', alias: 'permissionExtractor'}
App\Services\UserSystem\UserAvatarHelper:
arguments:
$use_gravatar: '%partdb.users.use_gravatar%'
App\Form\Type\ThemeChoiceType:
arguments:
$available_themes: '%partdb.available_themes%'
@@ -171,6 +222,9 @@ services:
####################################################################################################################
# Table settings
####################################################################################################################
App\DataTables\PartsDataTable:
arguments:
$visible_columns: '%partdb.table.parts.default_columns%'
App\DataTables\Helpers\ColumnSortHelper:
shared: false # Service has a state so not share it between different tables
@@ -192,6 +246,14 @@ services:
$fontDirectory: '%kernel.project_dir%/var/dompdf/fonts/'
$tmpDirectory: '%kernel.project_dir%/var/dompdf/tmp/'
####################################################################################################################
# Trees
####################################################################################################################
App\Services\Trees\TreeViewGenerator:
arguments:
$rootNodeExpandedByDefault: '%partdb.sidebar.root_expanded%'
$rootNodeEnabled: '%partdb.sidebar.root_node_enable%'
####################################################################################################################
# Part info provider system
####################################################################################################################
@@ -199,12 +261,76 @@ services:
arguments:
$providers: !tagged_iterator 'app.info_provider'
App\Services\InfoProviderSystem\Providers\Element14Provider:
arguments:
$api_key: '%env(string:PROVIDER_ELEMENT14_KEY)%'
$store_id: '%env(string:PROVIDER_ELEMENT14_STORE_ID)%'
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\TMEClient:
arguments:
$secret: '%env(string:PROVIDER_TME_SECRET)%'
$token: '%env(string:PROVIDER_TME_KEY)%'
App\Services\InfoProviderSystem\Providers\TMEProvider:
arguments:
$currency: '%env(string:PROVIDER_TME_CURRENCY)%'
$country: '%env(string:PROVIDER_TME_COUNTRY)%'
$language: '%env(string:PROVIDER_TME_LANGUAGE)%'
$get_gross_prices: '%env(bool:PROVIDER_TME_GET_GROSS_PRICES)%'
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)%'
App\Services\InfoProviderSystem\Providers\MouserProvider:
arguments:
$api_key: '%env(string:PROVIDER_MOUSER_KEY)%'
$language: '%env(string:PROVIDER_MOUSER_SEARCH_WITH_SIGNUP_LANGUAGE)%'
$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)%'
App\Services\InfoProviderSystem\Providers\OEMSecretsProvider:
arguments:
$api_key: '%env(string:PROVIDER_OEMSECRETS_KEY)%'
$country_code: '%env(string:PROVIDER_OEMSECRETS_COUNTRY_CODE)%'
$currency: '%env(PROVIDER_OEMSECRETS_CURRENCY)%'
$zero_price: '%env(PROVIDER_OEMSECRETS_ZERO_PRICE)%'
$set_param: '%env(PROVIDER_OEMSECRETS_SET_PARAM)%'
$sort_criteria: '%env(PROVIDER_OEMSECRETS_SORT_CRITERIA)%'
####################################################################################################################
# API system
####################################################################################################################
App\State\PartDBInfoProvider:
arguments:
$default_uri: '%partdb.default_uri%'
$global_locale: '%partdb.locale%'
$global_timezone: '%partdb.timezone%'
####################################################################################################################
# EDA system
####################################################################################################################
App\Services\EDA\KiCadHelper:
arguments:
$category_depth: '%env(int:EDA_KICAD_CATEGORY_DEPTH)%'
####################################################################################################################
# Symfony overrides
@@ -229,6 +355,7 @@ services:
####################################################################################################################
App\Controller\RedirectController:
arguments:
$default_locale: '%partdb.locale%'
$enforce_index_php: '%env(bool:NO_URL_REWRITE_AVAILABLE)%'
App\Doctrine\Purger\ResetAutoIncrementPurgerFactory:
@@ -243,6 +370,14 @@ services:
arguments:
$project_dir: '%kernel.project_dir%'
App\Services\System\UpdateAvailableManager:
arguments:
$check_for_updates: '%partdb.check_for_updates%'
App\Services\System\BannerHelper:
arguments:
$partdb_banner: '%partdb.banner%'
$project_dir: '%kernel.project_dir%'
App\Doctrine\Middleware\MySQLSSLConnectionMiddlewareWrapper:
arguments:

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

@@ -39,7 +39,7 @@
"@ckeditor/ckeditor5-block-quote": "^44.0.0",
"@ckeditor/ckeditor5-code-block": "^44.0.0",
"@ckeditor/ckeditor5-dev-translations": "^43.0.1",
"@ckeditor/ckeditor5-dev-utils": "^43.0.1",
"@ckeditor/ckeditor5-dev-utils": "43.0.*",
"@ckeditor/ckeditor5-editor-classic": "^44.0.0",
"@ckeditor/ckeditor5-essentials": "^44.0.0",
"@ckeditor/ckeditor5-find-and-replace": "^44.0.0",

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Command\Currencies;
use App\Settings\SystemSettings\LocalizationSettings;
use Symfony\Component\Console\Attribute\AsCommand;
use App\Entity\PriceInformations\Currency;
use App\Services\Tools\ExchangeRateUpdater;
@@ -40,7 +39,7 @@ use function strlen;
#[AsCommand('partdb:currencies:update-exchange-rates|partdb:update-exchange-rates|app:update-exchange-rates', 'Updates the currency exchange rates.')]
class UpdateExchangeRatesCommand extends Command
{
public function __construct(protected EntityManagerInterface $em, protected ExchangeRateUpdater $exchangeRateUpdater, private readonly LocalizationSettings $localizationSettings)
public function __construct(protected string $base_current, protected EntityManagerInterface $em, protected ExchangeRateUpdater $exchangeRateUpdater)
{
parent::__construct();
}
@@ -55,13 +54,13 @@ class UpdateExchangeRatesCommand extends Command
$io = new SymfonyStyle($input, $output);
//Check for valid base current
if (3 !== strlen($this->localizationSettings->baseCurrency)) {
if (3 !== strlen($this->base_current)) {
$io->error('Chosen Base current is not valid. Check your settings!');
return Command::FAILURE;
}
$io->note('Update currency exchange rates with base currency: '.$this->localizationSettings->baseCurrency);
$io->note('Update currency exchange rates with base currency: '.$this->base_current);
//Check what currencies we need to update:
$iso_code = $input->getArgument('iso_code');

View File

@@ -28,7 +28,6 @@ use App\Entity\Attachments\Attachment;
use App\Form\Filters\AttachmentFilterType;
use App\Services\Attachments\AttachmentManager;
use App\Services\Trees\NodesListBuilder;
use App\Settings\BehaviorSettings\TableSettings;
use Omines\DataTablesBundle\DataTableFactory;
use RuntimeException;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -99,8 +98,7 @@ class AttachmentFileController extends AbstractController
}
#[Route(path: '/attachment/list', name: 'attachment_list')]
public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder,
TableSettings $tableSettings): Response
public function attachmentsTable(Request $request, DataTableFactory $dataTableFactory, NodesListBuilder $nodesListBuilder): Response
{
$this->denyAccessUnlessGranted('@attachments.list_attachments');
@@ -112,7 +110,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])
->handleRequest($request);
if ($table->isCallback()) {

View File

@@ -38,7 +38,6 @@ use App\Services\LogSystem\LogEntryExtraFormatter;
use App\Services\LogSystem\LogLevelHelper;
use App\Services\LogSystem\LogTargetHelper;
use App\Services\LogSystem\TimeTravel;
use App\Settings\BehaviorSettings\TableSettings;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use Omines\DataTablesBundle\DataTableFactory;
@@ -59,7 +58,7 @@ class LogController extends AbstractController
}
#[Route(path: '/', name: 'log_view')]
public function showLogs(Request $request, DataTableFactory $dataTable, TableSettings $tableSettings): Response
public function showLogs(Request $request, DataTableFactory $dataTable): Response
{
$this->denyAccessUnlessGranted('@system.show_logs');
@@ -73,7 +72,7 @@ class LogController extends AbstractController
$table = $dataTable->createFromType(LogDataTable::class, [
'filter' => $filter,
], ['pageLength' => $tableSettings->fullDefaultPageSize])
])
->handleRequest($request);
if ($table->isCallback()) {

View File

@@ -36,7 +36,6 @@ use App\Exceptions\InvalidRegexException;
use App\Form\Filters\PartFilterType;
use App\Services\Parts\PartsTableActionHandler;
use App\Services\Trees\NodesListBuilder;
use App\Settings\BehaviorSettings\TableSettings;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\ORM\EntityManagerInterface;
use Omines\DataTablesBundle\DataTableFactory;
@@ -52,12 +51,7 @@ use function Symfony\Component\Translation\t;
class PartListsController extends AbstractController
{
public function __construct(private readonly EntityManagerInterface $entityManager,
private readonly NodesListBuilder $nodesListBuilder,
private readonly DataTableFactory $dataTableFactory,
private readonly TranslatorInterface $translator,
private readonly TableSettings $tableSettings
)
public function __construct(private readonly EntityManagerInterface $entityManager, private readonly NodesListBuilder $nodesListBuilder, private readonly DataTableFactory $dataTableFactory, private readonly TranslatorInterface $translator)
{
}
@@ -161,7 +155,11 @@ 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),
['lengthMenu' => PartsDataTable::LENGTH_MENU]
)
->handleRequest($request);
if ($table->isCallback()) {

View File

@@ -31,7 +31,6 @@ use App\Form\ProjectSystem\ProjectBuildType;
use App\Helpers\Projects\ProjectBuildRequest;
use App\Services\ImportExportSystem\BOMImporter;
use App\Services\ProjectSystem\ProjectBuildHelper;
use App\Settings\BehaviorSettings\TableSettings;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\EntityManagerInterface;
use League\Csv\SyntaxError;
@@ -56,12 +55,11 @@ class ProjectController extends AbstractController
}
#[Route(path: '/{id}/info', name: 'project_info', requirements: ['id' => '\d+'])]
public function info(Project $project, Request $request, ProjectBuildHelper $buildHelper, TableSettings $tableSettings): Response
public function info(Project $project, Request $request, ProjectBuildHelper $buildHelper): Response
{
$this->denyAccessUnlessGranted('read', $project);
$table = $this->dataTableFactory->createFromType(ProjectBomEntriesDataTable::class, ['project' => $project],
['pageLength' => $tableSettings->fullDefaultPageSize])
$table = $this->dataTableFactory->createFromType(ProjectBomEntriesDataTable::class, ['project' => $project])
->handleRequest($request);
if ($table->isCallback()) {

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace App\Controller;
use App\Entity\UserSystem\User;
use App\Settings\SystemSettings\LocalizationSettings;
use function function_exists;
use function in_array;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@@ -36,7 +35,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class RedirectController extends AbstractController
{
public function __construct(private readonly LocalizationSettings $localizationSettings, protected TranslatorInterface $translator, protected bool $enforce_index_php)
public function __construct(protected string $default_locale, protected TranslatorInterface $translator, protected bool $enforce_index_php)
{
}
@@ -47,7 +46,7 @@ class RedirectController extends AbstractController
public function addLocalePart(Request $request): RedirectResponse
{
//By default, we use the global default locale
$locale = $this->localizationSettings->locale;
$locale = $this->default_locale;
//Check if a user has set a preferred language setting:
$user = $this->getUser();

View File

@@ -1,72 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\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;
use Symfony\Contracts\Cache\TagAwareCacheInterface;
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, TagAwareCacheInterface $cache): Response
{
$this->denyAccessUnlessGranted('@config.change_system_settings');
//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);
//It might be possible, that the tree settings have changed, so clear the cache
$cache->invalidateTags(['tree_treeview', 'sidebar_tree_update']);
}
//Render the form
return $this->render('settings/settings.html.twig', [
'form' => $form
]);
}
}

View File

@@ -29,7 +29,6 @@ use App\Services\Doctrine\DBInfoHelper;
use App\Services\Doctrine\NatsortDebugHelper;
use App\Services\Misc\GitVersionInfo;
use App\Services\System\UpdateAvailableManager;
use App\Settings\AppSettings;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -48,8 +47,7 @@ class ToolsController extends AbstractController
#[Route(path: '/server_infos', name: 'tools_server_infos')]
public function systemInfos(GitVersionInfo $versionInfo, DBInfoHelper $DBInfoHelper, NatsortDebugHelper $natsortDebugHelper,
AttachmentSubmitHandler $attachmentSubmitHandler, UpdateAvailableManager $updateAvailableManager,
AppSettings $settings): Response
AttachmentSubmitHandler $attachmentSubmitHandler, UpdateAvailableManager $updateAvailableManager): Response
{
$this->denyAccessUnlessGranted('@system.server_infos');
@@ -57,23 +55,23 @@ class ToolsController extends AbstractController
//Part-DB section
'git_branch' => $versionInfo->getGitBranchName(),
'git_commit' => $versionInfo->getGitCommitHash(),
'default_locale' => $settings->system->localization->locale,
'default_timezone' => $settings->system->localization->timezone,
'default_currency' => $settings->system->localization->baseCurrency,
'default_theme' => $settings->system->customization->theme,
'default_locale' => $this->getParameter('partdb.locale'),
'default_timezone' => $this->getParameter('partdb.timezone'),
'default_currency' => $this->getParameter('partdb.default_currency'),
'default_theme' => $this->getParameter('partdb.global_theme'),
'enabled_locales' => $this->getParameter('partdb.locale_menu'),
'demo_mode' => $this->getParameter('partdb.demo_mode'),
'use_gravatar' => $settings->system->privacy->useGravatar,
'gdpr_compliance' => $this->getParameter('partdb.gdpr_compliance'),
'use_gravatar' => $this->getParameter('partdb.users.use_gravatar'),
'email_password_reset' => $this->getParameter('partdb.users.email_pw_reset'),
'environment' => $this->getParameter('kernel.environment'),
'is_debug' => $this->getParameter('kernel.debug'),
'email_sender' => $this->getParameter('partdb.mail.sender_email'),
'email_sender_name' => $this->getParameter('partdb.mail.sender_name'),
'allow_attachments_downloads' => $settings->system->attachments->allowDownloads,
'allow_attachments_downloads' => $this->getParameter('partdb.attachments.allow_downloads'),
'detailed_error_pages' => $this->getParameter('partdb.error_pages.show_help'),
'error_page_admin_email' => $this->getParameter('partdb.error_pages.admin_email'),
'configured_max_file_size' => $settings->system->attachments->maxFileSize,
'configured_max_file_size' => $this->getParameter('partdb.attachments.max_file_size'),
'effective_max_file_size' => $attachmentSubmitHandler->getMaximumAllowedUploadSize(),
'saml_enabled' => $this->getParameter('partdb.saml.enabled'),

View File

@@ -72,8 +72,7 @@ class ColumnSortHelper
* Apply the visibility configuration to the given DataTable and configure the columns.
* @param DataTable $dataTable
* @param string|array $visible_columns Either a list or a comma separated string of column names, which should
* be visible by default. If a column is not listed here, it will be hidden by default. If an array of enum values are passed,
* their value will be used as the column name.
* be visible by default. If a column is not listed here, it will be hidden by default.
* @return void
*/
public function applyVisibilityAndConfigureColumns(DataTable $dataTable, string|array $visible_columns,
@@ -84,14 +83,6 @@ class ColumnSortHelper
$visible_columns = array_map(trim(...), explode(",", $visible_columns));
}
//If $visible_columns is a list of enum values, convert them to the column names
foreach ($visible_columns as &$value) {
if ($value instanceof \BackedEnum) {
$value = $value->value;
}
}
unset ($value);
$processed_columns = [];
//First add all columns which visibility is not configurable

View File

@@ -45,7 +45,6 @@ use App\Entity\Parts\PartLot;
use App\Entity\ProjectSystem\Project;
use App\Services\EntityURLGenerator;
use App\Services\Formatters\AmountFormatter;
use App\Settings\BehaviorSettings\TableSettings;
use Doctrine\ORM\AbstractQuery;
use Doctrine\ORM\QueryBuilder;
use Omines\DataTablesBundle\Adapter\Doctrine\ORM\SearchCriteriaProvider;
@@ -66,8 +65,8 @@ final class PartsDataTable implements DataTableTypeInterface
private readonly AmountFormatter $amountFormatter,
private readonly PartDataTableHelper $partDataTableHelper,
private readonly Security $security,
private readonly string $visible_columns,
private readonly ColumnSortHelper $csh,
private readonly TableSettings $tableSettings,
) {
}
@@ -247,7 +246,7 @@ final class PartsDataTable implements DataTableTypeInterface
]);
//Apply the user configured order and visibility and add the columns to the table
$this->csh->applyVisibilityAndConfigureColumns($dataTable, $this->tableSettings->partsDefaultColumns,
$this->csh->applyVisibilityAndConfigureColumns($dataTable, $this->visible_columns,
"TABLE_PARTS_DEFAULT_COLUMNS");
$dataTable->addOrderBy('name')

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

@@ -197,7 +197,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
/**
* @var string|null The language/locale the user prefers
*/
#[Assert\Locale]
#[Assert\Language]
#[Groups(['full', 'import', 'user:read'])]
#[ORM\Column(name: 'config_language', type: Types::STRING, nullable: true)]
protected ?string $language = '';

View File

@@ -39,8 +39,6 @@ use App\Services\LogSystem\EventCommentHelper;
use App\Services\LogSystem\EventLogger;
use App\Services\LogSystem\EventUndoHelper;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsDoctrineListener;
use App\Settings\SystemSettings\HistorySettings;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Event\OnFlushEventArgs;
use Doctrine\ORM\Event\PostFlushEventArgs;
@@ -76,15 +74,14 @@ class EventLoggerListener
];
protected const MAX_STRING_LENGTH = 2000;
protected bool $save_new_data;
public function __construct(
protected EventLogger $logger,
protected SerializerInterface $serializer,
protected EventCommentHelper $eventCommentHelper,
private readonly HistorySettings $settings,
protected PropertyAccessorInterface $propertyAccessor,
protected EventUndoHelper $eventUndoHelper)
public function __construct(protected EventLogger $logger, protected SerializerInterface $serializer, protected EventCommentHelper $eventCommentHelper,
protected bool $save_changed_fields, protected bool $save_changed_data, protected bool $save_removed_data, bool $save_new_data,
protected PropertyAccessorInterface $propertyAccessor, protected EventUndoHelper $eventUndoHelper)
{
//This option only makes sense if save_changed_data is true
$this->save_new_data = $save_new_data && $save_changed_data;
}
public function onFlush(OnFlushEventArgs $eventArgs): void
@@ -203,14 +200,14 @@ class EventLoggerListener
if ($this->eventUndoHelper->isUndo()) {
$log->setUndoneEvent($this->eventUndoHelper->getUndoneEvent(), $this->eventUndoHelper->getMode());
}
if ($this->settings->saveRemovedData) {
if ($this->save_removed_data) {
//The 4th param is important here, as we delete the element...
$this->saveChangeSet($entity, $log, $em, true);
}
$this->logger->logFromOnFlush($log);
//Check if we have to log CollectionElementDeleted entries
if ($this->settings->saveOldData) {
if ($this->save_changed_data) {
$metadata = $em->getClassMetadata($entity::class);
$mappings = $metadata->getAssociationMappings();
//Check if class is whitelisted for CollectionElementDeleted entry
@@ -246,9 +243,9 @@ class EventLoggerListener
}
$log = new ElementEditedLogEntry($entity);
if ($this->settings->saveOldData) {
if ($this->save_changed_data) {
$this->saveChangeSet($entity, $log, $em);
} elseif ($this->settings->saveChangedFields) {
} elseif ($this->save_changed_fields) {
$changed_fields = array_keys($uow->getEntityChangeSet($entity));
//Remove lastModified field, as this is always changed (gives us no additional info)
$changed_fields = array_diff($changed_fields, ['lastModified']);
@@ -316,7 +313,7 @@ class EventLoggerListener
$changeSet = $uow->getEntityChangeSet($entity);
$old_data = array_combine(array_keys($changeSet), array_column($changeSet, 0));
//If save_new_data is enabled, we extract it from the change set
if ($this->settings->saveNewData && $this->settings->saveOldData) { //Only useful if we save old data too
if ($this->save_new_data) {
$new_data = array_combine(array_keys($changeSet), array_column($changeSet, 1));
}
}

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace App\EventSubscriber\UserSystem;
use App\Entity\UserSystem\User;
use App\Settings\SystemSettings\LocalizationSettings;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
@@ -34,7 +33,7 @@ use Symfony\Component\HttpKernel\KernelEvents;
*/
final class SetUserTimezoneSubscriber implements EventSubscriberInterface
{
public function __construct(private readonly LocalizationSettings $localizationSettings, private readonly Security $security)
public function __construct(private readonly string $default_timezone, private readonly Security $security)
{
}
@@ -49,8 +48,8 @@ final class SetUserTimezoneSubscriber implements EventSubscriberInterface
}
//Fill with default value if needed
if (null === $timezone && $this->localizationSettings->timezone !== '') {
$timezone = $this->localizationSettings->timezone;
if (null === $timezone && $this->default_timezone !== '') {
$timezone = $this->default_timezone;
}
//If timezone was configured anywhere set it, otherwise just use the one from php.ini

View File

@@ -25,7 +25,6 @@ namespace App\Form\AdminPages;
use App\Entity\PriceInformations\Currency;
use App\Entity\ProjectSystem\Project;
use App\Entity\UserSystem\Group;
use App\Services\LogSystem\EventCommentType;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\Base\AbstractStructuralDBElement;
@@ -153,7 +152,7 @@ class BaseEntityAdminForm extends AbstractType
$builder->add('log_comment', TextType::class, [
'label' => 'edit.log_comment',
'mapped' => false,
'required' => $this->eventCommentNeededHelper->isCommentNeeded($is_new ? EventCommentType::DATASTRUCTURE_CREATE: EventCommentType::DATASTRUCTURE_EDIT),
'required' => $this->eventCommentNeededHelper->isCommentNeeded($is_new ? 'datastructure_create': 'datastructure_edit'),
'empty_data' => null,
]);

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Form\AdminPages;
use App\Settings\SystemSettings\LocalizationSettings;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Base\AbstractNamedDBElement;
use App\Form\Type\BigDecimalMoneyType;
@@ -33,7 +32,7 @@ use Symfony\Component\Form\FormBuilderInterface;
class CurrencyAdminForm extends BaseEntityAdminForm
{
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, private readonly LocalizationSettings $localizationSettings)
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, private readonly string $base_currency)
{
parent::__construct($security, $eventCommentNeededHelper);
}
@@ -52,7 +51,7 @@ class CurrencyAdminForm extends BaseEntityAdminForm
$builder->add('exchange_rate', BigDecimalMoneyType::class, [
'required' => false,
'label' => 'currency.edit.exchange_rate',
'currency' => $this->localizationSettings->baseCurrency,
'currency' => $this->base_currency,
'scale' => 6,
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
]);

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Form\AdminPages;
use App\Settings\SystemSettings\LocalizationSettings;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\PriceInformations\Currency;
@@ -33,7 +32,7 @@ use Symfony\Component\Form\FormBuilderInterface;
class SupplierForm extends CompanyForm
{
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, private readonly LocalizationSettings $localizationSettings)
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, protected string $base_currency)
{
parent::__construct($security, $eventCommentNeededHelper);
}
@@ -54,7 +53,7 @@ class SupplierForm extends CompanyForm
$builder->add('shipping_costs', BigDecimalMoneyType::class, [
'required' => false,
'currency' => $this->localizationSettings->baseCurrency,
'currency' => $this->base_currency,
'scale' => 3,
'label' => 'supplier.shipping_costs.label',
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Form;
use App\Settings\SystemSettings\AttachmentsSettings;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Attachments\Attachment;
use App\Entity\Attachments\AttachmentType;
@@ -55,7 +54,9 @@ class AttachmentFormType extends AbstractType
protected Security $security,
protected AttachmentSubmitHandler $submitHandler,
protected TranslatorInterface $translator,
protected AttachmentsSettings $settings,
protected bool $allow_attachments_download,
protected bool $download_by_default,
protected string $max_file_size
) {
}
@@ -107,7 +108,7 @@ class AttachmentFormType extends AbstractType
'required' => false,
'label' => 'attachment.edit.download_url',
'mapped' => false,
'disabled' => !$this->settings->allowDownloads,
'disabled' => !$this->allow_attachments_download,
]);
$builder->add('file', FileType::class, [
@@ -176,7 +177,7 @@ class AttachmentFormType extends AbstractType
//If the attachment should be downloaded by default (and is download allowed at all), register a listener,
// which sets the downloadURL checkbox to true for new attachments
if ($this->settings->downloadByDefault && $this->settings->allowDownloads) {
if ($this->download_by_default && $this->allow_attachments_download) {
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event): void {
$form = $event->getForm();
$attachment = $form->getData();
@@ -203,7 +204,7 @@ class AttachmentFormType extends AbstractType
{
$resolver->setDefaults([
'data_class' => Attachment::class,
'max_file_size' => $this->settings->maxFileSize,
'max_file_size' => $this->max_file_size,
'allow_builtins' => true,
]);
}

View File

@@ -1,49 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\Form\History;
use App\Services\LogSystem\EventCommentType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* The type for the "enforceComments" setting in the HistorySettings.
*/
class EnforceEventCommentTypesType extends AbstractType
{
public function getParent(): string
{
return EnumType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'multiple' => true,
'class' => EventCommentType::class,
'empty_data' => [],
]);
}
}

View File

@@ -40,7 +40,6 @@ use App\Form\Type\SIUnitType;
use App\Form\Type\StructuralEntityType;
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\LogSystem\EventCommentNeededHelper;
use App\Services\LogSystem\EventCommentType;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
@@ -266,7 +265,7 @@ class PartBaseType extends AbstractType
$builder->add('log_comment', TextType::class, [
'label' => 'edit.log_comment',
'mapped' => false,
'required' => $this->event_comment_needed_helper->isCommentNeeded($new_part ? EventCommentType::PART_CREATE : EventCommentType::PART_EDIT),
'required' => $this->event_comment_needed_helper->isCommentNeeded($new_part ? 'part_create' : 'part_edit'),
'empty_data' => null,
]);

View File

@@ -1,60 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\Form;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SelectTypeOrderExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [
ChoiceType::class,
EnumType::class
];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('ordered', false);
$resolver->setDefault('by_reference', function (Options $options) {
//Disable by_reference if the field is ordered (otherwise the order will be lost)
return !$options['ordered'];
});
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
//Pass the data in ordered form to the frontend controller, so it can make the items appear in the correct order.
if ($options['ordered']) {
$view->vars['attr']['data-ordered-value'] = json_encode($form->getViewData(), JSON_THROW_ON_ERROR);
}
}
}

View File

@@ -25,7 +25,6 @@ namespace App\Form\Type;
use App\Entity\PriceInformations\Currency;
use App\Form\Type\Helper\StructuralEntityChoiceHelper;
use App\Services\Trees\NodesListBuilder;
use App\Settings\SystemSettings\LocalizationSettings;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Intl\Currencies;
use Symfony\Component\OptionsResolver\Options;
@@ -37,7 +36,7 @@ use Symfony\Contracts\Translation\TranslatorInterface;
*/
class CurrencyEntityType extends StructuralEntityType
{
public function __construct(EntityManagerInterface $em, NodesListBuilder $builder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choiceHelper, private readonly LocalizationSettings $localizationSettings)
public function __construct(EntityManagerInterface $em, NodesListBuilder $builder, TranslatorInterface $translator, StructuralEntityChoiceHelper $choiceHelper, protected ?string $base_currency)
{
parent::__construct($em, $builder, $translator, $choiceHelper);
}
@@ -58,7 +57,7 @@ class CurrencyEntityType extends StructuralEntityType
$resolver->setDefault('empty_message', function (Options $options) {
//By default, we use the global base currency:
$iso_code = $this->localizationSettings->baseCurrency;
$iso_code = $this->base_currency;
if ($options['base_currency']) { //Allow to override it
$iso_code = $options['base_currency'];

View File

@@ -1,53 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\Form\Type;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\LocaleType;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* A locale select field that uses the preferred languages from the configuration.
*/
class LocaleSelectType extends AbstractType
{
public function __construct(#[Autowire(param: 'partdb.locale_menu')] private readonly array $preferred_languages)
{
}
public function getParent(): string
{
return LocaleType::class;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'preferred_choices' => $this->preferred_languages,
]);
}
}

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Form;
use App\Form\Type\LocaleSelectType;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Base\AbstractNamedDBElement;
use App\Entity\UserSystem\Group;
@@ -36,6 +35,7 @@ use App\Form\Type\ThemeChoiceType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\ResetType;
@@ -140,10 +140,11 @@ class UserAdminForm extends AbstractType
])
//Config section
->add('language', LocaleSelectType::class, [
->add('language', LanguageType::class, [
'required' => false,
'placeholder' => 'user_settings.language.placeholder',
'label' => 'user.language_select',
'preferred_choices' => ['en', 'de'],
'disabled' => !$this->security->isGranted('change_user_settings', $entity),
])
->add('timezone', TimezoneType::class, [

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace App\Form;
use App\Form\Type\LocaleSelectType;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\UserSystem\User;
use App\Form\Type\CurrencyEntityType;
@@ -34,6 +33,7 @@ use Symfony\Component\Form\Event\PreSetDataEvent;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
use Symfony\Component\Form\Extension\Core\Type\ResetType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
@@ -47,7 +47,7 @@ class UserSettingsType extends AbstractType
{
public function __construct(protected Security $security,
protected bool $demo_mode,
)
#[Autowire(param: 'partdb.locale_menu')] private readonly array $preferred_languages)
{
}
@@ -107,11 +107,12 @@ class UserSettingsType extends AbstractType
'mode' => 'markdown-full',
'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode,
])
->add('language', LocaleSelectType::class, [
->add('language', LanguageType::class, [
'disabled' => $this->demo_mode,
'required' => false,
'placeholder' => 'user_settings.language.placeholder',
'label' => 'user.language_select',
'preferred_choices' => $this->preferred_languages,
])
->add('timezone', TimezoneType::class, [
'disabled' => $this->demo_mode,

View File

@@ -40,7 +40,6 @@ use App\Entity\Attachments\StorageLocationAttachment;
use App\Entity\Attachments\SupplierAttachment;
use App\Entity\Attachments\UserAttachment;
use App\Exceptions\AttachmentDownloadException;
use App\Settings\SystemSettings\AttachmentsSettings;
use Hshn\Base64EncodedFile\HttpFoundation\File\Base64EncodedFile;
use Hshn\Base64EncodedFile\HttpFoundation\File\UploadedBase64EncodedFile;
use const DIRECTORY_SEPARATOR;
@@ -65,14 +64,12 @@ class AttachmentSubmitHandler
'asp', 'cgi', 'py', 'pl', 'exe', 'aspx', 'js', 'mjs', 'jsp', 'css', 'jar', 'html', 'htm', 'shtm', 'shtml', 'htaccess',
'htpasswd', ''];
public function __construct(
protected AttachmentPathResolver $pathResolver,
protected HttpClientInterface $httpClient,
protected MimeTypesInterface $mimeTypes,
protected FileTypeFilterTools $filterTools,
protected AttachmentsSettings $settings,
protected readonly SVGSanitizer $SVGSanitizer,
)
public function __construct(protected AttachmentPathResolver $pathResolver, protected bool $allow_attachments_downloads,
protected HttpClientInterface $httpClient, protected MimeTypesInterface $mimeTypes, protected readonly SVGSanitizer $SVGSanitizer,
protected FileTypeFilterTools $filterTools, /**
* @var string The user configured maximum upload size. This is a string like "10M" or "1G" and will be converted to
*/
protected string $max_upload_size)
{
//The mapping used to determine which folder will be used for an attachment type
$this->folder_mapping = [
@@ -340,7 +337,7 @@ class AttachmentSubmitHandler
protected function downloadURL(Attachment $attachment, bool $secureAttachment): Attachment
{
//Check if we are allowed to download files
if (!$this->settings->allowDownloads) {
if (!$this->allow_attachments_downloads) {
throw new RuntimeException('Download of attachments is not allowed!');
}
@@ -499,7 +496,7 @@ class AttachmentSubmitHandler
$this->max_upload_size_bytes = min(
$this->parseFileSizeString(ini_get('post_max_size')),
$this->parseFileSizeString(ini_get('upload_max_filesize')),
$this->parseFileSizeString($this->settings->maxFileSize)
$this->parseFileSizeString($this->max_upload_size),
);
return $this->max_upload_size_bytes;

View File

@@ -112,12 +112,12 @@ class AttachmentURLGenerator
/**
* Returns a URL to a thumbnail of the attachment file.
* For external files the original URL is returned.
* @return string|null The URL or null if the attachment file is not existing
* @return string|null The URL or null if the attachment file is not existing or is invalid
*/
public function getThumbnailURL(Attachment $attachment, string $filter_name = 'thumbnail_sm'): ?string
{
if (!$attachment->isPicture()) {
throw new InvalidArgumentException('Thumbnail creation only works for picture attachments!');
return null;
}
if (!$attachment->hasInternal()){

View File

@@ -29,7 +29,6 @@ use App\Entity\Parts\Part;
use App\Services\Cache\ElementCacheTagGenerator;
use App\Services\EntityURLGenerator;
use App\Services\Trees\NodesListBuilder;
use App\Settings\MiscSettings\KiCadEDASettings;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@@ -40,9 +39,6 @@ use Symfony\Contracts\Translation\TranslatorInterface;
class KiCadHelper
{
/** @var int The maximum level of the shown categories. 0 Means only the top level categories are shown. -1 means only a single one containing */
private readonly int $category_depth;
public function __construct(
private readonly NodesListBuilder $nodesListBuilder,
private readonly TagAwareCacheInterface $kicadCache,
@@ -51,9 +47,9 @@ class KiCadHelper
private readonly UrlGeneratorInterface $urlGenerator,
private readonly EntityURLGenerator $entityURLGenerator,
private readonly TranslatorInterface $translator,
KiCadEDASettings $kiCadEDASettings,
/** The maximum level of the shown categories. 0 Means only the top level categories are shown. -1 means only a single one containing */
private readonly int $category_depth,
) {
$this->category_depth = $kiCadEDASettings->categoryDepth;
}
/**

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace App\Services\Formatters;
use App\Entity\PriceInformations\Currency;
use App\Settings\SystemSettings\LocalizationSettings;
use Locale;
use NumberFormatter;
@@ -31,7 +30,7 @@ class MoneyFormatter
{
protected string $locale;
public function __construct(private readonly LocalizationSettings $localizationSettings)
public function __construct(protected string $base_currency)
{
$this->locale = Locale::getDefault();
}
@@ -46,7 +45,7 @@ class MoneyFormatter
*/
public function format(string|float $value, ?Currency $currency = null, int $decimals = 5, bool $show_all_digits = false): string
{
$iso_code = $this->localizationSettings->baseCurrency;
$iso_code = $this->base_currency;
if ($currency instanceof Currency && ($currency->getIsoCode() !== '')) {
$iso_code = $currency->getIsoCode();
}

View File

@@ -57,6 +57,7 @@ class EntityImporter
/**
* Creates many entries at once, based on a (text) list of name.
* The created entities are not persisted to database yet, so you have to do it yourself.
* It returns all entities in the hierachy chain (even if they are already persisted).
*
* @template T of AbstractNamedDBElement
* @param string $lines The list of names seperated by \n
@@ -132,32 +133,38 @@ class EntityImporter
//We can only use the getNewEntityFromPath function, if the repository is a StructuralDBElementRepository
if ($repo instanceof StructuralDBElementRepository) {
$entities = $repo->getNewEntityFromPath($new_path);
$entity = end($entities);
if ($entity === false) {
if ($entities === []) {
throw new InvalidArgumentException('getNewEntityFromPath returned an empty array!');
}
} else { //Otherwise just create a new entity
$entity = new $class_name;
$entity->setName($name);
$entities = [$entity];
}
//Validate entity
$tmp = $this->validator->validate($entity);
//If no error occured, write entry to DB:
if (0 === count($tmp)) {
$valid_entities[] = $entity;
} else { //Otherwise log error
$errors[] = [
'entity' => $entity,
'violations' => $tmp,
];
foreach ($entities as $entity) {
$tmp = $this->validator->validate($entity);
//If no error occured, write entry to DB:
if (0 === count($tmp)) {
$valid_entities[] = $entity;
} else { //Otherwise log error
$errors[] = [
'entity' => $entity,
'violations' => $tmp,
];
}
}
$last_element = $entity;
$last_element = end($entities);
if ($last_element === false) {
$last_element = null;
}
}
return $valid_entities;
//Only return objects once
return array_values(array_unique($valid_entities));
}
/**

View File

@@ -35,7 +35,6 @@ use App\Entity\Parts\Supplier;
use App\Entity\PriceInformations\Currency;
use App\Entity\PriceInformations\Orderdetail;
use App\Entity\PriceInformations\Pricedetail;
use App\Settings\SystemSettings\LocalizationSettings;
use Brick\Math\BigDecimal;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Intl\Currencies;
@@ -48,7 +47,7 @@ class PKPartImporter
{
use PKImportHelperTrait;
public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor, private readonly LocalizationSettings $localizationSettings)
public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor, private readonly string $base_currency)
{
$this->em = $em;
$this->propertyAccessor = $propertyAccessor;
@@ -211,7 +210,7 @@ class PKPartImporter
$currency_iso_code = strtoupper($currency_iso_code);
//We do not have a currency for the base currency to be consistent with prices without currencies
if ($currency_iso_code === $this->localizationSettings->baseCurrency) {
if ($currency_iso_code === $this->base_currency) {
return null;
}

View File

@@ -43,7 +43,6 @@ 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\SystemSettings\LocalizationSettings;
use Doctrine\ORM\EntityManagerInterface;
/**
@@ -55,11 +54,8 @@ final class DTOtoEntityConverter
private const TYPE_DATASHEETS_NAME = 'Datasheet';
private const TYPE_IMAGE_NAME = 'Image';
private readonly string $base_currency;
public function __construct(private readonly EntityManagerInterface $em, LocalizationSettings $localizationSettings)
public function __construct(private readonly EntityManagerInterface $em, private readonly string $base_currency)
{
$this->base_currency = $localizationSettings->baseCurrency;
}
/**

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

@@ -29,7 +29,6 @@ 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\Element14Settings;
use Composer\CaBundle\CaBundle;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -46,8 +45,8 @@ class Element14Provider implements InfoProviderInterface
'rohsPhthalatesCompliant', 'SVHC', 'tariffCode', 'usEccn', 'hazardCode'];
private readonly HttpClientInterface $element14Client;
public function __construct(HttpClientInterface $element14Client, private readonly Element14Settings $settings)
public function __construct(HttpClientInterface $element14Client, private readonly string $api_key, private readonly string $store_id)
{
/* We use the mozilla CA from the composer ca bundle directly, as some debian systems seems to have problems
* with the SSL.COM CA, element14 uses. See https://github.com/Part-DB/Part-DB-server/issues/866
@@ -77,7 +76,7 @@ class Element14Provider implements InfoProviderInterface
public function isActive(): bool
{
return trim($this->settings->apiKey) !== '';
return $this->api_key !== '';
}
/**
@@ -89,11 +88,11 @@ class Element14Provider implements InfoProviderInterface
$response = $this->element14Client->request('GET', self::ENDPOINT_URL, [
'query' => [
'term' => $term,
'storeInfo.id' => $this->settings->storeId,
'storeInfo.id' => $this->store_id,
'resultsSettings.offset' => 0,
'resultsSettings.numberOfResults' => self::NUMBER_OF_RESULTS,
'resultsSettings.responseGroup' => 'large',
'callInfo.apiKey' => $this->settings->apiKey,
'callInfo.apiKey' => $this->api_key,
'callInfo.responseDataFormat' => 'json',
'versionNumber' => self::API_VERSION_NUMBER,
],
@@ -161,7 +160,7 @@ class Element14Provider implements InfoProviderInterface
$locale = 'en_US';
}
return 'https://' . $this->settings->storeId . '/productimages/standard/' . $locale . $image['baseName'];
return 'https://' . $this->store_id . '/productimages/standard/' . $locale . $image['baseName'];
}
/**
@@ -196,7 +195,7 @@ class Element14Provider implements InfoProviderInterface
public function getUsedCurrency(): string
{
//Decide based on the shop ID
return match ($this->settings->storeId) {
return match ($this->store_id) {
'bg.farnell.com', 'at.farnell.com', 'si.farnell.com', 'sk.farnell.com', 'ro.farnell.com', 'pt.farnell.com', 'nl.farnell.com', 'be.farnell.com', 'lv.farnell.com', 'lt.farnell.com', 'it.farnell.com', 'fr.farnell.com', 'fi.farnell.com', 'ee.farnell.com', 'es.farnell.com', 'ie.farnell.com', 'cpcireland.farnell.com', 'de.farnell.com' => 'EUR',
'cz.farnell.com' => 'CZK',
'dk.farnell.com' => 'DKK',
@@ -223,7 +222,7 @@ class Element14Provider implements InfoProviderInterface
'tw.element14.com' => 'TWD',
'kr.element14.com' => 'KRW',
'vn.element14.com' => 'VND',
default => throw new \RuntimeException('Unknown store ID: ' . $this->settings->storeId)
default => throw new \RuntimeException('Unknown store ID: ' . $this->store_id)
};
}

View File

@@ -29,7 +29,6 @@ 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;
@@ -40,7 +39,7 @@ class LCSCProvider implements InfoProviderInterface
public const DISTRIBUTOR_NAME = 'LCSC';
public function __construct(private readonly HttpClientInterface $lcscClient, private readonly LCSCSettings $settings)
public function __construct(private readonly HttpClientInterface $lcscClient, private readonly string $currency, private readonly bool $enabled = true)
{
}
@@ -63,7 +62,7 @@ class LCSCProvider implements InfoProviderInterface
// This provider is always active
public function isActive(): bool
{
return $this->settings->enabled;
return $this->enabled;
}
/**
@@ -74,7 +73,7 @@ class LCSCProvider implements InfoProviderInterface
{
$response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/product/detail", [
'headers' => [
'Cookie' => new Cookie('currencyCode', $this->settings->currency)
'Cookie' => new Cookie('currencyCode', $this->currency)
],
'query' => [
'productCode' => $id,
@@ -124,7 +123,7 @@ class LCSCProvider implements InfoProviderInterface
{
$response = $this->lcscClient->request('GET', self::ENDPOINT_URL . "/search/global", [
'headers' => [
'Cookie' => new Cookie('currencyCode', $this->settings->currency)
'Cookie' => new Cookie('currencyCode', $this->currency)
],
'query' => [
'keyword' => $term,
@@ -274,7 +273,7 @@ class LCSCProvider implements InfoProviderInterface
'kr.' => 'DKK',
'₹' => 'INR',
//Fallback to the configured currency
default => $this->settings->currency,
default => $this->currency,
};
}

View File

@@ -37,7 +37,6 @@ use App\Services\InfoProviderSystem\DTOs\FileDTO;
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Settings\InfoProviderSystem\MouserSettings;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -51,7 +50,10 @@ class MouserProvider implements InfoProviderInterface
public function __construct(
private readonly HttpClientInterface $mouserClient,
private readonly MouserSettings $settings,
private readonly string $api_key,
private readonly string $language,
private readonly string $options,
private readonly int $search_limit
) {
}
@@ -72,7 +74,7 @@ class MouserProvider implements InfoProviderInterface
public function isActive(): bool
{
return $this->settings->apiKey !== '' && $this->settings->apiKey !== null;
return $this->api_key !== '';
}
public function searchByKeyword(string $keyword): array
@@ -118,15 +120,15 @@ class MouserProvider implements InfoProviderInterface
$response = $this->mouserClient->request('POST', self::ENDPOINT_URL."/keyword", [
'query' => [
'apiKey' => $this->settings->apiKey
'apiKey' => $this->api_key,
],
'json' => [
'SearchByKeywordRequest' => [
'keyword' => $keyword,
'records' => $this->settings->searchLimit, //self::NUMBER_OF_RESULTS,
'records' => $this->search_limit, //self::NUMBER_OF_RESULTS,
'startingRecord' => 0,
'searchOptions' => $this->settings->searchOption->value,
'searchWithYourSignUpLanguage' => $this->settings->searchWithSignUpLanguage ? 'true' : 'false',
'searchOptions' => $this->options,
'searchWithYourSignUpLanguage' => $this->language,
]
],
]);
@@ -159,7 +161,7 @@ class MouserProvider implements InfoProviderInterface
$response = $this->mouserClient->request('POST', self::ENDPOINT_URL."/partnumber", [
'query' => [
'apiKey' => $this->settings->apiKey,
'apiKey' => $this->api_key,
],
'json' => [
'SearchByPartRequest' => [

View File

@@ -88,8 +88,6 @@ use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Services\InfoProviderSystem\DTOs\ParameterDTO;
use App\Settings\InfoProviderSystem\OEMSecretsSettings;
use App\Settings\InfoProviderSystem\OEMSecretsSortMode;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Psr\Cache\CacheItemPoolInterface;
@@ -101,7 +99,12 @@ class OEMSecretsProvider implements InfoProviderInterface
public function __construct(
private readonly HttpClientInterface $oemsecretsClient,
private readonly OEMSecretsSettings $settings,
private readonly string $api_key,
private readonly string $country_code,
private readonly string $currency,
private readonly string $zero_price,
private readonly string $set_param,
private readonly string $sort_criteria,
private readonly CacheItemPoolInterface $partInfoCache
)
{
@@ -265,7 +268,7 @@ class OEMSecretsProvider implements InfoProviderInterface
*/
public function isActive(): bool
{
return $this->settings->apiKey !== '';
return $this->api_key !== '';
}
@@ -321,9 +324,9 @@ class OEMSecretsProvider implements InfoProviderInterface
$response = $this->oemsecretsClient->request('GET', self::ENDPOINT_URL, [
'query' => [
'searchTerm' => $keyword,
'apiKey' => $this->settings->apiKey,
'currency' => $this->settings->currency,
'countryCode' => $this->settings->country,
'apiKey' => $this->api_key,
'currency' => $this->currency,
'countryCode' => $this->country_code,
],
]);
@@ -530,7 +533,7 @@ class OEMSecretsProvider implements InfoProviderInterface
// Extract prices
$priceDTOs = $this->getPrices($product);
if (empty($priceDTOs) && !$this->settings->keepZeroPrices) {
if (empty($priceDTOs) && (int)$this->zero_price === 0) {
return null; // Skip products without valid prices
}
@@ -554,7 +557,7 @@ class OEMSecretsProvider implements InfoProviderInterface
}
$imagesResults[$provider_id] = $this->getImages($product, $imagesResults[$provider_id] ?? []);
if ($this->settings->parseParams) {
if ($this->set_param == 1) {
$parametersResults[$provider_id] = $this->getParameters($product, $parametersResults[$provider_id] ?? []);
} else {
$parametersResults[$provider_id] = [];
@@ -579,7 +582,7 @@ class OEMSecretsProvider implements InfoProviderInterface
$regionB = $this->countryCodeToRegionMap[$countryCodeB] ?? '';
// If the map is empty or doesn't contain the key for $this->country_code, assign a placeholder region.
$regionForEnvCountry = $this->countryCodeToRegionMap[$this->settings->country] ?? '';
$regionForEnvCountry = $this->countryCodeToRegionMap[$this->country_code] ?? '';
// Convert to string before comparison to avoid mixed types
$countryCodeA = (string) $countryCodeA;
@@ -596,9 +599,9 @@ class OEMSecretsProvider implements InfoProviderInterface
}
// Step 1: country_code from the environment
if ($countryCodeA === $this->settings->country && $countryCodeB !== $this->settings->country) {
if ($countryCodeA === $this->country_code && $countryCodeB !== $this->country_code) {
return -1;
} elseif ($countryCodeA !== $this->settings->country && $countryCodeB === $this->settings->country) {
} elseif ($countryCodeA !== $this->country_code && $countryCodeB === $this->country_code) {
return 1;
}
@@ -678,8 +681,8 @@ class OEMSecretsProvider implements InfoProviderInterface
if (is_array($prices)) {
// Step 1: Check if prices exist in the preferred currency
if (isset($prices[$this->settings->currency]) && is_array($prices[$this->settings->currency])) {
$priceDetails = $prices[$this->$this->settings->currency];
if (isset($prices[$this->currency]) && is_array($prices[$this->currency])) {
$priceDetails = $prices[$this->currency];
foreach ($priceDetails as $priceDetail) {
if (
is_array($priceDetail) &&
@@ -691,7 +694,7 @@ class OEMSecretsProvider implements InfoProviderInterface
$priceDTOs[] = new PriceDTO(
minimum_discount_amount: (float)$priceDetail['unit_break'],
price: (string)$priceDetail['unit_price'],
currency_iso_code: $this->settings->currency,
currency_iso_code: $this->currency,
includes_tax: false,
price_related_quantity: 1.0
);
@@ -1290,7 +1293,7 @@ class OEMSecretsProvider implements InfoProviderInterface
private function sortResultsData(array &$resultsData, string $searchKeyword): void
{
// If the SORT_CRITERIA is not 'C' or 'M', do not sort
if ($this->settings->sortMode !== OEMSecretsSortMode::COMPLETENESS && $this->settings->sortMode !== OEMSecretsSortMode::MANUFACTURER) {
if ($this->sort_criteria !== 'C' && $this->sort_criteria !== 'M') {
return;
}
usort($resultsData, function ($a, $b) use ($searchKeyword) {
@@ -1329,9 +1332,9 @@ class OEMSecretsProvider implements InfoProviderInterface
}
// Final sorting: by completeness or manufacturer, if necessary
if ($this->settings->sortMode === OEMSecretsSortMode::COMPLETENESS) {
if ($this->sort_criteria === 'C') {
return $this->compareByCompleteness($a, $b);
} elseif ($this->settings->sortMode === OEMSecretsSortMode::MANUFACTURER) {
} elseif ($this->sort_criteria === 'M') {
return strcasecmp($a->manufacturer, $b->manufacturer);
}

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

@@ -31,7 +31,6 @@ use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
use App\Settings\InfoProviderSystem\PollinSettings;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -40,7 +39,8 @@ class PollinProvider implements InfoProviderInterface
{
public function __construct(private readonly HttpClientInterface $client,
private readonly PollinSettings $settings,
#[Autowire(env: 'bool:PROVIDER_POLLIN_ENABLED')]
private readonly bool $enabled = true,
)
{
}
@@ -62,7 +62,7 @@ class PollinProvider implements InfoProviderInterface
public function isActive(): bool
{
return $this->settings->enabled;
return $this->enabled;
}
public function searchByKeyword(string $keyword): array

View File

@@ -29,7 +29,6 @@ use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
use App\Settings\InfoProviderSystem\ReicheltSettings;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -40,7 +39,16 @@ class ReicheltProvider implements InfoProviderInterface
public const DISTRIBUTOR_NAME = "Reichelt";
public function __construct(private readonly HttpClientInterface $client,
private readonly ReicheltSettings $settings,
#[Autowire(env: "bool:PROVIDER_REICHELT_ENABLED")]
private readonly bool $enabled = true,
#[Autowire(env: "PROVIDER_REICHELT_LANGUAGE")]
private readonly string $language = "en",
#[Autowire(env: "PROVIDER_REICHELT_COUNTRY")]
private readonly string $country = "DE",
#[Autowire(env: "PROVIDER_REICHELT_INCLUDE_VAT")]
private readonly bool $includeVAT = false,
#[Autowire(env: "PROVIDER_REICHELT_CURRENCY")]
private readonly string $currency = "EUR",
)
{
}
@@ -62,7 +70,7 @@ class ReicheltProvider implements InfoProviderInterface
public function isActive(): bool
{
return $this->settings->enabled;
return $this->enabled;
}
public function searchByKeyword(string $keyword): array
@@ -113,8 +121,8 @@ class ReicheltProvider implements InfoProviderInterface
sprintf(
'https://www.reichelt.com/?ACTION=514&id=74&article=%s&LANGUAGE=%s&CCOUNTRY=%s',
$id,
strtoupper($this->settings->language),
strtoupper($this->settings->country)
strtoupper($this->language),
strtoupper($this->country)
)
);
$json = $response->toArray();
@@ -125,8 +133,8 @@ class ReicheltProvider implements InfoProviderInterface
$response = $this->client->request('GET', $productPage, [
'query' => [
'CCTYPE' => $this->settings->includeVAT ? 'private' : 'business',
'currency' => $this->settings->currency,
'CCTYPE' => $this->includeVAT ? 'private' : 'business',
'currency' => $this->currency,
],
]);
$html = $response->getContent();
@@ -150,7 +158,7 @@ class ReicheltProvider implements InfoProviderInterface
distributor_name: self::DISTRIBUTOR_NAME,
order_number: $json[0]['article_artnr'],
prices: array_merge(
[new PriceDTO(1.0, $priceString, $currency, $this->settings->includeVAT)]
[new PriceDTO(1.0, $priceString, $currency, $this->includeVAT)]
, $this->parseBatchPrices($dom, $currency)),
product_url: $productPage
);
@@ -210,7 +218,7 @@ class ReicheltProvider implements InfoProviderInterface
//Strip any non-numeric characters
$priceString = preg_replace('/[^0-9.]/', '', $priceString);
$prices[] = new PriceDTO($minAmount, $priceString, $currency, $this->settings->includeVAT);
$prices[] = new PriceDTO($minAmount, $priceString, $currency, $this->includeVAT);
});
return $prices;
@@ -262,7 +270,7 @@ class ReicheltProvider implements InfoProviderInterface
private function getBaseURL(): string
{
//Without the trailing slash
return 'https://www.reichelt.com/' . strtolower($this->settings->country) . '/' . strtolower($this->settings->language);
return 'https://www.reichelt.com/' . strtolower($this->country) . '/' . strtolower($this->language);
}
public function getCapabilities(): array

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace App\Services\InfoProviderSystem\Providers;
use App\Settings\InfoProviderSystem\TMESettings;
use Symfony\Contracts\HttpClient\HttpClientInterface;
use Symfony\Contracts\HttpClient\ResponseInterface;
@@ -31,15 +30,15 @@ class TMEClient
{
public const BASE_URI = 'https://api.tme.eu';
public function __construct(private readonly HttpClientInterface $tmeClient, private readonly TMESettings $settings)
public function __construct(private readonly HttpClientInterface $tmeClient, private readonly string $token, private readonly string $secret)
{
}
public function makeRequest(string $action, array $parameters): ResponseInterface
{
$parameters['Token'] = $this->settings->apiToken;
$parameters['ApiSignature'] = $this->getSignature($action, $parameters, $this->settings->apiSecret);
$parameters['Token'] = $this->token;
$parameters['ApiSignature'] = $this->getSignature($action, $parameters, $this->secret);
return $this->tmeClient->request('POST', $this->getUrlForAction($action), [
'body' => $parameters,
@@ -48,7 +47,7 @@ class TMEClient
public function isUsable(): bool
{
return !($this->settings->apiToken === '' || $this->settings->apiSecret === '');
return $this->token !== '' && $this->secret !== '';
}
/**
@@ -59,7 +58,7 @@ class TMEClient
public function isUsingPrivateToken(): bool
{
//Private tokens are longer than anonymous ones (50 instead of 45 characters)
return strlen($this->settings->apiToken) > 45;
return strlen($this->token) > 45;
}
/**

View File

@@ -30,21 +30,24 @@ use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
use App\Settings\InfoProviderSystem\TMESettings;
class TMEProvider implements InfoProviderInterface
{
private const VENDOR_NAME = 'TME';
/** @var bool If true, the prices are gross prices. If false, the prices are net prices. */
private readonly bool $get_gross_prices;
public function __construct(private readonly TMEClient $tmeClient, private readonly TMESettings $settings)
public function __construct(private readonly TMEClient $tmeClient, private readonly string $country,
private readonly string $language, private readonly string $currency,
bool $get_gross_prices)
{
//If we have a private token, set get_gross_prices to false, as it is automatically determined by the account type then
if ($this->tmeClient->isUsingPrivateToken()) {
$this->get_gross_prices = false;
} else {
$this->get_gross_prices = $this->settings->grossPrices;
$this->get_gross_prices = $get_gross_prices;
}
}
@@ -71,8 +74,8 @@ class TMEProvider implements InfoProviderInterface
public function searchByKeyword(string $keyword): array
{
$response = $this->tmeClient->makeRequest('Products/Search', [
'Country' => $this->settings->country,
'Language' => $this->settings->language,
'Country' => $this->country,
'Language' => $this->language,
'SearchPlain' => $keyword,
]);
@@ -101,8 +104,8 @@ class TMEProvider implements InfoProviderInterface
public function getDetails(string $id): PartDetailDTO
{
$response = $this->tmeClient->makeRequest('Products/GetProducts', [
'Country' => $this->settings->country,
'Language' => $this->settings->language,
'Country' => $this->country,
'Language' => $this->language,
'SymbolList' => [$id],
]);
@@ -146,8 +149,8 @@ class TMEProvider implements InfoProviderInterface
public function getFiles(string $id): array
{
$response = $this->tmeClient->makeRequest('Products/GetProductsFiles', [
'Country' => $this->settings->country,
'Language' => $this->settings->language,
'Country' => $this->country,
'Language' => $this->language,
'SymbolList' => [$id],
]);
@@ -188,9 +191,9 @@ class TMEProvider implements InfoProviderInterface
public function getVendorInfo(string $id, ?string $productURL = null): PurchaseInfoDTO
{
$response = $this->tmeClient->makeRequest('Products/GetPricesAndStocks', [
'Country' => $this->settings->country,
'Language' => $this->settings->language,
'Currency' => $this->settings->currency,
'Country' => $this->country,
'Language' => $this->language,
'Currency' => $this->currency,
'GrossPrices' => $this->get_gross_prices,
'SymbolList' => [$id],
]);
@@ -231,8 +234,8 @@ class TMEProvider implements InfoProviderInterface
public function getParameters(string $id, string|null &$footprint_name = null): array
{
$response = $this->tmeClient->makeRequest('Products/GetParameters', [
'Country' => $this->settings->country,
'Language' => $this->settings->language,
'Country' => $this->country,
'Language' => $this->language,
'SymbolList' => [$id],
]);

View File

@@ -42,7 +42,6 @@ declare(strict_types=1);
namespace App\Services\LabelSystem;
use App\Entity\LabelSystem\LabelProcessMode;
use App\Settings\SystemSettings\CustomizationSettings;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\Contracts\NamedElementInterface;
use App\Entity\LabelSystem\LabelOptions;
@@ -61,7 +60,7 @@ final class LabelHTMLGenerator
private readonly LabelBarcodeGenerator $barcodeGenerator,
private readonly SandboxedTwigFactory $sandboxedTwigProvider,
private readonly Security $security,
private readonly CustomizationSettings $customizationSettings,)
private readonly string $partdb_title)
{
}
@@ -89,8 +88,7 @@ final class LabelHTMLGenerator
'page' => $page,
'last_page' => count($elements),
'user' => $current_user,
'install_title' => $this->customizationSettings->instanceName,
'partdb_title' => $this->customizationSettings->instanceName,
'install_title' => $this->partdb_title,
'paper_width' => $options->getWidth(),
'paper_height' => $options->getHeight(),
]

View File

@@ -41,7 +41,6 @@ declare(strict_types=1);
namespace App\Services\LabelSystem\PlaceholderProviders;
use App\Settings\SystemSettings\CustomizationSettings;
use Symfony\Bundle\SecurityBundle\Security;
use App\Entity\UserSystem\User;
use DateTime;
@@ -55,18 +54,14 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
*/
final class GlobalProviders implements PlaceholderProviderInterface
{
public function __construct(
private readonly Security $security,
private readonly UrlGeneratorInterface $url_generator,
private CustomizationSettings $customizationSettings,
)
public function __construct(private readonly string $partdb_title, private readonly Security $security, private readonly UrlGeneratorInterface $url_generator)
{
}
public function replace(string $placeholder, object $label_target, array $options = []): ?string
{
if ('[[INSTALL_NAME]]' === $placeholder) {
return $this->customizationSettings->instanceName;
return $this->partdb_title;
}
$user = $this->security->getUser();

View File

@@ -22,25 +22,37 @@ declare(strict_types=1);
*/
namespace App\Services\LogSystem;
use App\Settings\SystemSettings\HistorySettings;
/**
* This service is used to check if a log change comment is needed for a given operation type.
* It is configured using the "enforce_change_comments_for" config parameter.
* @see \App\Tests\Services\LogSystem\EventCommentNeededHelperTest
*/
final class EventCommentNeededHelper
class EventCommentNeededHelper
{
public function __construct(private readonly HistorySettings $settings)
{
final public const VALID_OPERATION_TYPES = [
'part_edit',
'part_create',
'part_delete',
'part_stock_operation',
'datastructure_edit',
'datastructure_create',
'datastructure_delete',
];
public function __construct(protected array $enforce_change_comments_for)
{
}
/**
* Checks if a log change comment is needed for the given operation type
*/
public function isCommentNeeded(EventCommentType $comment_type): bool
public function isCommentNeeded(string $comment_type): bool
{
return in_array($comment_type, $this->settings->enforceComments, true);
//Check if the comment type is valid
if (! in_array($comment_type, self::VALID_OPERATION_TYPES, true)) {
throw new \InvalidArgumentException('The comment type "'.$comment_type.'" is not valid!');
}
return in_array($comment_type, $this->enforce_change_comments_for, true);
}
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\Services\LogSystem;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* This enum represents the different types of event comments that could be required, by the system.
* They are almost only useful when working with the EventCommentNeededHelper service.
*/
enum EventCommentType: string implements TranslatableInterface
{
case PART_EDIT = 'part_edit';
case PART_CREATE = 'part_create';
case PART_DELETE = 'part_delete';
case PART_STOCK_OPERATION = 'part_stock_operation';
case DATASTRUCTURE_EDIT = 'datastructure_edit';
case DATASTRUCTURE_CREATE = 'datastructure_create';
case DATASTRUCTURE_DELETE = 'datastructure_delete';
public function trans(TranslatorInterface $translator, ?string $locale = null): string
{
return $translator->trans('settings.system.history.enforceComments.type.' . $this->value, locale: $locale);
}
}

View File

@@ -25,7 +25,6 @@ namespace App\Services\Parts;
use App\Entity\Parts\Part;
use App\Entity\PriceInformations\Currency;
use App\Entity\PriceInformations\Pricedetail;
use App\Settings\SystemSettings\LocalizationSettings;
use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;
use Doctrine\ORM\PersistentCollection;
@@ -40,7 +39,7 @@ class PricedetailHelper
{
protected string $locale;
public function __construct()
public function __construct(protected string $base_currency)
{
$this->locale = Locale::getDefault();
}

View File

@@ -23,14 +23,12 @@ declare(strict_types=1);
namespace App\Services\System;
use App\Settings\SystemSettings\CustomizationSettings;
/**
* Helper service to retrieve the banner of this Part-DB installation
*/
class BannerHelper
{
public function __construct(private CustomizationSettings $customizationSettings)
public function __construct(private readonly string $project_dir, private readonly string $partdb_banner)
{
}
@@ -41,6 +39,18 @@ class BannerHelper
*/
public function getBanner(): string
{
return $this->customizationSettings->banner ?? "";
$banner = $this->partdb_banner;
if ($banner === '') {
$banner_path = $this->project_dir
.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'banner.md';
$tmp = file_get_contents($banner_path);
if (false === $tmp) {
throw new \RuntimeException('The banner file could not be read.');
}
$banner = $tmp;
}
return $banner;
}
}

View File

@@ -23,7 +23,6 @@ declare(strict_types=1);
namespace App\Services\System;
use App\Settings\SystemSettings\PrivacySettings;
use Psr\Log\LoggerInterface;
use Shivas\VersioningBundle\Service\VersionManagerInterface;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
@@ -44,7 +43,7 @@ class UpdateAvailableManager
public function __construct(private readonly HttpClientInterface $httpClient,
private readonly CacheInterface $updateCache, private readonly VersionManagerInterface $versionManager,
private readonly PrivacySettings $privacySettings, private readonly LoggerInterface $logger,
private readonly bool $check_for_updates, private readonly LoggerInterface $logger,
#[Autowire(param: 'kernel.debug')] private readonly bool $is_dev_mode)
{
@@ -84,7 +83,7 @@ class UpdateAvailableManager
public function isUpdateAvailable(): bool
{
//If we don't want to check for updates, we can return false
if (!$this->privacySettings->checkForUpdates) {
if (!$this->check_for_updates) {
return false;
}
@@ -102,7 +101,7 @@ class UpdateAvailableManager
private function getLatestVersionInfo(): array
{
//If we don't want to check for updates, we can return dummy data
if (!$this->privacySettings->checkForUpdates) {
if (!$this->check_for_updates) {
return [
'version' => '0.0.1',
'url' => 'update-checking-disabled'

View File

@@ -23,14 +23,13 @@ declare(strict_types=1);
namespace App\Services\Tools;
use App\Entity\PriceInformations\Currency;
use App\Settings\SystemSettings\LocalizationSettings;
use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;
use Swap\Swap;
class ExchangeRateUpdater
{
public function __construct(private LocalizationSettings $localizationSettings, private readonly Swap $swap)
public function __construct(private readonly string $base_currency, private readonly Swap $swap)
{
}
@@ -40,7 +39,7 @@ class ExchangeRateUpdater
public function update(Currency $currency): Currency
{
//Currency pairs are always in the format "BASE/QUOTE"
$rate = $this->swap->latest($this->localizationSettings->baseCurrency.'/'.$currency->getIsoCode());
$rate = $this->swap->latest($this->base_currency.'/'.$currency->getIsoCode());
//The rate says how many quote units are worth one base unit
//So we need to invert it to get the exchange rate

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

@@ -38,7 +38,6 @@ use App\Repository\StructuralDBElementRepository;
use App\Services\Cache\ElementCacheTagGenerator;
use App\Services\Cache\UserCacheKeyGenerator;
use App\Services\EntityURLGenerator;
use App\Settings\BehaviorSettings\SidebarSettings;
use Doctrine\ORM\EntityManagerInterface;
use InvalidArgumentException;
use RecursiveIteratorIterator;
@@ -54,10 +53,6 @@ use function count;
*/
class TreeViewGenerator
{
private readonly bool $rootNodeExpandedByDefault;
private readonly bool $rootNodeEnabled;
public function __construct(
protected EntityURLGenerator $urlGenerator,
protected EntityManagerInterface $em,
@@ -66,10 +61,11 @@ class TreeViewGenerator
protected UserCacheKeyGenerator $keyGenerator,
protected TranslatorInterface $translator,
private readonly UrlGeneratorInterface $router,
private readonly SidebarSettings $sidebarSettings,
protected bool $rootNodeExpandedByDefault,
protected bool $rootNodeEnabled,
//TODO: Make this configurable in the future
protected bool $rootNodeRedirectsToNewEntity = false,
) {
$this->rootNodeEnabled = $this->sidebarSettings->rootNodeEnabled;
$this->rootNodeExpandedByDefault = $this->sidebarSettings->rootNodeExpanded;
}
/**
@@ -192,7 +188,7 @@ class TreeViewGenerator
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) {
if ($this->rootNodeRedirectsToNewEntity) {
return match ($class) {
Category::class => $this->router->generate('category_new'),
StorageLocation::class => $this->router->generate('store_location_new'),

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

@@ -30,7 +30,6 @@ use App\Entity\Attachments\UserAttachment;
use App\Entity\UserSystem\User;
use App\Services\Attachments\AttachmentSubmitHandler;
use App\Services\Attachments\AttachmentURLGenerator;
use App\Settings\SystemSettings\PrivacySettings;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Asset\Packages;
use Symfony\Component\HttpFoundation\File\UploadedFile;
@@ -43,7 +42,7 @@ class UserAvatarHelper
public const IMG_DEFAULT_AVATAR_PATH = 'img/default_avatar.svg';
public function __construct(
private readonly PrivacySettings $privacySettings,
private readonly bool $use_gravatar,
private readonly Packages $packages,
private readonly AttachmentURLGenerator $attachmentURLGenerator,
private readonly EntityManagerInterface $entityManager,
@@ -66,7 +65,7 @@ class UserAvatarHelper
}
//If not check if gravatar is enabled (then use gravatar URL)
if ($this->privacySettings->useGravatar) {
if ($this->use_gravatar) {
return $this->getGravatar($user, 200); //200px wide picture
}
@@ -83,7 +82,7 @@ class UserAvatarHelper
}
//If not check if gravatar is enabled (then use gravatar URL)
if ($this->privacySettings->useGravatar) {
if ($this->use_gravatar) {
return $this->getGravatar($user, 50); //50px wide picture
}
@@ -100,7 +99,7 @@ class UserAvatarHelper
}
//If not check if gravatar is enabled (then use gravatar URL)
if ($this->privacySettings->useGravatar) {
if ($this->use_gravatar) {
return $this->getGravatar($user, 150);
}

View File

@@ -1,52 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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;
use App\Settings\BehaviorSettings\BehaviorSettings;
use App\Settings\InfoProviderSystem\InfoProviderSettings;
use App\Settings\MiscSettings\MiscSettings;
use App\Settings\SystemSettings\AttachmentsSettings;
use Jbtronics\SettingsBundle\Settings\EmbeddedSettings;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
#[Settings]
#[SettingsIcon('folder-tree')]
class AppSettings
{
use SettingsTrait;
#[EmbeddedSettings()]
public ?SystemSettings $system = null;
#[EmbeddedSettings()]
public ?BehaviorSettings $behavior = null;
#[EmbeddedSettings()]
public ?InfoProviderSettings $infoProviders = null;
#[EmbeddedSettings()]
public ?MiscSettings $miscSettings = 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 - 2024 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 Jbtronics\SettingsBundle\Settings\EmbeddedSettings;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
#[Settings]
class BehaviorSettings
{
use SettingsTrait;
#[EmbeddedSettings]
public ?SidebarSettings $sidebar = null;
#[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

@@ -1,66 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
enum PartTableColumns : string implements TranslatableInterface
{
case NAME = "name";
case ID = "id";
case IPN = "ipn";
case DESCRIPTION = "description";
case CATEGORY = "category";
case FOOTPRINT = "footprint";
case MANUFACTURER = "manufacturer";
case LOCATION = "storage_location";
case AMOUNT = "amount";
case MIN_AMOUNT = "minamount";
case PART_UNIT = "partUnit";
case ADDED_DATE = "addedDate";
case LAST_MODIFIED = "lastModified";
case NEEDS_REVIEW = "needs_review";
case FAVORITE = "favorite";
case MANUFACTURING_STATUS = "manufacturing_status";
case MPN = "manufacturer_product_number";
case MASS = "mass";
case TAGS = "tags";
case ATTACHMENTS = "attachments";
case EDIT = "edit";
public function trans(TranslatorInterface $translator, ?string $locale = null): string
{
$key = match($this) {
self::LOCATION => 'part.table.storeLocations',
self::NEEDS_REVIEW => 'part.table.needsReview',
self::MANUFACTURING_STATUS => 'part.table.manufacturingStatus',
self::MPN => 'part.table.mpn',
default => 'part.table.' . $this->value,
};
return $translator->trans($key, locale: $locale);
}
}

View File

@@ -1,53 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
enum SidebarItems: string implements TranslatableInterface
{
case TOOLS = "tools";
case CATEGORIES = "categories";
case LOCATIONS = "locations";
case FOOTPRINTS = "footprints";
case MANUFACTURERS = "manufacturers";
case SUPPLIERS = "suppliers";
case PROJECTS = "projects";
public function trans(TranslatorInterface $translator, ?string $locale = null): string
{
$key = match($this) {
self::TOOLS => 'tools.label',
self::CATEGORIES => 'category.labelp',
self::LOCATIONS => 'storelocation.labelp',
self::FOOTPRINTS => 'footprint.labelp',
self::MANUFACTURERS => 'manufacturer.labelp',
self::SUPPLIERS => 'supplier.labelp',
self::PROJECTS => 'project.labelp',
};
return $translator->trans($key, locale: $locale);
}
}

View File

@@ -1,76 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\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(label: new TM("settings.behavior.sidebar"))]
#[SettingsIcon('fa-border-top-left')]
class SidebarSettings
{
use SettingsTrait;
/**
* @var SidebarItems[] The items to show in the sidebar.
*/
#[SettingsParameter(ArrayType::class,
label: new TM("settings.behavior.sidebar.items"),
description: new TM("settings.behavior.sidebar.items.help"),
options: ['type' => EnumType::class, 'options' => ['class' => SidebarItems::class]],
formType: \Symfony\Component\Form\Extension\Core\Type\EnumType::class,
formOptions: ['class' => SidebarItems::class, 'multiple' => true, 'ordered' => true]
)]
#[Assert\NotBlank()]
#[Assert\Unique()]
public array $items = [SidebarItems::CATEGORIES, SidebarItems::PROJECTS, SidebarItems::TOOLS];
/**
* @var bool Whether categories, etc. should be grouped under a root node or put directly into the sidebar trees.
*/
#[SettingsParameter(
label: new TM("settings.behavior.sidebar.rootNodeEnabled"),
description: new TM("settings.behavior.sidebar.rootNodeEnabled.help")
)]
public bool $rootNodeEnabled = true;
/**
* @var bool Whether the root node should be expanded by default, or not.
*/
#[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,90 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\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(label: new TM("settings.behavior.table"))]
#[SettingsIcon('fa-table')]
class TableSettings
{
use SettingsTrait;
#[SettingsParameter(
label: new TM("settings.behavior.table.default_page_size"),
description: new TM("settings.behavior.table.default_page_size.help"),
envVar: "int:TABLE_DEFAULT_PAGE_SIZE",
envVarMode: EnvVarMode::OVERWRITE,
)]
#[Assert\AtLeastOneOf(constraints:
[
new Assert\Positive(),
new Assert\EqualTo(value: -1)
]
)]
public int $fullDefaultPageSize = 50;
/** @var PartTableColumns[] */
#[SettingsParameter(ArrayType::class,
label: new TM("settings.behavior.table.parts_default_columns"),
description: new TM("settings.behavior.table.parts_default_columns.help"),
options: ['type' => EnumType::class, 'options' => ['class' => PartTableColumns::class]],
formType: \Symfony\Component\Form\Extension\Core\Type\EnumType::class,
formOptions: ['class' => PartTableColumns::class, 'multiple' => true, 'ordered' => true],
envVar: "TABLE_PARTS_DEFAULT_COLUMNS", envVarMode: EnvVarMode::OVERWRITE, envVarMapper: [self::class, 'mapPartsDefaultColumnsEnv']
)]
#[Assert\NotBlank()]
#[Assert\Unique()]
#[Assert\All([new Assert\Type(PartTableColumns::class)])]
public array $partsDefaultColumns = [PartTableColumns::NAME, PartTableColumns::DESCRIPTION,
PartTableColumns::CATEGORY, PartTableColumns::FOOTPRINT, PartTableColumns::MANUFACTURER,
PartTableColumns::LOCATION, PartTableColumns::AMOUNT];
public static function mapPartsDefaultColumnsEnv(string $columns): array
{
$exploded = explode(',', $columns);
$ret = [];
foreach ($exploded as $column) {
$enum = PartTableColumns::tryFrom($column);
if (!$enum) {
throw new \InvalidArgumentException("Invalid column '$column' in TABLE_PARTS_DEFAULT_COLUMNS");
}
$ret[] = $enum;
}
return $ret;
}
}

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

@@ -1,46 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\Translation\TranslatableMessage as TM;
#[Settings(label: new TM("settings.ips.element14"))]
#[SettingsIcon("fa-plug")]
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)]
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)]
public string $storeId = "de.farnell.com";
}

View File

@@ -1,61 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Jbtronics\SettingsBundle\Settings\EmbeddedSettings;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
#[Settings()]
class InfoProviderSettings
{
use SettingsTrait;
#[EmbeddedSettings]
public ?DigikeySettings $digikey = null;
#[EmbeddedSettings]
public ?MouserSettings $mouser = null;
#[EmbeddedSettings]
public ?TMESettings $tme = null;
#[EmbeddedSettings]
public ?Element14Settings $element14 = null;
#[EmbeddedSettings]
public ?OctopartSettings $octopartSettings = null;
#[EmbeddedSettings]
public ?LCSCSettings $lcsc = null;
#[EmbeddedSettings]
public ?OEMSecretsSettings $oemsecrets = null;
#[EmbeddedSettings]
public ?ReicheltSettings $reichelt = null;
#[EmbeddedSettings]
public ?PollinSettings $pollin = null;
}

View File

@@ -1,49 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\CurrencyType;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(label: new TM("settings.ips.lcsc"), description: new TM("settings.ips.lcsc.help"))]
#[SettingsIcon("fa-plug")]
class LCSCSettings
{
use SettingsTrait;
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"),
envVar: "bool:PROVIDER_LCSC_ENABLED", envVarMode: EnvVarMode::OVERWRITE)]
public bool $enabled = false;
#[SettingsParameter(label: new TM("settings.ips.lcsc.currency"), formType: CurrencyType::class,
envVar: "string:PROVIDER_LCSC_CURRENCY", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Currency()]
public string $currency = 'EUR';
}

View File

@@ -1,47 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
enum MouserSearchOptions: string implements TranslatableInterface
{
case NONE = "None";
case ROHS = "Rohs";
case IN_STOCK = "InStock";
case ROHS_AND_INSTOCK = "RohsAndInStock";
public function trans(TranslatorInterface $translator, ?string $locale = null): string
{
$key = match($this) {
self::NONE => "settings.ips.mouser.searchOptions.none",
self::ROHS => "settings.ips.mouser.searchOptions.rohs",
self::IN_STOCK => "settings.ips.mouser.searchOptions.inStock",
self::ROHS_AND_INSTOCK => "settings.ips.mouser.searchOptions.rohsAndInStock",
};
return $translator->trans($key, locale: $locale);
}
}

View File

@@ -1,67 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(label: new TM("settings.ips.mouser"))]
#[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)]
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)]
#[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"])]
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")]
public bool $searchWithSignUpLanguage = true;
public static function mapSearchOptionEnvVar(?string $value): MouserSearchOptions
{
if (!$value) {
return MouserSearchOptions::NONE;
}
return MouserSearchOptions::tryFrom($value) ?? MouserSearchOptions::NONE;
}
}

View File

@@ -1,88 +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\Validator\Constraints as Assert;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(label: new TM("settings.ips.oemsecrets"))]
#[SettingsIcon("fa-plug")]
class OEMSecretsSettings
{
use SettingsTrait;
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)]
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)]
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)]
#[Assert\Choice(choices: self::SUPPORTED_CURRENCIES)]
public string $currency = "EUR";
/**
* @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)]
public bool $keepZeroPrices = false;
/**
* @var bool If set to 1 the parameters for the part are generated
* # 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)]
public bool $parseParams = true;
#[SettingsParameter(label: new TM("settings.ips.oemsecrets.sortMode"), envVar: "PROVIDER_OEMSECRETS_SORT_CRITERIA", envVarMapper: [self::class, "mapSortModeEnvVar"])]
public OEMSecretsSortMode $sortMode = OEMSecretsSortMode::COMPLETENESS;
public static function mapSortModeEnvVar(?string $value): OEMSecretsSortMode
{
if (!$value) {
return OEMSecretsSortMode::NONE;
}
return OEMSecretsSortMode::tryFrom($value) ?? OEMSecretsSortMode::NONE;
}
}

View File

@@ -1,46 +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 Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* This environment variable determines the sorting criteria for product results.
* The sorting process first arranges items based on the provided keyword.
* Then, if set to 'C', it further sorts by completeness (prioritizing items with the most
* detailed information). If set to 'M', it further sorts by manufacturer name.
* If unset or set to any other value, no sorting is performed.
*/
enum OEMSecretsSortMode : string implements TranslatableInterface
{
case NONE = "N";
case COMPLETENESS = "C";
case MANUFACTURER = "M";
public function trans(TranslatorInterface $translator, ?string $locale = null): string
{
return $translator->trans('settings.ips.oemsecrets.sortMode.' . $this->value, locale: $locale);
}
}

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

@@ -1,39 +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 Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(label: new TM("settings.ips.pollin"), description: new TM("settings.ips.pollin.help"))]
#[SettingsIcon("fa-plug")]
class PollinSettings
{
#[SettingsParameter(label: new TM("settings.ips.lcsc.enabled"),
envVar: "bool:PROVIDER_POLLIN_ENABLED", envVarMode: EnvVarMode::OVERWRITE)]
public bool $enabled = false;
}

View File

@@ -1,68 +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\LanguageType;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(label: new TM("settings.ips.reichelt"), description: new TM("settings.ips.reichelt.help"))]
#[SettingsIcon("fa-plug")]
class ReicheltSettings
{
use SettingsTrait;
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)]
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)]
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)]
#[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)]
#[Assert\Country]
public string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.reichelt.include_vat"),
envVar: "bool:PROVIDER_REICHELT_INCLUDE_VAT", envVarMode: EnvVarMode::OVERWRITE)]
public bool $includeVAT = true;
}

View File

@@ -1,72 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\LanguageType;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(label: new TM("settings.ips.tme"))]
#[SettingsIcon("fa-plug")]
class TMESettings
{
use SettingsTrait;
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)]
public ?string $apiToken = null;
#[SettingsParameter(label: new TM("settings.ips.tme.secret"),
envVar: "PROVIDER_TME_SECRET", envVarMode: EnvVarMode::OVERWRITE)]
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)]
#[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)]
#[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)]
#[Assert\Country]
public string $country = "DE";
#[SettingsParameter(label: new TM("settings.ips.tme.grossPrices"),
envVar: "bool:PROVIDER_TME_GET_GROSS_PRICES", envVarMode: EnvVarMode::OVERWRITE)]
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

@@ -1,46 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(label: new TM("settings.misc.kicad_eda"))]
#[SettingsIcon("fa-bolt-lightning")]
class KiCadEDASettings
{
use SettingsTrait;
#[SettingsParameter(label: new TM("settings.misc.kicad_eda.category_depth"),
description: new TM("settings.misc.kicad_eda.category_depth.help"),
envVar: "int:EDA_KICAD_CATEGORY_DEPTH", envVarMode: EnvVarMode::OVERWRITE)]
#[Assert\Range(min: -1)]
public int $categoryDepth = 0;
}

View File

@@ -1,37 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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 Jbtronics\SettingsBundle\Settings\EmbeddedSettings;
use Jbtronics\SettingsBundle\Settings\Settings;
#[Settings]
class MiscSettings
{
#[EmbeddedSettings]
public ?KiCadEDASettings $kicadEDA = null;
#[EmbeddedSettings]
public ?ExchangeRateSettings $exchangeRate = null;
}

View File

@@ -1,32 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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;
#[\Attribute(\Attribute::TARGET_CLASS)]
class SettingsIcon
{
public function __construct(public string $icon)
{
}
}

View File

@@ -1,51 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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;
use App\Settings\SystemSettings\AttachmentsSettings;
use App\Settings\SystemSettings\CustomizationSettings;
use App\Settings\SystemSettings\HistorySettings;
use App\Settings\SystemSettings\LocalizationSettings;
use App\Settings\SystemSettings\PrivacySettings;
use Jbtronics\SettingsBundle\Settings\EmbeddedSettings;
use Jbtronics\SettingsBundle\Settings\Settings;
#[Settings]
class SystemSettings
{
#[EmbeddedSettings()]
public ?LocalizationSettings $localization = null;
#[EmbeddedSettings()]
public ?CustomizationSettings $customization = null;
#[EmbeddedSettings()]
public ?PrivacySettings $privacy = null;
#[EmbeddedSettings()]
public ?AttachmentsSettings $attachments = null;
#[EmbeddedSettings()]
public ?HistorySettings $history = null;
}

View File

@@ -1,61 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\SystemSettings;
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\Translation\TranslatableMessage as TM;
use Symfony\Component\Validator\Constraints as Assert;
#[Settings(label: new TM("settings.system.attachments"))]
#[SettingsIcon("fa-paperclip")]
class AttachmentsSettings
{
use SettingsTrait;
#[SettingsParameter(
label: new TM("settings.system.attachments.maxFileSize"),
description: new TM("settings.system.attachments.maxFileSize.help"),
envVar: "MAX_ATTACHMENT_FILE_SIZE", envVarMode: EnvVarMode::OVERWRITE
)]
#[Assert\Regex("/^([1-9][0-9]*)([KMG])?$/", message: "validator.fileSize.invalidFormat")]
public string $maxFileSize = '100M';
#[SettingsParameter(
label: new TM("settings.system.attachments.allowDownloads"),
description: new TM("settings.system.attachments.allowDownloads.help"),
formOptions: ['help_html' => true],
envVar: "bool:ALLOW_ATTACHMENT_DOWNLOADS", envVarMode: EnvVarMode::OVERWRITE
)]
public bool $allowDownloads = false;
#[SettingsParameter(
label: new TM("settings.system.attachments.downloadByDefault"),
envVar: "bool:ATTACHMENT_DOWNLOAD_BY_DEFAULT", envVarMode: EnvVarMode::OVERWRITE
)]
public bool $downloadByDefault = false;
}

View File

@@ -1,61 +0,0 @@
<?php
/*
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
*
* Copyright (C) 2019 - 2024 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\SystemSettings;
use App\Form\Type\RichTextEditorType;
use App\Form\Type\ThemeChoiceType;
use App\Settings\SettingsIcon;
use App\Validator\Constraints\ValidTheme;
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
use Jbtronics\SettingsBundle\Settings\Settings;
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
use Symfony\Component\Translation\TranslatableMessage as TM;
#[Settings(name: "customization", label: new TM("settings.system.customization"))]
#[SettingsIcon("fa-paint-roller")]
class CustomizationSettings
{
use SettingsTrait;
#[SettingsParameter(
label: new TM("settings.system.customization.instanceName"),
description: new TM("settings.system.customization.instanceName.help"),
envVar: "INSTANCE_NAME", envVarMode: EnvVarMode::OVERWRITE,
)]
public string $instanceName = "Part-DB";
#[SettingsParameter(
label: new TM("settings.system.customization.banner"),
formType: RichTextEditorType::class, formOptions: ['mode' => 'markdown-full'],
)]
public ?string $banner = null;
#[SettingsParameter(
label: new TM("settings.system.customization.theme"),
formType: ThemeChoiceType::class, formOptions: ['placeholder' => false]
)]
#[ValidTheme]
public string $theme = 'bootstrap';
}

Some files were not shown because too many files have changed in this diff Show More